一、ES 写入数据的总体流程
1、ES 客户端选择一个节点发送请求,这个节点就是协调节点coordinating Node
2、协调节点对document进行路由,通过hash算法进行路由计算应该落在哪一个分片shard上,然后根基节点维护的shard信息,将请求转发到对应的实际处理shard节点上
shard=hash(document_id)%(num_of_primary_shards)
3、实际节点上的primary shard 主分片处理请求,然后将数据同步到副本节点replica node
4、coordinating node等到primary node都执行成功返回响应结果给客户端
二 refresh 操作(为什么es是近实时的)
primary shard 写数据 -->memory buffer --> 1秒刷新 --> 写入新的segment --> filesystem cache --> 同时清空memory buffer
这个过程叫做refresh,每个segment就是一个倒排索引,只有真正经历了refresh才能被检索到
三、translog事务日志文件
因为memory buffer和Filesystem cache 都是基于内存的,假设服务宕机就会造成数据丢失,所以Es 使用translog 日志文件保证数据可靠性。
在写memory buffer的同时写translog日志文件中,在宕机重启时,es 会读取translog日志文件的最后一个提交点 commint point之后的数据,
恢复到memorybuffer和FileSystem cache中
1、translog 日志文件也是先写 FileSystem cache 每个5秒刷新到磁盘,如果这时宕机会丢失5秒的数据
2、可以设置translog 设置每次fsync同步刷盘,但是这样会影响性能
四、flush 操作
重复上面的动作,translog日志文件不断增大,当translog 每30分钟或者大小超过512M时,就会触发flush操作,将memory buffer 的数据写入到一个新的segment
文件中,将内存中的memorybuffer全部落盘然后清空translog,创建一个新的translog flush结束。
ES 的 flush 操作主要通过以下几个参数控制:
index.translog.flush_threshold_period:每隔多长时间执行一次flush,默认30m
index.translog.flush_threshold_size:当事务日志大小到达此预设值,则执行flush,默认512mb
index.translog.flush_threshold_ops:当事务日志累积到多少条数据后flush一次
五、segment 合并原理
1、ElasticSearch 有一个后台进程专门负责 segment 的合并,定期执行 merge 操作;
2、将多个小 segment 文件合并成一个 segment;
3、在合并时被标识为 deleted 的 doc(或被更新文档的旧版本)不会被写入到新的 segment 中。
4、合并完成后,然后将新的 segment 文件 flush 写入磁盘;
5、然后创建一个新的 commit point 文件,标识所有新的 segment 文件,并排除掉旧的 segement 和已经被合并的小 segment;
6、然后打开新 segment 文件用于搜索使用,等所有的检索请求都从小的 segment 转到大 segment上以后,删除旧的 segment 文件;
这时候,索引里 segment 数量就下降了
查询Elasticsearch 查询所有索引分配情况
Optimize 命令
1、Es的API也提供了命令强制合并segment,即optimize命令,它可以强制一个分片shard 合并成max_num_segment (合并的段数),一个索引segment段数越少,搜索就越高
2、optimize 不要用在经常更新的索引上,Es默认的合并策略就是最优的选择
3、optimize 对一些固定的数据,只是用存储后搜索是最优的使用选择,比如日志都按照天或者周 等日期的形式存储的,用optimize非常合适。
API如:
POST /索引/_optimize?max_num_segment=1
Segment细节性能优化:
1、合并策略:合并线程是按照一定的运行策略来挑选segment进行归并,主要有一下几条
a、index.merge.policy.floor_segment:默认20M,小于该值的segment优先归并
b、index.merge.policy.max_merge_at_once:默认一次最多归并10个segment
c、index.merge.policy.max_merge_at_once_explicit:默认forcemerge时最多一次并归30个segment
d、index.merge.policy.max_merge_segment:默认5G,大于该值的segment,不用参与归并,forcemerge除外
2、segment延时提交,默认一秒,本身segment比较大,减少合并次数提升性能
配置:curl -XPUT localhost:9200/索引名称/_settings -d'{"index.refresh_interval":"10s"}'
3、对特定字段field禁用norms和doc_values和stored
a、如果不需要对field不进行相关度算分,norms禁用,减少倒排索引的内存占用量,字段粒度配置:omit_norms=true
b、如果不需要对field进行聚合和排序,可以禁用doc_values字段
c、如果field只需要提供搜索,可以不需要返回stored设置false
ElasticSearch mapping 配置:
1、coyp_to 提供跨字段进行检索,但是会额外占用空间
2、_source 存储post提交到Es原始json,不读取原始json可以关闭,但是关闭后无法重建索引reindex
3、store 是否单独存储字段,会占用额外空间,与_source独立,会保存两份数据
4、doc_values 支持排序和聚合 与source独立 会保存两份数据,关闭后无法聚合和排序 docvalue_fields 选的显示那些字段
5、index 关闭后字段无法进行搜索,但是仍然可以存储到_source和doc_values中,字段可以排序和聚合
6、enable 是否对该字段进行处理,关闭后只有_source中存储,类似index与doc_values总开关
ES 搜索流程
搜索被执行成一个两阶段 即:Query then Fetch
Query阶段:
客户端发送请求到coordinate node,协调节点将请求广播到Primary shard 或 replica,每个分片在本地搜索构建一个匹配文档为from+size的优先队列。
接着每个分片返回各自优先队列中,所有docId和打分值给协调节点,有协调节点进行数据合并、排序、分页操作产出最终结果。
Fetch阶段:
协调节点根据Query阶段产生的结果,去各个节点查询docId实际的document内容,最终由协调节点返回给客户端。
ES 高并发下如何保证读写一致
1、对于更新操作:可以使用版本号乐观锁操作,已确定新版本不被旧版本覆盖
2、对于写操作,一致级别支持quorum/one/all 默认quorum ,既有大部分节点可用才允许写操作。
quorum:要求ES中大部分的shard活跃可以的才可以执行写操作
one:只要有一个primary shard 活跃可以用的就可以执行写操作
all:写操作只有所有的primaryshrad和replica node 节点都可以用 才可以操作
3、对于读操作
a、可以设置relication为sync(默认同步),这使得主分片和副本分片都完成才返回;
b、如果设置replication为async(异步),可以通过搜索请求参数_preference为主来查询主分片,以确保是最新文档
ES集群如何选举master
1、ElasticSearch 分布式原理
ElasticSearch 会对存储的数据进行切分,划分到不同的分片上,同时每一个分片会生成多个副本,从而保证分布式环境高可用。
ES 只有建立index和type时经过master,而数据的写是通过路由集群的任意节点。
2、节点失效检测
在ES中有两个相关工作进程专门用于检查节点的存活状态,分别是:
a、NodesFaultDetection 节点故障检测即NodesFD,用于定期检查集群中节点是否存活
b、MasterFaultDetection 主节点故障检测即MasterFD ,用于定期检查Master节点是否存活
它们分别通过ping的方式定期检查集群中,普通节点和master节点是否存活
3、ES集群如何选举Master
ElasticSearch 选举主要zenDiscovery模块负责,通过ping(节点之间通过这个rpc来发现彼此) 和Unicast (单播模块包含一个主机列表以控制那些节点需要ping通)这两部分;
1、确认候选节点最少投票数量配置:discovery.zen.minimun_master_node
2、选举时,集群中每个节点对所有master候选节点(mode.master:true)根据nodeId进行字典排序,id最小的排在前面,也是集群状态版本
最高的排前面,里面是最新的meta信息,然后选择第一个节点第0位
3、如果对某个节点投票数达到阈值,并且节点也选择自己,那这个节点就是master,否则重新选举,重复上述条件
ES 建立索引阶段性能提升方法:
1、如果是大批量导入_bulk,可以设置index.number_of_replicas:0等数据导入完在开启副本
2、使用批量请求并调整其大小:每个批次5-15MB大小
3、如果搜索结果不需要近实时性,可以设置每个索引的index.refresh_interval 改到比如30M 根据自己业务绝对这个时间
4、增加index.translog.flush_threshold_size大小,从默认值512M比如更改到1GB
5、使用SSD存储介质
6、段和合并:elasticseach 默认值20MB/s.但是如果使用SSD,可以调整到100-200MB/s 如果你是在做批量导入,完全不在意搜索,你可以彻底关掉合并限流
Elasticsearch 三种分页方式
1、from+size 比如 from=500,size=10
假设5个分片,则是从5个分片中各自取100条,汇总成500条,选择最后的10条数据
2、scroll 深分页
为了解决form+size深分页问题,ElasticSearch 提出一个scroll 滚动的方式,scroll每次只能返回一页内容,然后返回一个scroll_id
根据scroll_id 继续获取下一页内容,但是scroll不能调页
在使用scroll时from必须设置为0,而scroll比较耗内存,在不用的时候及时删掉scroll_id:DELETE _search/scroll/DnF1ZXJ5VGhlbkZldGNo.....
scroll 的方式,官方的建议不用于实时的请求(一般用于数据导出),
因为每一个 scroll_id 不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。
3、search_after 深分页
search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。
但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。
a、使用search_after必须要设置from=0
b、在最后一条数据里拿到sort属性的值传入到search_after,如:search_after[1541495312521(时间戳),"d0xH6GYBBtbwbQSP0j1A(uuid)"]