一、写入数据
1、ES 的任意节点都可以作为协调(Coordinating)节点接受请求(包括新建、索引或者删除请求),每个节点都知道集群中任一文档位置;
2、协调节点会通过 routing 字段计算出一个主分片(primary shard),并把请求路由到主分片所在节点(routing 是一个可变值,默认是文档的 _id) ;
代码语言:javascript复制shard = hash(routing) % number_of_primary_shards
tips:这就解释了为什么我们要在创建索引的时候就确定好主分片的数量,并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
3、在主分片节点上,数据会先被写入(index buffer)中,同时写入 translog,这个时候数据还不能被搜索到(这个也是 es 是近实时搜索的原因);
tips:整个过程大部分在内存中,如果断电就会导致数据丢失。因此,ES 引入了 translog,数据写入内存时,会同时写入 translog(会立即落盘),来保证数据不丢失。
4、经过一段时间(默认 1s)或者 index buffer 满了(默认 jvm 的 10%),会将 index buffer 中的文档 refresh 到系统文件缓存(os cache),然后再刷入到 lucene 的底层文件 segment 中,同时建立倒排索引,这个时候文档是可以被搜索到的;
5、 由于 segment 的不可变性,随着 segment 越来越多,每打开一个 segment 就会消耗一个文件句柄,导致查询性能越来越差。这时,ES 后台会有一个单独线程专门合并 segment,将零碎的小的 segment 合并成一个大的 segment;
6、经过一段时间(默认30 min)或者 tanslog 满了(默认512M),会将文件系统缓存的 segment 落盘;
7、如果主分片所在的节点请求执行成功,它会将请求同步转发到副本分片所在节点,做到主副数据的一致性,一旦所有的副本分片都报告成功,主分片节点将向协调节点报告成功,协调节点向客户端报告成功。因此,数据写入,主副本之间采用的是同步写入过程。
tips:写一致性默认的策略是 —— Quorum ,即大多数的分片副本状态没问题才允许执行写操作。
8、当集群中某个节点宕机,该节点上所有分片中的数据全部丢失(既有主分片,又有副分片);丢失的副分片对数据的完整性没有影响,丢失的主分片在其他节点上的副分片会被选举成主分片;所以整个索引的数据完整性没有被破坏。
9、如果是删除操作,refresh 的时候就会生成一个 .del 文件,逻辑删除,将这个 document 标识为 deleted 状态,在搜索的搜索的时候就不会被搜索到了。
10、如果是更新操作,就是将原来的 document 标识为 deleted 状态,然后新写入一条数据。
二、查询数据
代码语言:javascript复制GET my-index/_doc/0
1、ES 的任意节点都可以作为协调(Coordinating)节点接受请求,每个节点都知道集群中任一文档位置; 2、协调节点对 id 进行路由,从而判断该数据在哪个 shard,然后将请求转发给对应的节点,此时会使用随机轮询算法,在 primary shard 和 replica shard 中随机选择一个,让读取请求负载均衡, 3、处理请求的节点返回 document 给协调节点。 4、协调节点,返回 document 给客户端。
三、检索数据
代码语言:javascript复制GET /my-index/_search
{
"query": {
"match_all": {}
}
}
1、ES 的任意节点都可以作为协调(Coordinating)节点接受请求,每个节点都知道集群中任一文档位置; 2、协调节点进行分词等操作后,去查询所有的 shard 节点。 3、所有 shard 将满足条件的数据(id、排序字段等)信息返回给协调节点。 4、协调节点将数据重新进行排序,获取到真正需要返回的数据的 id。 5、协调节点再次请求对应的 shard (此时有 id 了,可以直接定位到对应shard)。 6、获取到全量数据,返回给客户端。
tips: ES 要尽量避免深度分页查询,因为每个 shard 都会返回 from size 的数据。比如我们要每页显示 10 条,查询第 10000 页数据,那么每个分片就要返回10010 条数据,协调节点要处理更多的数据,这会严重的影响性能。 参考博文:
- 分布式系统的 Quorum 策略
- 协调节点