es弊端2

GitHub使用elasticsearch遇到的一些问题及解决方法

 

http://www.searchtech.pro/github-elasticsearch-usecase

https://github.com/blog/1397-recent-code-search-outages

GitHub此前的搜索使用Solr实现,新上线的搜索基于elasticsearch,运行在多个集群上。由于代码搜索索引很大,GitHub专门为其指定了一个集群。目前该集群包括26个存储节点和8个客户端节点。存储节点负责保存构成搜索索引的数据,而客户端节点负责协调查询活动。每个搜索节点中有2TB的SSD存储。

 

     发生故障时,整个集群保存了大概17TB的代码。数据以分片方式跨越整个集群存储,每个分片(shard)在另一个节点上有一份复制作为冗余,整个索引用去大约34TB存储空间。整个存储容量占用了集群总存储空间的67%左右。代码搜索集群运行在Java 6和elasticsearch0.19.9上。当时这个集群已经正常运行了好几个月。

 

     在1月17日、星期四,准备上线这个代码搜索功能,以完成整个统一搜索的实现。后来发现elasticsearch已经发布了0.20.2版本,其中包括多个功能修复和性能改进。

他们决定推迟代码搜索上线,先升级elasticsearch,希望这可以让新功能的上线更加顺畅。

 

      在1月17日完成了升级,集群中所有节点都成功上线,而且恢复到正常的集群状态。

问题就此开始出现。

 

     从这次升级开始,集群中出现两次故障。

elasticsearch与其他使用大量单一索引存储数据的索引服务不同,它使用分片模式切分数据,这样可以很容易地将数据分布在整个集群中,而且易于管理。每个分片自己是一个Lucene索引,elasticsearch使用Lucene合并索引来聚合所有的分片搜索查询。

 

     在升级后两个小时,第一次故障出现了,恢复的过程中要重启整个集群。我们在索引日志中发现的错误信息指出:有些分片无法分配到特定节点上。进一步检查后,我们发现:这些数据分片有些区段的缓存文件已经损坏,其他在磁盘上已经丢失,但elasticsearch仍然可以恢复有损坏区段缓存的分片,它也可以恢复丢失了一份复制的分片,但是总计510个分片中有7个分片,主分片和副本都已经丢失了。

 

     我们复核了发生故障的环境,当时的结论是:我们发现的问题,来源于集群恢复带来的高负载。对问题的进一步研究后,没有发现其他elasticsearch用户遇到过类似问题。在那个周末,集群没有问题,因此我们决定发布新功能。

 

     下一次故障出现在1月24日、星期四。引起团队注意的,是他们的异常跟踪和监控系统,其中检测到大量的异常爆发。进一步调查指出:大部分异常来自代码查询的超时,以及加入新数据时更新代码搜索索引的后台作业。

 

     这一次,我们开始同时检查集群中全部节点的整体状态和elasticsearch的日志。我们发现:看似随机性的多个存储节点出现高负载。大多数节点的CPU使用率都是个位数,有几个消耗量几乎100%可用的CPU核。我们可以消除系统和IO引起的负载,在这些服务器上唯一导致高负载的是运行elasticsearch的Java进程。现在搜索和索引还是会超时,我们也在日志中注意到:一些节点被很快选为master,此后又很快被移除。为了消除这种快速切换master角色带来的潜在问题,我们决定:最好的方法,是让集群完全停机,然后将其启动,禁止数据分片的分配和重新平衡。

     这种方式让集群恢复上线,但是他们发现elasticsearch日志中有一些问题。集群重启后,他们发现有些节点无法重新加入到集群中,有些数据分片试图二次分配到同一个节点上。这次,他们求助于elasticsearch公司的技术人员,并确认:

 

     这些无法分配的分片(主分片与复制合计23个)都有数据丢失。除数据丢失外,集群花费很多时间试图恢复剩余的分片。在这次恢复过程中,我们必须多次重启整个集群,因为我们加入了多次升级和配置变更,必须要验证和再次恢复分片。这是这次故障中最耗时的部分,因为多次从磁盘中加载17TB索引数据十分缓慢。

 

     和elasticsearch的技术人员一起,他们发现集群中有些地方配置错误,或是需要调优配置,才能得到最佳性能。这次问题也让elasticsearch的技术人员也发现了elasticsearch的两个bug。还有一个很重要的原因:

 

     我们当时运行的Java 6是2009年早期的版本,其中包含多个严重bug,影响elasticsearch和Lucene,同时造成大量内存分配,导致高负载。

 

     根据他们的建议,我们马上升级了Java和elasticsearch,并按他们的推荐调整了配置。具体做法是:在我们的Puppetmaster上,针对这些特定变更,创建了一个新的话题分支,并在环境中的这些节点上运行了Puppet。使用了新的配置、新版本elasticsearch和Java7之后,此前两次故障中遇到的负载不稳定和快速master选举问题再也没有出现了。

 

但是,1月28日,又出现一次故障。不过这次与之前没有关系,完全是人为错误。

 

     一名工程师要把包含有Java和elasticsearch更新的特性分支合并到我们的生产环境中。过程中,工程师将代码搜索节点上的Puppet环境回滚到了生产环境中,这是在部署合并代码之前。这导致elasticsearch在节点上重启,因为Puppet运行在上面。

 

     我们马上就发现了问题根源,并停下集群,防止在一个集群中运行多个版本Java和elasticsearch导致任何问题。当合并代码部署后,我们在所有代码搜索节点上再次运行Puppet,启动集群。我们没有选择在集群处于降级状态时建立索引和查询,而是等它完全恢复。当集群完成恢复后,我们将代码搜索功能打开。

 

     总结这几次故障,Will指出:

 

     在将代码搜索集群中的elasticsearch升级到0.20.2版本之前,我们没有在我们的基础设施中对其进行足够测试,也没在其他集群中测试。还有一个原因是:对于代码搜索集群,我们没有足够的上线前(staging)环境。

 

     现在运行的Java版本已经经过elasticsearch团队测试,而且代码集群配置也经过他们审核,未来的审核过程也已经安排确定。

 

     对于周一的故障,我们正在开发自动化过程,保证这样的效果:如果GitHub上的分支比Puppetmaster上分支更超前,确保这个环境中的Puppet不会运行。

 

最后,elasticsearch团队提供了对运行大集群的几点优化建议:

 

1.设置ES_HEAP_SIZE环境变量,保证JVM使用的最大和最小内存用量相同。如果设置的最小和最大内存不一样,这意味着当jvm需要额外的内存时(最多达到最大内存的大小),它会阻塞java进程来分配内存给它。结合使用旧版本的java情况就可以解释为什么集群中的节点会停顿、出现高负载和不断的进行内存分配的情况。elasticsearch团队建议给es设置50%的系统内存

2.缩短recover_after_time超时配置,这样恢复可以马上进行,而不是还要再等一段时间。

3.配置minimum_master_nodes,避免多个节点长期暂停时,有些节点的子集合试图自行组织集群,从而导致整个集群不稳定。

4.在es初始恢复的时候,一些节点用完了磁盘空间。这个不知道是怎样发生的,因为整个集群只使用了总空间的67%,不过相信这是由于之前的高负载和旧java版本引起的。elasticsearch的团队也在跟进这个问题。

 

Elasticsearch - 处理冲突

http://blog.csdn.net/xifeijian/article/details/49615559

当你使用 索引 API来更新一个文档时,我们先看到了原始文档,然后修改它,最后一次性地将整个新文档进行再次索引处理。Elasticsearch会根据请求发出的顺序来选择出最新的一个文档进行保存。但是,如果在你修改文档的同时其他人也发出了指
令,那么他们的修改将会丢失。

       但是有些时候如果我们丢失了数据就会出大问题。想象一下,如果我们使用Elasticsearch来存储一个网店的商品数量。每当我们卖出一件,我们就会将这个数量减少一个。突然有一天,老板决定来个大促销。瞬间,每秒就产生了多笔交易。并行处理,多个进程来处理交易:
       web_1 中 库存量 的变化丢失的原因是 web_2 并不知道它所得到的 库存量 数据是是过期的。这样就会导致我们误认为还有很多货存,最终顾客就会对我们的行为感到失望。
当我们对数据修改得越频繁,或者在读取和更新数据间有越长的空闲时间,我们就越容易丢失掉我们的数据。

 

有两种能避免在并发更新时丢失数据的方法:

悲观并发控制(PCC)

这一点在关系数据库中被广泛使用。假设这种情况很容易发生,我们就可以组织对这一资源的访问。典型的例子就是当我们在读取一个数据前先锁定这一行,然后确保只有读取到数据的这个线程可以修改这一行数据。

乐观并发控制(OCC)

       Elasticsearch所使用的。假设这种情况并不会经常发生,也不会去组织某一数据的访问。然而,如果基础数据在我们读取和写入的间隔中发生了变化,更新就会失败。这时候就由程序来决定如何处理这个冲突。例如,它可以重新读取新数据来进行更新,又或者它可以将这一情况直接反馈给用户。

Elasticsearch是分布式的。当文档被创建、更新或者删除时,新版本的文档就会被复制到集群中的其他节点上。Elasticsearch即是同步的又是异步的,也就是说复制的请求被平行发送出去,然后可能会混乱地到达目的地。这就需要一种方法能够保证新的数据不会被旧数据所覆盖。
       我们在上文提到每当有 索引 、 put 和 删除 的操作时,无论文档有没有变化,它的 _version 都会增加。Elasticsearch使用 _version 来确保所有的改变操作都被正确排序。如果一个旧的版本出现在新版本之后,它就会被忽略掉。
       我们可以利用 _version 的优点来确保我们程序修改的数据冲突不会造成数据丢失我们可以按照我们的想法来指定_version 的数字如果数字错误请求就是失败

 

我们来创建一个新的博文:

[php] viewplain copy

1.  PUT /website/blog/1/_create  

2.  {  

3.  "title""My first blog entry",  

4.  "text""Just trying this out..."  

5.  }  

反馈告诉我们这是一个新建的文档,它的 _version 是 1 。假设我们要编辑它,把这个数据加载到网页表单中,修改完毕然后。

首先我们先要得到文档:

[php] viewplain copy

1.  GET /website/blog/1  

返回结果显示 _version 为 1 :

[php] viewplain copy

1.  {  

2.  "_index" : "website",  

3.  "_type" : "blog",  

4.  "_id" : "1",  

5.  "_version" : 1,  

6.  "found" : true,  

7.  "_source" : {  

8.  "title""My first blog entry",  

9.  "text""Just trying this out..."  

10. }  

11. }  

现在,我们试着重新索引文档以保存变化,我们这样指定了 version 的数字:

[php] viewplain copy

1.  PUT /website/blog/1?version=1 <1>  

2.  {  

3.  "title""My first blog entry",  

4.  "text""Starting to get the hang of this..."  

5.  }  

1. 我们只希望当索引中文档的_version 是 1 时,更新才生效。请求成功相应,返回内容告诉我们 _version 已经变成了 2 :

[php] viewplain copy

1.  {  

2.  "_index""website",  

3.  "_type""blog",  

4.  "_id""1",  

5.  "_version": 2  

6.  "created": false  

7.  }  

然而,当我们再执行同样的索引请求,并依旧指定 version=1 时,Elasticsearch就会返回一个 409 Conflict 的响应码,返回内容如下:

[php] viewplain copy

1.  {  

2.  "error" : "VersionConflictEngineException[[website][2] [blog][1]:  

3.  version conflict, current [2], provided [1]]",  

4.  "status" : 409  

5.  }  

这里面指出了文档当前的 _version 数字是 2 ,而我们要求的数字是 1 。我们需要做什么取决于我们程序的需求。比如我们可以告知用户已经有其它人修改了这个文档,你应该再保存之前看一下变化。而对于“仓库库存量“问题,我们可能需要重新读取一下最新的文档,然后显示新的数据。所有的有关于更新或者删除文档的API都支持 version 这个参数,有了它你就通过修改你的程序来使用乐观并发控制。

使用外部系统的版本
       还有一种常见的情况就是我们还是使用其他的数据库来存储数据,而Elasticsearch只是帮我们检索数据。这也就意味着主数据库只要发生的变更,就需要将其拷贝到Elasticsearch中。如果多个进程同时发生,就会产生上文提到的那些并发问题。
       如果你的数据库已经存在了版本号码,或者也可以代表版本的 时间戳 。这是你就可以Elasticsearch的查询字符串后面添加 version_type=external 来使用这些号码。版本号码必须要是大于零小于 9.2e+18 (Java中long的最大正值)的整数。
       Elasticsearch在处理外部版本号时会与对内部版本号的处理有些不同。它不再是检查 _version 是否与请求中指定的数值相同,而是检查当前的 _version 是否比指定的数值小。如果请求成功,那么外部的版本号就会被存储到文档中的 _version 中。

例如,创建一篇使用外部版本号为 5 的博文,我们可以这样操作:

[php] viewplain copy

1.  PUT /website/blog/2?version=5&version_type=external  

2.  {  

3.  "title""My first external blog entry",  

4.  "text""Starting to get the hang of this..."  

5.  }  

在返回结果中,我们可以发现 _version 是 5 :

[php] viewplain copy

1.  {  

2.  "_index""website",  

3.  "_type""blog",  

4.  "_id""2",  

5.  "_version": 5,  

6.  "created": true  

7.  }  

现在我们更新这个文档,并指定 version 为 10 :

[php] viewplain copy

1.  PUT /website/blog/2?version=10&version_type=external  

2.  {  

3.  "title""My first external blog entry",  

4.  "text""This is a piece of cake..."  

5.  }  

请求被成功执行并且 version 也变成了 10 :

[php] viewplain copy

1.  {  

2.  "_index""website",  

3.  "_type""blog",  

4.  "_id""2",  

5.  "_version": 10,  

6.  "created": false  

7.  }  

如果你再次执行这个命令,你会得到之前的错误提示信息,因为你所指定的版本号并没有大于当前Elasticsearch中的版本号。

更新文档中的一部分

       文档不能被修改,它们只能被替换掉。 更新API(update)也必须遵循这一法则。从表边看来,貌似是文档被替换了。对内而言,它必须按照找回-修改-索引的流程来进行操作与管理。不同之处在于这个流程是在一个片(shard) 中完成的,因此可以节省多个请求所带来的网络开销。除了节省了步骤,同时我们也能减少多个进程造成冲突的可能性。
       使用 更新 请求最简单的一种用途就是添加新数据。新的数据会被合并到现有数据中,而如果存在相同的字段,就会被新的数据所替换。

更新和冲突

       在本节的开篇我们提到了当取回与重新索引两个步骤间的时间越少,发生改变冲突的可能性就越小。但它并不能被完全消除,在 更新 的过程中还能可能存在另一个进程进行重新索引的可能性。
       为了避免丢失数据, 更新 API会在获取步骤中获取当前文档中的 _version ,然后将其传递给重新索引步骤中的 索引 请求。如果其他的进程在这两步之间修改了这个文档,那么 _version 就会不同,这样更新就会失败。
       对于很多的局部更新来说,文档有没有发生变化实际上是不重要的。例如,两个进程都要增加页面浏览的计数器,谁先谁后其实并不重要 —— 发生冲突时只需要重新来过即可。你可以通过设定retry_on_conflict 参数来设置自动完成这项请求的次数,它的默认值是 0 。

[php] viewplain copy

1.  POST /website/pageviews/1/_update?retry_on_conflict=5 <1>  

2.  {  

3.  "script" : "ctx._source.views+=1",  

4.  "upsert": {  

5.  "views": 0  

6.  }  

7.  }  

http://blog.csdn.net/huwei2003/article/details/41214391

 

Elasticsearch常见问题收集

 

1 _riverStatus Import_fail 

问题描述:发现有个索引的数据同步不完整,在 http://192.168.1.17:9200/_plugin/head/browse - river里看到 _riverStatus Import_fail

查看 elasticsearch log发现有几条数据由于异常造成同步失败,处理好数据好重新建索引数据同步正常。

2 EsRejectedExecutionException[rejectedexecution (queue capacity 50) on

此异常主要是因为 bulk thead poolset  queue capacity =50 这个可以设置打点

打开 elasticsearch.yml 在末尾加上 

threadpool:
    bulk:
        type: fixed
        size: 60
        queue_size: 1000

重新启动服务即可

另:

--查看线程池设置--
curl -XGET "http://localhost:9200/_nodes/thread_pool/"


类似thread pool设置均可以这样修改
threadpool:
    index:
        type: fixed
        size: 30
        queue_size: 1000

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值