[翻译]Elasticsearch重要文章之五:预加载fielddata

Elasticsearch 是默认延迟加载fielddata到内存里的。当elasticsearch第一次遇到一个查询需要一个指定field的fielddata的时候,就会把索引的每个段中整个field加载到内存。对于小段,这是个可以忽略不计的时间,但是如果你有一些5G大小的段并且需要加载10GB的fielddata到内存里,这个过程需要数十秒,习惯于秒内响应时间的用户会被网突如其来的迟钝所打击。 有三种方法应对这种延迟尖峰: Eagerly load fielddata(饿汉式(预)加载fielddata) Eagerly load global ordinals(饿汉式(预)加载全局序数) Prepopulate caches with warmers (使用warmer提前加载缓存)。 所有这些都是一个意思:预先加载fielddata到内存里,这样当用户需要执行一个搜索的时候就感受不到延迟了。 饿汉式(预)加载fielddata 首先是预先加载(而不是默认的延迟加载),当一个新的段形成时(无乱是刷新,写入或者是合并),可以预先加载的field会提前把段的fielddata加载到内存里,在这段可以用于搜索之前。 这意味着当你第一次查询的时候,如果碰到在这个段上,你不需要再触发加载fielddata的操作,它们已经在内存中了,这会防止你的用户遇到一些冷到缓存而发生延迟尖峰。 预先加载是基于每个field的,所以你可以控制哪些field进行预加载。 PUT /music/_mapping/_song { "price_usd": { "type": "integer", "fielddata": { "loading" : "eager" } } } 备注: 通过设置fielddata.loading: eager,告诉elasticsearch预先加载这个field的内容到内存里。 fielddata的加载可以设置成饿汉模式(预先加载)还是懒惰模式(延迟加载),使用update-mapping的api。 预先加载是简单对fielddata加载的开销的转移,从查询时间转义到刷新时刻。 大段的刷新时间会比小段的时间长,通常大段的产生都是由哪些已经可搜素的小段合并而来的,所以慢一点的刷新时间不是那么重要(译者注:意思是大段的刷新时间长不影响你的搜索,在大段合并成前的小段可以用于搜索)。 全局序数 其中一项用于减少string类型的fielddata占用内存的技术叫做序数。 假设我们有十亿条文档,每个文档都有一个status的field,只有三个值:status_pending, status_published, status_deleted,如果我们把所有的status加载到内存里,每个文档需要14-16byte,也就是说15GB 相反,我们可以确认这三个特殊的字符串,对他们排序,依次编号0,1,2 Ordinal | Term ------------------- 0 | status_deleted 1 | status_pending 2 | status_published 序号对应的字符串值要在序号列表中存储一次,每个文档只要使用他们的编号来表示他们所包含的值就可以了。 Doc | Ordinal ------------------------- 0 | 1 # pending 1 | 1 # pending 2 | 2 # published 3 | 0 # deleted 这个可以把15GB的内存占用减少到小于1GB ...

2015年6月13日 · 2 分钟

[翻译]Elasticsearch重要文章之四:监控每个节点(ThreadPool部分)

ThreadPool部分 Elasticsearch 内部使用了线程池,通过这些线程池之间的合作完成工作,在需要时传递工作。一般来说你不需要调整和优化线程池。但是有时候你看着这些线程池的状态,对你掌握你的集群行为是很有帮助的。 这有十几个线程池,他们的格式都是类似的: "index": { "threads": 1, "queue": 0, "active": 0, "rejected": 0, "largest": 1, "completed": 1 } 每个线程都列出了配置的线程数,其中有多少个线程是正在处理事务的,也就是活动的,还有多少等待处理的事务在队列里。 如果队列满了,超出了限制,新的事务就会开始被拒绝,你可以看到拒绝的事务的统计,这通常表示你的集群正处在一个资源瓶颈,因为一个满的队列表示你的集群或者节点正在以最大的速度处理事务,但是依然赶不上新事务增加的速度。 关于bulk的拒绝 如果你的线程队列出现拒绝请求的事情,那么醉有可能发生的就是bulk批量索引的请求,通过采用并发导入线程,很容易发给elasticsearch很多的bulk请求,并发请求越多越好吗? 现实中,任何集群都有一定的线程,造成入不敷出。一旦这个阈值达到了,你的队列就会被迅速的填满,新的bulk请求就会被拒绝。 这是一个好事,队列的拒绝是对压力的一个有效措施,他们告诉你你的集群正在处于最大的容量,这要好过把数据全部塞到内存队列里。增大队列大小不会提升性能,它只会隐藏问题,如果你的集群每秒只能处理1万个文档,这和你的队列大小是100还是一千万没有任何关系,你的集群每秒的处理能力仍然是1万个文档。 队列只会隐藏性能问题,并且带来数据丢失的风险,在队列里的表示还没有被处理的,如果你的节点挂了,那么这些请求就会永远的丢失了,此外队列会消耗很大的内存,这不是个好主意。 最好我们通过优雅的解决队列满了的问题来清理队列。当你遇到bulk拒绝请求时候,你应该采取如下措施: 1、停止插入线程3-5秒 2、从bluk请求里提取被拒绝的操作,可能大部分请求都成功了。bulk的响应里会告诉你哪些操作成功了,哪些操作被拒绝了。 3、把拒绝的操作重新生成一个新的bulk请求。 4、如果再有拒绝请求发生,就重复上面的步骤。 通过这种方式,你的代码会自然的适应你的集群的负载,自然的减压。 请求拒绝不是错误,它们只是表示你需要过会重试。 有十几个线程池,大部分你可以忽视,但是有少部分需要你特别注意: indexing 正常的索引文档的请求 bulk 批量请求,这有区别于非批量的请求 get 根据id获取文档的操作 search 索引的检索和查询请求 merging 专门管理lucene合并的线程池 FS和Network部分(剩余空间和网络) 继续看node stats api返回的信息,你会看到一个关于文件系统的统计信息,剩余空间,数据存放目录,磁盘io等待。如果你没有监控剩余磁盘空间大小,你可以从这里得到。磁盘io也是很容易得到,但是一些更专业的命令行工具(例如iostat)可能更有用。 很显然,如果你的磁盘空间不足了,elasticsearch肯定完蛋了,所以一定要保证充足的磁盘空间。 下面是关于network统计的两个部分: "transport": { "server_open": 13, "rx_count": 11696, "rx_size_in_bytes": 1525774, "tx_count": 10282, "tx_size_in_bytes": 1440101928 }, "http": { "current_open": 4, "total_opened": 23 }, transport: 显示了网络传输的基本信息,这涉及到节点之间的通信(通常是9300端口)和一些客户端和节点之间的链接。如果你看到很多链接在这里,不要担心,elasticsearch会保持大量的节点之间的链接。 ...

2015年6月3日 · 1 分钟

[翻译]Elasticsearch重要文章之四:监控每个节点(jvm部分)

操作系统和进程部分 操作系统和进程部分的含义是很清楚的,这里不会描述的很详细。他们列出了基本的资源统计,例如CPU和负载。操作系统部分描述了整个操作系统的情况,进程部分只是描述了Elasticsearch的JVM进程的使用情况。 这显然是很有用的统计, 但是往往会被忽视,一些统计包括如下部分: CPU 负载 内存使用情况 swap使用情况 打开文件句柄数 JVM部分 jvm部分包含一些有关于运行elasticsearch的jvm进程的关键信息。最重要的是,它包含了垃圾回收方面的细节,这对你的elasticsearch的集群的稳定性有很大影响。 垃圾收集(GC)入门 在我们描述这个之前,很有必要先介绍下GC以及它对elasticsearch的影响。如果你对jvm中的GC很熟悉,可以跳过这一章。 java是一个自己进行垃圾回收的语言,也就是说程序员不需要主动管理内存的分配和释放。程序员只要专心写自己的代码,java虚拟机会管理根据需要分配内存的过程,然后当内存不再使用的时候,它自己会去释放。 当内存被分配给JVM进程,它会被分配成一个叫堆的大块区域。JVM会把这个堆分成两组,叫做“代”: 年轻代(或者伊甸园) 新实例化的对象就在这里分配空间,年轻代的空间通常很小,大约100MB-500MB。年轻代包含两个幸存者区域 老年代 存储那些老的对象的区域。这些对象是长期存在,并且持续很长时间。老年代通常比年轻代大很多。你可以看到elasticsearch节点的老年代可能大到30GB 当一个对象被实例化后,它会被放置到年轻代,当年轻代的空间满了,一个年轻代的垃圾回收就启动了。那些仍然存活的对象就会被移动到其中一个幸存者区域。而死了的对象就会被清除了。如果一个对象在年轻代中经历了多次GC仍然幸存,那它将被晋升到老年代。 类似的过程也发生在老年代,当老年代的空间越来越满了,一个垃圾回收就启动了,同时死了对象会被清除。 天下没有免费的午餐,年轻代和老年代的垃圾回收都包含一个“stop-the-world”的阶段。在这个时间内,JVM会停止程序的执行,进行对象的标记和收集,在这个stop-the-world的阶段,没有任何事情发生,请求不会被处理,ping不会被会回应。shards不会再进行迁移。整个世界真的停止了。 对于年轻代这不是一个问题,因为它很小,GC执行的很快。但是对于大一点的老年代,缓慢的GC意味着1s甚至15s的停顿,这对于一个服务器软件来说是不可接受的。 垃圾回收在JVM是很复杂的算法,为了减少停顿做了很多的工作。同时Elasticsearch很努力适应GC,比如通过内部对象的重用,利用网络缓冲区,并挺贵一些特征值例如文档的数量。但是GC的频率和长短是需要你特别留意的信息,因为它是集群不稳定的头号元凶。 如果一个集群经常性的发生长时间GC,那么你的集群一定内存不足并且负载特别高。这些长时间GC会导致节点周期性的脱离集群。这种不稳定会导致分片数据不断的重新生成,以保证集群内的平衡以及足够的分片数量。这会增加网络贷款和磁盘IO,同时你的集群还要承担进行正常的索引数据和查询。 简而言之,长时间的GC是很糟糕的,需要尽可能的减少。 因为GC对Elasticsearch如此重要,你必须对node stats的API显示的这个部分特别熟悉才行。 "jvm": { "timestamp": 1408556438203, "uptime_in_millis": 14457, "mem": { "heap_used_in_bytes": 457252160, "heap_used_percent": 44, "heap_committed_in_bytes": 1038876672, "heap_max_in_bytes": 1038876672, "non_heap_used_in_bytes": 38680680, "non_heap_committed_in_bytes": 38993920, jvm部分首先列出的是有关堆内存使用情况的一般情况,你可以看到多少heap被用到,有多少可以被使用(已经分配了线程),还有堆内存最大可以长到多少。理想情况下heap_committed_in_bytes应该和heap_max_in_bytes相同,如果被分配的堆较小,那JVM将会不得不调整堆的大小,这个过程代价是很高的。如果你的这两个值是不同的,请看《Heap: Sizing and Swapping》章节,确认你配置的是否正确。 heap_used_percent 是你必须盯着看的一个有用的参数。Elasticsearch配置的是当堆使用到75%的时候进行GC,如果你的节点总是大约75%,那你节点正在承受内存方面的压力,这是一个告警,预示着你不久就会出现慢GC。 如果你的heap使用率一直在85%以上,那你有麻烦了,90-95%的概率会因为10-30s的GC 发生性能问题,这还是好的,最坏的就是发生内存溢出。 "pools": { "young": { "used_in_bytes": 138467752, "max_in_bytes": 279183360, "peak_used_in_bytes": 279183360, "peak_max_in_bytes": 279183360 }, "survivor": { "used_in_bytes": 34865152, "max_in_bytes": 34865152, "peak_used_in_bytes": 34865152, "peak_max_in_bytes": 34865152 }, "old": { "used_in_bytes": 283919256, "max_in_bytes": 724828160, "peak_used_in_bytes": 283919256, "peak_max_in_bytes": 724828160 } } }, young, survivor, and old sections 显示了每个代在GC中的使用情况,供你分析。这些数据方便你看到他们的相对大小,但是对于你调查问题往往不是很重要。 ...

2015年5月29日 · 1 分钟

[翻译]Elasticsearch重要文章之四:监控每个节点(Indices部分)

集群的健康只是一个方面,它是对整个集群所有方面的一个很高的概括。节点状态的api是另外一个方面,它提供了关于你的集群中每个节点令你眼花缭乱的统计数据。 节点的状态提供了那么多的统计数据,在你很熟悉它们执勤,你可能不确定哪些指标是至关重要。我们会把需要监控的最重要的几个指标跳出来(我们建议你把所有的统计指标记录下来,例如使用Marvel插件,因为你不知道你哪天可能就需要)。 节点状态的API可以通过下面的方式执行 GET _nodes/stats 在输出内容的开头,我们可以看到集群的名字和我们第一个node的信息: { "cluster_name": "elasticsearch_zach", "nodes": { "UNr6ZMf5Qk-YCPA_L18BOQ": { "timestamp": 1408474151742, "name": "Zach", "transport_address": "inet[zacharys-air/192.168.1.131:9300]", "host": "zacharys-air", "ip": [ "inet[zacharys-air/192.168.1.131:9300]", "NONE" ], ... 节点会根据一个hash值的顺序来显示,也就是node的uuid值。还有一些关于node的网络属性会显示(例如传输地址和HOST)。这些信息有助于调试发现问题,比如那些节点没有加入集群。通常你可能会发现端口用错了,或者节点绑错了IP地址等等。 Indices部分 indices部分列出的是对于所有的索引在该节点上的汇总信息。 "indices": { "docs": { "count": 6163666, "deleted": 0 }, "store": { "size_in_bytes": 2301398179, "throttle_time_in_millis": 122850 }, 它返回的统计信息可以分成这样几个部分: docs: 显示有多少文档在该节点,以及有多少删除的文档还没有从数据段中清除出去。 store: 显示该节点消耗了多少物理存储,这个数据包含主分片和副分片,如果throttle_time_in_millis太大,说明你设置的磁盘流量太低(参考段的合并一章节) "indexing": { "index_total": 803441, "index_time_in_millis": 367654, "index_current": 99, "delete_total": 0, "delete_time_in_millis": 0, "delete_current": 0 }, "get": { "total": 6, "time_in_millis": 2, "exists_total": 5, "exists_time_in_millis": 2, "missing_total": 1, "missing_time_in_millis": 0, "current": 0 }, "search": { "open_contexts": 0, "query_total": 123, "query_time_in_millis": 531, "query_current": 0, "fetch_total": 3, "fetch_time_in_millis": 55, "fetch_current": 0 }, "merges": { "current": 0, "current_docs": 0, "current_size_in_bytes": 0, "total": 1128, "total_time_in_millis": 21338523, "total_docs": 7241313, "total_size_in_bytes": 5724869463 }, indexing: 表示索引文档的次数,这个是通过一个计数器累加计数的。当文档被删除时,它不会减少。注意这个值永远是递增的,发生在内部索引数据的时候,包括那些更新操作。 ...

2015年5月26日 · 1 分钟

[翻译]Elasticsearch重要文章之三:重要配置项的修改

Elasticsearch已经有很好的默认值,特别是涉及到性能相关的配置或者选项。如果你有什么拿不准的,最好就不要动它。我们已经目睹了数十个因为错误的设置而导致集群毁灭,因为它的管理者总认为他改动一个配置或者选项就可以带来100倍的提升。 注意:请阅读全文,所有的配置项都同等重要,和描述顺序无关,请阅读所有的配置选项,并应用到你的集群中。 其它数据库可能需要调优,但总得来说,Elasticsearch不需要。如果你遇到了性能问题,最好的解决方法通常是更好的数据布局或者更多的节点。在Elasticsearch中有很少的"神奇的配置项",如果存在,我们也已经帮你优化了。 指定名字 Elasticsearch默认启动的集群名字叫elasticsearch,你最好给你的生产环境的集群改个名字,改名字的目的很简单,就是防止某个人的笔记本加入到了集群,造成意外。简单修改成elasticsearch_production ,会省掉多次心痛~。 你可以在你的elasticsearch.yml中修改: cluster.name: elasticsearch_production 同样,修改节点的名字也是明智的,就像你现在可能发现的那样,Elasticsearch会在你的节点启动的时候随机给它指定一个名字。这在你开发的时候可能觉得很萌,但是当凌晨3点钟,你还在尝试会议哪台物理机是Tagak the Leopard Lord.的时候,你就不觉得萌了。 更重要的是,这些名师是在启动的时候产生的,每次启动节点,它都会得到一个新的名字,这可以使日志混淆,因为所有节点的名称都是不断变化的。 这些可能性都是很无聊的,我们建议你给每个及诶点一个有意义的名字-一个清楚的,描述性的名字,同样你可以在elasticsearch.yml中配置: node.name: elasticsearch_005_data 路径 默认情况下,Eleasticsearch会把插件、日志以及你最重要的数据放在安装目录下。这会带来不幸的事故。即如果你重新安装Elasticsearch的时候就可能不小心把安装目录覆盖了,如果你不小心,你就可能把你的全部数据删掉了。 不要笑,这种情况,我们见过很多次了。 最好的选择就是把你的数据目录配置到安装目录以外的地方,同样你也可以选择转移你的插件和日志目录。 可以更改如下: path.data: /path/to/data1,/path/to/data2 Path to log files: path.logs: /path/to/logs Path to where plugins are installed: path.plugins: /path/to/plugins 注意:你可以通过逗号分隔指定多个目录。 数据可以保存到多个不同的目录,每个目录如果是挂载在不同的硬盘,做一个人RAID 0是一个简单而有效的方式。Elasticsearch会自动把数据分隔到不同的目录,以便提高性能。 最小主节点数 minimum_master_nodes的设定对你的集群的稳定及其重要,当你的集群中有两个masters的时候,这个配置有助于防止集群分裂。 如果你发生了一个集群分裂,你集群就会处在丢失数据的危险中,因为master节点是被认为是这个集群的最高统治者,它决定了什么时候新的索引可以创建,多少分片要移动等等。如果你有两个master节点,你的数据的完整性将得不到保证,因为你有两个master节点认为他们有集群的控制权。 这个配置就是告诉Elasticsearch当没有足够master候选节点的时候,就不要进行master选举,等master候选节点足够了才进行选举。 该配置必须应该配置成master候选节点的法定个数(大多数个),法定个数就是(master候选节点个数/2)+1. 这里有几个例子: *如果你有10个节点(能保存数据,同时能成为master) 法定数就是6 *如果你有3个候选master,和100个数据节点,法定数就是2,你只要数数那些可以做master的节点数就可以了。 *如果你有两个节点,你遇到难题了,法定数当然是2,但是这意味着如果有一个节点挂掉,你整个集群就不可用了。设置成1可以保证集群的功能,但是就无法保证集群分裂了,像这样的情况,你最好至少保证有3个节点。 elasticsearch.yml中这样配置: discovery.zen.minimum_master_nodes: 2 但是由于ELasticsearch是动态的,你可以很容易的添加和删除节点,这会改变这个法定个数,如果你不得不修改索引的节点的配置并且重启你的整个集群为了让配置生效,这将是非常痛苦的一件事情。 基于这个原因,minimum_master_nodes (还有一些其它配置),允许通过API调用的方式动态进行配置,当你的集群在线运行的时候,你可以这样修改配置: PUT /_cluster/settings { “persistent” : { “discovery.zen.minimum_master_nodes” : 2 } } 这将成为一个永久的配置,并且无论你配置项里配置的如何,这个将优先生效。当你添加和删除master节点的时候,你需要更改这个配置。 集群恢复方面的配置项 当你集群重启时,几个配置项影响你的分片恢复的表现。首先,我们必须明白,如果什么也没配置将会发生什么。 想象一下假设你有10个节点,每个节点保存一个分片,主分片或者副分片,也就是有一个有5个主分片/1个副本 的索引。你需要停止整个集群进行休整(举个例子,为了安装一个新的驱动程序)。当你重启你的集群,很自然会出现5个节点已经起动了,还有5个还没启动的场景。 ...

2015年5月18日 · 1 分钟

[翻译]Elasticsearch重要文章之二:堆内存的大小和swapping

Elasticsearch默认安装后设置的内存是1GB,对于任何一个业务部署来说,这个都太小了。如果你正在使用这些默认堆内存配置,你的集群配置可能有点问题。 这里有两种方式修改Elasticsearch的堆内存(下面就说内存好了),最简单的一个方法就是指定ES_HEAP_SIZE环境变量。服务进程在启动时候会读取这个变量,并相应的设置堆的大小。举例,你可以用下面的命令设置它 export ES_HEAP_SIZE=10g 此外,你也可以通过命令行参数的形式,在程序启动的时候把内存大小传递给它: ./bin/elasticsearch -Xmx10g -Xms10g 备注:确保Xmx和Xms的大小是相同的,防止程序在运行时改变大小,这个是很废的。 一般来说设置ES_HEAP_SIZE环境变量,比直接写-Xmx10g -Xms10g更好一点。 把你的内存的一半给Lucene 一个常见的问题是配置一个大内存,假设你有一个64G内存的机器,我的天,你想把64G内存给Elasticsearch吗? 越大越好! 当然,内存对于Elasticsearch来说绝对是重要的,用于更多的内存数据提供更快的操作。而且还有一个内存消耗大户-Lucene Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。 Lucene的性能取决于和OS的交互,如果你把所有的内存都分配给Elasticsearch,不留一点给Lucene,那你的全文检索性能会很差的。 最后标准的建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存。 不要超过32G 这里有另外一个原因不分配大内存给Elasticsearch,事实上jvm在内存小于32G的时候会采用一个内存对象指针压缩技术。 在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,不是32bit就是64bit,这取决于你的处理器,指针指向了你的值的精确位置。 对于32位系统,你的内存最大可使用4G。对于64系统可以使用更大的内存。但是64位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如LLC, L1等)之间移动数据的时候,会占用更多的带宽。 java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。最终,也就是说堆内存长到32G的物理内存,也可以用32bit的指针表示。 一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的CPU内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达40-50GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32G内存。 这段描述的意思就是说:即便你有足够的内存,也尽量不要超过32G,因为它浪费了内存,降低了CPU的性能,还要让GC应对大内存。 *我有一个1TB内存的机器 这个32GB的线是很很重要的,那如果你的机器有很大的内存怎么办呢?现在的机器内存普遍增长,你现在都可以看到有300-500GB内存的机器。 首先,我们建议编码使用这样的大型机 其次,如果你已经有了这样的机器,你有两个可选项: >你主要做全文检索吗?考虑给Elasticsearch 32G内存,剩下的交给Lucene用作操作系统的文件系统缓存,所有的segment都缓存起来,会加快全文检索。 >你需要更多的排序和聚合?你希望更大的堆内存。你可以考虑一台机器上创建两个或者更多ES节点,而不要部署一个使用32+GB内存的节点。仍然要坚持50%原则,假设 你有个机器有128G内存,你可以创建两个node,使用32G内存。也就是说64G内存给ES的堆内存,剩下的64G给Lucene。 如果你选择第二种,你需要配置cluster.routing.allocation.same_shard.host:true。这会防止同一个shard的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了)。 swapping是性能的坟墓 这是显而易见的,但是还是有必要说的更清楚一点,内存交换到磁盘对服务器性能来说是致命的。想想看一个内存的操作必须是快速的。 如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒,再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕。 最好的办法就是在你的操作系统中完全禁用swapping。这样可以暂时禁用: sudo swapoff -a 为了永久禁用它,你可能需要修改/etc/fstab文件,这要参考你的操作系统相关文档。 如果完全禁用swap,对你来说是不可行的。你可以降低swappiness 的值,这个值决定操作系统交换内存的频率。这可以预防正常情况下发生交换。但仍允许os在紧急情况下发生交换。 对于大部分Linux操作系统,可以在sysctl 中这样配置: vm.swappiness = 1 备注:swappiness设置为1比设置为0要好,因为在一些内核版本,swappness=0会引发OOM(内存溢出) 最后,如果上面的方法都不能做到,你需要打开配置文件中的mlockall开关,它的作用就是运行JVM锁住内存,禁止OS交换出去。在elasticsearch.yml配置如下: bootstrap.mlockall: true 原文地址:https://www.elastic.co/guide/en/elasticsearch/guide/current/heap-sizing.html.

2015年5月17日 · 1 分钟

[翻译]Elasticsearch重要文章之一:不要触碰这些配置

在Elasticsearch中有一些热点,人们可能不可避免的会碰到。我们所理解的,所有的调整就是为了优化,但是这些调整,你真的不需要理会它。因为它们经常会被乱用,从而造成系统的不稳定或者糟糕的性能,甚至两者都有可能。 垃圾收集器 在章节已经有一个简短的介绍,JVM使用一个垃圾收集器来释放不再使用的内存,这篇内容的确是上一篇的一个延续,但是因为重要,所以值得单独拿出来作为一节。 不要更换默认的垃圾收集器! Elasticsearch默认的垃圾收集器是CMS垃圾收集器。这个垃圾收集器因为可以和应用并行处理,所以有很小的暂停,当然它有两个stop-the-world阶段,处理大内存也有点吃力。 尽管这些缺点,它也是目前像Elasticsearch这样低延迟需求的软件的最佳垃圾收集器。官方建议使用CMS。 现在有一款新的垃圾收集器,叫G1垃圾收集器,这款GC设计目的是比CMS更小的暂停时间,以及对大内存的处理能力。它的原理是把内存分成许多区域,并且预测哪些区域最有可能需要回收内存。G1 GC通过首先收集这些区域,产生更小的暂停时间,从而能应对更大的内存。 听起来不错,很遗憾的是G1 GC还是太新,经常有bug爆出,这些bug大都是段错误那种,会导致硬盘崩溃。Lucene的测试套件对GC是很严格残酷的,好像G1 GC一直都无法完全胜任。 我们很希望在将来某一天推荐使用G1 GC,但是对于现在,它还不能足够稳定以满足Elasticsearch和luncene的要求。 线程池 许多人喜欢调整线程池,无论什么原因,人们好像都无法抵挡的想增加线程数。索引太多了?增加线程!搜索太多了,增加线程!节点空闲率低于95%? 增加线程! Elasticsearch默认的线程设置已经是很合理的了。对于所有的线程池(除了搜索的),线程个数是根据CPU核心数设置的。如果你有8个核,你可以同时运行8个线程,那么对于一些线程池,你设置8个线程是合适的。 搜索线程池设置的大一点,是核心数的3倍。 你可能争辩说,一些线程会堵塞在IO处,所以你才想加大线程的。对于elasticsearch来说,这不是问题,因为大多数IO的操作是由Lucene线程管理的,而不是Elasticsearch。 此外,线程池通过彼此之间的合作工作。你不需要担心网络相关的线程因为它在等待磁盘写入而堵塞。因为网络线程早已把这个工作交给另外的线程池,并且网络进行了响应。 最后,你的处理器的计算容量是有限的,拥有更多的线程会导致你的处理器频繁切换线程上下文。一个处理器同时只能运行一个线程,所以当它需要切换到其它不同的线程的时候,它会存储当前的状态(寄存器等等),然后加载另外一个线程。如果幸运的话,这个切换发生在同一个cpu核心,如果不幸的话,这个切换可能发生在不同的核心,这就需要在内核间总线上进行传输。 这个上下文的切换,会循环的带来管理调度开销,在现代的CPU上,估计高达30us,也就是说线程会被堵塞30us,如果这个时间用于线程的运行,估计早就结束了。 人们经常稀里糊涂的设置线程池的值,8个核的CUP,我们见过有人配了60,100甚至1000个线程,这些设置只会让CPU实际工作效率更低。 所以下次请不要调整线程池的线程数,如果真想调整,一定要关注你的CPU核心数,最多设置成核心数的两倍,再多了都是浪费。 原文地址:http://www.elastic.co/guide/en/elasticsearch/guide/current/_don_8217_t_touch_these_settings.html

2015年5月14日 · 1 分钟

Elasticsearch配置官方建议

第一 有关内存 修改ES_MIN_MEM 和 ES_MAX_MEM 建议: in general, the more memory allocated to the process, the better(通常,越大越好) It is recommended to set the min and max memory to the same value, and enable mlockall.(建议设置成相同的值,并且开启mlockall) No more than 50% of available RAM (不要超过内存的一半) No more than 32 GB: If the heap is less than 32 GB, the JVM can use compressed pointers, which saves a lot of memory: 4 bytes per pointer instead of 8 bytes.(不要超过32G,因为对于JVM,小于32G的时候会使用压缩指针,这个会节省很大内存) Increasing the heap from 32 GB to 34 GB would mean that you have much less memory available, because all pointers are taking double the space. Also, with bigger heaps, garbage collection becomes more costly and can result in node instability.(从32G到34G,意味着你失去了更多的可用内存,因为所有的内存指针都变成了double,而且heap越大,GC花销越大,这将是不稳定因素) ...

2015年5月1日 · 2 分钟

使用curl命令操作elasticsearch

第一:_cat系列 _cat系列提供了一系列查询elasticsearch集群状态的接口。你可以通过执行 curl -XGET localhost:9200/_cat 获取所有_cat系列的操作 =^.^= /_cat/allocation /_cat/shards /_cat/shards/{index} /_cat/master /_cat/nodes /_cat/indices /_cat/indices/{index} /_cat/segments /_cat/segments/{index} /_cat/count /_cat/count/{index} /_cat/recovery /_cat/recovery/{index} /_cat/health /_cat/pending_tasks /_cat/aliases /_cat/aliases/{alias} /_cat/thread_pool /_cat/plugins /_cat/fielddata /_cat/fielddata/{fields} 你也可以后面加一个v,让输出内容表格显示表头,举例 name component version type url Prometheus analysis-mmseg NA j Prometheus analysis-pinyin NA j Prometheus analysis-ik NA j Prometheus analysis-ik NA j Prometheus analysis-smartcn 2.1.0 j Prometheus segmentspy NA s /_plugin/segmentspy/ Prometheus head NA s /_plugin/head/ Prometheus bigdesk NA s /_plugin/bigdesk/ Xandu analysis-ik NA j Xandu analysis-pinyin NA j Xandu analysis-mmseg NA j Xandu analysis-smartcn 2.1.0 j Xandu head NA s /_plugin/head/ Xandu bigdesk NA s /_plugin/bigdesk/ Onyxx analysis-ik NA j Onyxx analysis-mmseg NA j Onyxx analysis-smartcn 2.1.0 j Onyxx analysis-pinyin NA j Onyxx head NA s /_plugin/head/ Onyxx bigdesk NA s /_plugin/bigdesk/ 第二:_cluster系列 1、查询设置集群状态 curl -XGET localhost:9200/_cluster/health?pretty=true pretty=true表示格式化输出 level=indices 表示显示索引状态 level=shards 表示显示分片信息 2、curl -XGET localhost:9200/_cluster/stats?pretty=true 显示集群系统信息,包括CPU JVM等等 3、curl -XGET localhost:9200/_cluster/state?pretty=true 集群的详细信息。包括节点、分片等。 3、curl -XGET localhost:9200/_cluster/pending_tasks?pretty=true 获取集群堆积的任务 3、修改集群配置 举例: ...

2015年4月25日 · 2 分钟

elasticseach日常维护之shard管理

elasticseach的数据分片shard,在创建索引之后,在生命周期内就不可改变了,所以在索引开始创建的时候,要根据预估的数据规模合理的设置shard数目。在集群中让shard分布均匀,可以有效的均衡集群负载,所以我们要尽量保证shard的在集群中分布均匀。 每个shard都有自己的编号,从1往后数。你可以通过 curl -XGET ‘http://192.168.1.1:9200/_cat/shards’ 来查看shard分布情况: index_1 100 p STARTED 44217999 12.3gb 192.168.1.1 Strongarm index_1 100 r STARTED 44217999 12.3gb 192.168.1.2 Agony index_1 109 p STARTED 39394176 10.7gb 192.168.1.3 Captain America index_1 109 r STARTED 39394176 10.6gb 192.168.1.1 Smuggler index_1 103 p STARTED 42910705 11.9gb 192.168.1.2 Alyssa Moy index_1 103 r STARTED 42910716 11.9gb 192.168.1.1 Chi Demon index_1 169 r STARTED 40889958 11.4gb 192.168.1.2 Milos Masaryk index_1 169 p STARTED 40889958 11.3gb 192.168.1.1 Sergeant Fury index_1 55 r STARTED 44243841 12.2gb 192.168.1.3 Milos Masaryk index_1 55 p STARTED 44243841 12.1gb 192.168.1.3 Whiteout index_1 214 r STARTED 43512570 12gb 192.168.1.1 Mekano index_1 214 p STARTED 43512570 12gb 192.168.1.2 Sergeant Fury index_1 97 r STARTED 45660486 12.7gb 192.168.1.1 Pathway index_1 97 p STARTED 45660486 12.7gb 192.168.1.2 Mekano ...

2015年4月3日 · 2 分钟

cassandra日常维护之nodetool cleanup

nodetool cleanup 官方的解释是:cassandra在扩容一个节点到新集群的时候,有些数据会复制一份到新节点,然后属于新节点的数据仍然存在那些老节点上。它自己不会主动的去删除。所以在每次扩容之后,你需要在每个老节点上执行cleanup操作。 根据实践: cleanup操作在执行过程中可能会生成一些临时文件,并且导致IO利用率少许上升,机器负载少许增加, cleanup是单线程操作,整体上影响不大,不需要关闭压缩。 cleanup是单机行为,也不需要限制节点stream传输。 只要不是业务特别繁忙的时间,尽可放心的逐节点执行。 在执行cleanup之后,节点数据明显减少。 cleanup在扩容之后随时可以执行。也可以指定只cleanup某个keyspace或者columnFamily nodetool -h 192.168.1.1 [keyspace [cfnames]]

2015年3月19日 · 1 分钟

怎么开发一款elasticseach的插件

工作需要开发一款elasticsearch插件,来完成某项工作,根据其它插件的源码,依瓢画葫芦,总结一下开发一个elasticsearch插件的流程: 第一步:首先创建一个java的maven工程,依赖elasticsearch的jar包 org.elasticsearch elasticsearch 1.2.1 provided 第二步:在类文件下创建一个es-plugin.properties文件,指定插件加载入口 plugin=org.elasticsearch.plugin.zhaoyanblog.myplugin 第三步:创建MyPlugin类,继承org.elasticsearch.plugins.AbstractPlugin。 package org.elasticsearch.plugin.zhaoyanblog; import org.elasticsearch.common.collect.ImmutableList; import org.elasticsearch.common.inject.Module; import org.elasticsearch.plugins.AbstractPlugin; import org.elasticsearch.zhaoyanblog.MyModule; public class MyPlugin extend org.elasticsearch.plugins.AbstractPlugin { //我们的插件Module private static final Collection<Class<? extends Module>> modules = ImmutableList.<Class<? extends Module>> of(MyModule.class); /** * {@inheritDoc} */ public String name() { //插件名字 return "my-plugin"; } /** * {@inheritDoc} */ public String description() { //插件描述 return "my first plugin"; } @Override public Collection<Class<? extends Module>> modules() { return modules; } } 第四步:创建自定义的Module。继承org.elasticsearch.common.inject.AbstractModule。注入我们的服务类。 ...

2015年3月8日 · 1 分钟

cassandra日常维护之nodetool repair

前提 cassandra的根据分区key的操作是很快的,这也是它的优势,但是它的多条件查询是很弱的,特别是如果你有删除操作的话,就更坑爹了。cassandra的删除操作,实际上并不是真的删除,它是执行的插入操作,插入的数据叫做tombstone(墓碑),记录了被删除记录的信息和删除时间。当你根据条件查询的时候,如果它会把满足条件的记录查询出来,包括tombstone。然后过滤掉删除的记录,再把结果返回给你。 现象 如果你的表mykeyspace.t_table有3个副本,主键是(a,b,c) 。你插入4000条数据(a=1),然后再删除掉3999条,你再根据a=1去查询,你会在cassandra的日志中发现一条警告日志. WARN [ReadStage:18926] 2015-02-05 07:18:02,869 SliceQueryFilter.java Read 1 live and 11997 tombstoned cells in mykeyspace.t_table (see tombstone_warn_threshold).... 这个警告信息是根据你的cassandra.yaml里配置的tombstone_warn_threshold决定的。也就是说它搜索了11997=3999*3条数据,才搜到一个1个live。这就是墓碑的危害。当你删除的数据更多的时候。到达配置项tombstone_failure_threshold的值,这次查询就失败了。你会看到下面的ERROR日志。 ERROR [ReadStage:219774] 2015-02-04 00:31:55,713 SliceQueryFilter.java (line 200) Scanned over 100000 tombstones in mykeyspace.t_table; query aborted (see tombstone_fail_threshold) ERROR [ReadStage:219774] 2015-02-04 00:31:55,713 CassandraDaemon.java (line 199) Exception in thread Thread[ReadStage:219774,5,main] java.lang.RuntimeException: org.apache.cassandra.db.filter.TombstoneOverwhelmingException at org.apache.cassandra.service.StorageProxy$DroppableRunnable.run(StorageProxy.java:1916) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caused by: org.apache.cassandra.db.filter.TombstoneOverwhelmingException at org.apache.cassandra.db.filter.SliceQueryFilter.collectReducedColumns(SliceQueryFilter.java:202) 真实的数据实实在在的被删除,发生在两个时期: 第一个时期是新的SSTable文件生成的时候。而且它只删除gc_grace_seconds之前插入的tombstone。 gc_grace_seconds是表结构的一个额外参数,可以通过alter table进行修改。所以说如果你的某个节点挂了,挂的时间超过gc_grace_seconds。可能导致删除的数据又出现了。 第二个时期就来自日常的Nodetool repair操作。每个gc_grace_seconds周期内至少repair一遍。 解决 普通repair nodetool repair基本语法是这样的: nodetool -h host repair [keyspace] [cfnames] 它会修复keyspace.cfnames这个表分区主键token值落在这个节点上的数据(包括master和slave数据)。等你把所有的节点repair一遍, 如果你有三个副本,你相当于repair了三遍数据,所以这个时间会很长。 ...

2015年2月6日 · 2 分钟

cassandra新版驱动,使用SchemaBuilder进行表的操作

在cassanda的官方驱动cassandra-driver-core-2.1.3.jar之前,创建表、修改表、创建索引操作,只能通过拼CQL语句,然后通过session去执行的方式。可能会经常导致语法格式错误。 在最新版的驱动cassandra-driver-core-2.1.3.jar中,提供了一种更方便的对表的修改方式。类似于用于增删改查操作的com.datastax.driver.core.querybuilder.QueryBuilder类,它提供了一个com.datastax.driver.core.schemabuilder.SchemaBuilder类用于对表的操作。 这个类可以构建几乎所有关于对表的操作,这里举例常用的操作: 先引入jar包: <dependency> <groupId>com.datastax.cassandra</groupId> <artifactId>cassandra-driver-core</artifactId> <version>2.1.3</version> </dependency> 然后你就可以像写cql语句一样,流畅的使用SchemaBuilder了。其中省略了session的生成,可以参考《java实现cassandra的增删改查》 //创建表 Create createTbale = SchemaBuilder.createTable("mykeyspace", "mytable") .addPartitionKey("pk1", DataType.cint()) .addColumn("col1", DataType.text()) .addColumn("col2", DataType.bigint()); session.execute(createTbale); //增加一列 SchemaStatement addColumn = SchemaBuilder.alterTable("mykeyspace", "mytable") .addColumn("col3") .type(DataType.cdouble()); session.execute(addColumn); //删除一列 SchemaStatement dropColumn = SchemaBuilder.alterTable("mykeyspace", "mytable") .dropColumn("col2"); session.execute(dropColumn); //修改一列 SchemaStatement alterColumn = SchemaBuilder.alterTable("mykeyspace", "mytable") .alterColumn("col1") .type(DataType.cdouble()); session.execute(alterColumn); //列更改名字 SchemaStatement renameColumn = SchemaBuilder.alterTable("mykeyspace", "mytable") .renameColumn("col1") .to("col4"); session.execute(alterColumn); //增肌索引 SchemaStatement createIndex = SchemaBuilder.createIndex("idx_col4") .onTable("mykeyspace", "mytable") .andColumn("col4"); session.execute(createIndex); //删除索引 Drop dropIndex = SchemaBuilder.dropIndex("mykeyspace", "idx_col4") .ifExists(); session.execute(dropIndex); //删除表 Drop dropTable = SchemaBuilder.dropTable("mykeyspace", "mytable") .ifExists(); session.execute(dropTable);

2015年1月15日 · 1 分钟

cassandra 2.1.2版本的一个bug

最近从cassandra 2.0.8升级到2.1.2,在考察2.1.2的时候,发现了一个2.1.2版的一个bug,一直怀疑是java驱动的原因,后来发现只要满足特定条件,就可以重现,用cqlsh客户端也可以。 bug重现步骤: 首先创建keyspace space1和space2. 在space1里创建一个表table3 在space2里创建两个表table1,table2 在table3里插入一条记录 删除表table1 删除table3插入的那条记录 删除表table2 再根据主键,在tbale3里查询,会发现之前那条记录并未删除。 CREATE KEYSPACE space1 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; CREATE TABLE space1.table3(a int, b int, c text,primary key(a,b)); CREATE KEYSPACE space2 WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3}; CREATE TABLE space2.table1(a int, b int, c int, primary key(a,b)); CREATE TABLE space2.table2(a int, b int, c int, primary key(a,b)); INSERT INTO space1.table3(a,b,c) VALUES(1,1,'1'); drop table space2.table1; DELETE FROM space1.table3 where a=1 and b=1; drop table space2.table2; select * from space1.table3 where a=1 and b=1; you will find that the row (a=1 and b=1) in space1.table3 is not deleted ...

2015年1月4日 · 1 分钟