基本概念
ES依赖一个重要的组件Lucene,关于数据结构的优化通常来说是对Lucene的优化,它是集群的一个存储于检索工作单元,结构如下图:
在Lucene中,分为索引(录入)与检索(查询)两部分,索引部分包含 分词器
、过滤器
、字符映射器
等,检索部分包含 查询解析器
等。
一个Lucene索引包含多个segments,一个segment包含多个文档,每个文档包含多个字段,每个字段经过分词后形成一个或多个term。
通过Luke工具查看ES的lucene文件如下,主要增加了_id和_source字段:
Lucene索引实现
Lucene 索引文件结构主要的分为:词典
、倒排表
、正向文件
、DocValues
等,如下图:
Lucene 随机三次磁盘读取比较耗时。其中.fdt
文件保存数据值损耗空间大,.tim
和.doc
则需要SSD存储提高随机读写性能。
另外一个比较消耗性能的是打分流程,不需要则可屏蔽。
ES索引与检索分片
ES中一个索引由一个或多个lucene索引构成,一个lucene索引由一个或多个segment构成,其中segment是最小的检索域。
数据具体被存储到哪个分片上:
shard = hash(routing) % number_of_primary_shards
默认情况下 routing参数是文档ID (murmurhash3),可通过 URL中的 _routing
参数指定数据分布在同一个分片中,index和search的时候都需要一致才能找到数据。
如果能明确根据_routing
进行数据分区,则可减少分片的检索工作,以提高性能。
ES工作原理
ES写数据工作原理
ES读数据工作原理
可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。
ES搜索数据工作原理
ES底层原理
ES写数据底层原理
为什么叫 es 是准实时的?
NRT,全称 near real-time。默认是每隔 1 秒 refresh 一次的,所以 es 是准实时的。
可以通过 es 的 restful api 或者 java api,手动执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 os cache中,让数据立马就可以被搜索到。
只要数据被输入 os cache 中,buffer 就会被清空了,因为不需要保留 buffer 了,数据在 translog 里面已经持久化到磁盘去一份了。
重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 buffer 数据写入一个又一个新的 segment file 中去,每次 refresh 完 buffer 清空,translog 保留。
随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 commit 操作。
commit 操作发生第一步,就是将 buffer 中现有数据 refresh 到 os cache 中去,清空 buffer。
然后,将一个 commit point写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file,同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去。
最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。
这个 commit 操作叫做 flush。默认 30 分钟自动执行一次 flush,但如果 translog 过大,也会触发 flush。
flush 操作就对应着 commit 的全过程,我们可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。
translog 日志文件的作用是什么?
执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中
无论是 buffer 还是 os cache 都是内存,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog 中
一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。
translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去
所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会丢失 5 秒钟的数据。
但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。
其实 es 第一是准实时的,数据写入 1 秒后可以搜索到;可能会丢失数据的。有 5 秒的数据,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盘上,此时如果宕机,会导致 5 秒的数据丢失。
总结一下,数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。
每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。
ES删除/更新数据底层原理
ES性能优化
优化索引性能
优化检索性能
{
"mappings": {
"data": {
"dynamic": "false",
"_source": {
"includes": ["XXX"] -- 仅将查询结果所需的数据存储仅_source中
},
"properties": {
"state": {
"type": "keyword", -- 虽然state为int值,但如果不需要做范围查询,尽量使用keyword,因为int需要比keyword增加额外的消耗。
"doc_values": false -- 关闭不需要字段的doc values功能,仅对需要排序,汇聚功能的字段开启。
},
"b": {
"type": "long" -- 使用了范围查询字段,则需要用long或者int之类 (构建类似KD-trees结构)
}
}
}
},
"settings": {......}
}