ElasticSearch理解与优化,脑裂怎么办?

闲话少说

最近迷上了CPU的中断、内核、epoll等技术,感觉计算机真是个美丽的东西!但是我这里要给大家讲一讲的是ElasticSearch,相信知道这项技术的都知道Lucene吧。虽说ElasticSearch是基于Lucene开发的一款搜索服务器,但是使用起来Lucene是很复杂的,需要对检索的相关知识很懂的大牛才能很好的使用,它复杂也是有其优点的,Lucene做搜索引擎会涉及到很diao的领域,比如数据挖掘,网络爬虫等等。
我们经常听到的ELK,就是ElasticSearch和Logstach(数据收集和日志解析引擎)及一个叫Kibana的分析和可视化平台一起开发的,这三个产品被设计成一个集成解决方案,称为“Elastic Stack”。
对于我这种小白,我当然毫不犹豫的选择了ElasticSearch(后面简称“es”),那么接下来就是吹捧时间!

  1. ElasticSearch是基于Lucene的搜索服务器,提供了一个分布式多用户能力的全文搜索引擎。
  2. 它屏蔽掉了Lucene的复杂特性。
  3. 提供了简单的RESTful API,使我们的开发简单易用。
  4. ElasticSearch由Java语言开发,开源,作为企业级搜索引擎,快速,稳定,可靠,方便。

直奔主题

ElasticSearch特点
最大的特点就是可以模糊查询,由于数据库的模糊查询使用了like就不走索引,所以使用它就可以解决。
而且一般模糊查询的数据会很多,我们也只需要几条或几十条就够,这样也提升了用户体验。
ElasticSearch的实现快速的模糊匹配,相关性查询,实际上是写入数据到ElasticSearch的时候会进行分词。那么匹配时根据某个词去查找对应记录,称它为倒排索引。
ElasticSearch理解与优化,脑裂怎么办?_第1张图片
分词器
ElasticSearch内置了一些分词器,比如Standard Analyzer是按词去切分的,我们用的最多的是IK分词器。
分词器是采用一种算法,把中英文本中的字符拆开,形成一个个小词汇,等用户输入关键字去匹配搜索。
分词流程:

  1. 按分词器拆分出词汇
  2. 去除停用词和禁用词
  3. 英文转为小写

我们使用的IK分词器就是IKAnalyzer,它是一个中文首选的分词器,是按照中文的词语进行拆分的。
一般开发中我们需要导入对应的jar包或者导依赖,然后进行相应配置,配置前,第一行需要一个空行。


那就具体讲一讲ElasticSearch的数据结构

  1. 首先是以FST形式保存的Term Index,其空间占用小,查询速度快,存储在内存中,只存储部分词的前缀。
  2. 然后是Term Dictionary 也就是分词后的汇总字典,字典里面的词会进行排序,查找的时候就可以通过二分法去找。
  3. 还有一个PostingList,通过字典找到相应记录时,会通过这个list去查找记录具体在哪。

ElasticSearch架构
介绍几个术语:

  1. Index:相当于数据库的表
  2. Document:相当于数据库的一行记录
  3. Field:相当于数据库的字段
  4. Mapping:相当于数据库对象的集合
  5. DSL:相当于数据库的sql语句

基本是由ElasticSearch集群组成的,集群由一个个ElasticSearch节点组成,其中有一个Master Node,负责维护索引元数据,切换主分片和副本分片身份等,主节点挂了就会选举一个新节点出来。

脑海突然浮现Hadoop的NameNode

分片就是ElasticSearch最外层的index的数据分发导不同的Node上进行存储。当索引存在大量文档时,由于内存,cpu,硬盘的限制,可能导致应用无法快速去响应。这种情况把数据分为若干个部分,每个部分就是一个分片,也就是一个独立的Lucene索引。它们可以放在不同的ElasticSearch服务里,这样数据就可以在集群中传播。当查询的索引分布在多个分片上时,ElasticSearch会把查询发送给每个相关的分片,然后把结果结合一下,这就好比Hadoop的dataNode,我们在使用分布式文件系统时也是这样,分片数据有多个副本分片,保证了数据不会因服务器故障出现丢失。

所以为了实现高可用,分片也会有主分片和副本分片,数据写入是写到主分片,副本分片复制主分片的数据,读取时主分片和副本分片都可以读。 节点挂了,就会把副本分片提升为主分片,保证了数据的高可用。

ElasticSearch的写入、更新删除、查询

ElasticSearch写入流程
对于数据库来说我们写入只是写入行,就是把数据写进去;对于ElasticSearch来说我们写入时建立索引文件,数据就是索引。注意的是ElasticSearch不支持事务
ElasticSearch写入默认需要1秒后才能被检索的到。
首先集群每个节点都是coordinating node 协调节点,可以作为路由。如果一个节点收到了请求,但是发现这个请求应该是另一个系欸但那处理的,那么它也会把这个请求转发给那个节点。
协调节点是通过hash算法计算在哪个主分片上,然后路由到对应节点,具体算法为:shard=hash(document_id)%(num_of_primary_shards)

所以当路由到对应节点及对应主分片时,操作:

  1. 将数据写到内存缓存区。
  2. 然后把数据写到translog缓存区,translo类似于binlog,就是事务日志。
  3. 隔1秒把数据从buffer中refresh(更新)到FileSystemCache中,生成segment文件,只要生成segment文件,就可以通过索引查询到了。也就是隔1秒生成一个segment文件
  4. refresh完,memory buffer就会清空。
  5. 每隔5秒中,translog从buffer flush到磁盘中。
  6. 定期从FileSystemCache中结合translog内容flush index到磁盘中。
  7. 为了防止节点宕机,内存中的数据会丢失,ElasticSearch会写一份数据的日志。如果节点挂了,就会有5s的数据丢失,然后如果磁盘的translog日志文件大到一定体积或者超过30分钟,就会触发commit操作,把segment文件异步刷到磁盘中,这也是个持久化操作。主分片写完后,会把数据并行发送到副本分片中,所有节点成功写完后会返回ack给协调节点,并返回ack给客户端,完成写入操作。

ElasticSearch更新和删除

  1. 给对应的doc记录打上 .del 标识,删除操作就打上delete状态,如果是更新操作就把原来的doc标志为delete,然后重新写一条数据。
  2. 那么,因为每隔1秒就会生成一个segment文件,对此ElasticSearch有一个merge任务,会把多个segment文件合并成一个。
  3. 合并过程中,会把带有delete状态的doc物理删除掉。

ElasticSearch查询

  1. 根据id查询doc:public Document doc(int docID);
  2. 根据query(搜索词)查询匹配的doc:public TopDocs search(Query q,int n);
  3. 根据id查询的流程时:检索内存的translog文件,然后检索硬盘的translog文件,最后检索硬盘的segment文件。
  4. 根据query匹配doc流程时:同时查询内存和硬盘的segment文件。

ElasticSearch的查询分为三个阶段,用到最多的是QUERY_THEN_FETCH,也就是先查询对应的docID,然后根据id匹配对应的文档。


ElasticSearch优化之脑裂

1.ElasticSearch集群的脑裂现象
在集群中,有时会因为某些节点失效,造成部分节点网络连接会断开,这样他们就跟细胞一样分裂出去,而由于我们集群中的配置,这些节点会形成一个和原来的大群集一样名字的集群,相当于一部分节点分了个山头自立为王,而这个王的名号和原来的王是一样的。这就是集群脑裂现象,这样就有一个很严重,很危险的问题,就是这两个集群会同时索引和修改集群的数据。
而在集群中,我们可通过“ curl -XGET ‘http://master:9200/_cluster/health’ ”命令查看集群状态,看看集群的节点是否正常都在使用,如果有状态信息不一致的那就极有可能是发生脑裂。也可以定时去获取每个节点 /_nodes响应,此操作会返回集群所有节点的状态报告,如果两个节点返回的集群状态不一样,就说明有脑裂的情况发生。
2.发生脑裂的原因

  • 网络问题,一般来说网络通信的异常很可能使节点认为master挂掉,从而选举新的master,这样网络恢复后就造成了脑裂。但我们使用内网时就很少会发生此现象。
  • 内存回收,在集群中,分布式文件系统的data节点中的ElasticSearch进程占用的内存比较大的时候,回收内存时也会有几率造成ElasticSearch进程失去响应。
  • 节点负载严重,这个原因是影响脑裂最大的一个,因为master节点和data节点是混合的,工作节点负载比较大,这样会导致ElasticSearch实例失去响应,这样其他节点就会认为这个master失效,从而选新的master,这样就导致脑裂了。

3.怎么防止脑裂发生

  1. 既然网络,负载,内存回收会导致master失去响应,那我们就对症下药,将等待响应的时间增加就可以防止其一失去响应就选举新master ,虽然这是个比较无脑的方法,但是也是有用的。设置这个参数就行了,默认是3秒:discovery.zen.ping_timeout
  2. 对于master节点容易因负载过大失去响应,我们可以将其和data分离,添加或者用某几个服务器作为纯master,设置 node.master:true node.data:false ,并且将其他data节点设置成 node.master:false node.data:true ,同时为了更快定位master位置可以将data节点发现master的方式改成
    discovery.zen.ping.multicast.enabled: false
    discovery.zen.ping.unicast.hosts: [“master1”, “master2”, “master3”,…]
  3. 比较常用的方法是使用discovery.zen.minimum_master_nodes参数,决定如果要选举master需要多少节点,默认是1。常理来说,我们一般会设置成N/2+1(向下取整)
  4. 第三个方法遗留的问题:minimum_master_nodes参数即使设置了一个正确的值,也有可能发生脑裂,在es1.4.0.Betal版本后,新增了一个参数:discovery.zen.no_master_block可以在没有master时阻塞集群,此时当master没有活动的(或失去响应)时,我们通过此参数设置比如read,write需要拒绝执行,设置值为:all, write ,默认是write。这个方法就比较好,很实用,但要注意es的版本。

4.已经发生了脑裂,该怎么办?

  1. 当我们解决节点后,重启服务器时很容易造成数据丢失或错误。由于已经发生脑裂,那么重启服务器会选择第一个启动的节点为主节点,假设脑裂后有两方集群,这样这个主节点的数据其实和另一方的集群节点是不一样的,同理,会造成数据丢失或错误。
  2. 一般最好先给所有数据重新索引,我们重启服务器的时候一定要注意哪个节点里面的数据才是我们需要的正确的,将不正确的可以将数据目录删除(记得备份),然后启动正确的那个节点为主节点,后续就可以启动集群的其他节点了。

其他优化

  1. es查询很耗操作系统的file

你可能感兴趣的:(技术杂谈,elasticsearch,搜索引擎,java)