- 正排索引:文档ID到文档内容和单词的关联
- 倒排索引:单词到文档ID的关系
备注:ES对文档每个字段都有自己的倒排索引,可以指定某些字段不做索引,这样可以节省存储空间,缺点是这个字段无法被搜索。
倒排索引不可变性
倒排索引采用Immutable Design,一旦生成,不可更改。
- 优点
(1)无需考虑并发写文件的问题,避免了锁机制带来的性能问题
(2)一旦读入内核的文件系统缓存,便留在那里。只要文件系统存有足够大的空间。大部分请求就会直接请求内存,不会命中磁盘,提升了很大的性能 - 缺点
不可变性也带来了另一个挑战,如果需要让一个新的文档可以被索引,需要重建整个索引。
倒排索引文件结构

- 在Lucene中,单个倒排索引文件被称作Segment。Segment是不可变更的。多个Segment汇总在一起,称为Lucene的Index,其对应的就是ES中的Shard
- 当有新文档写入时,会生成新Segment,查找时会同时查找多个Segments,并且对结果进行汇总。Lucene中有个Commit Point文件,用来记录所有Segments信息。
- 删除文档的信息,会保存在.del文件中。在搜索时,返回的结果会根据.del中记录的文档信息,将其过滤掉。
ES索引文档的过程
请求发送到Coordinating Node,如果该节点不是Master节点,需要将该请求转发到Master.
Master节点通过路由算法,确定该分片在哪个节点上:
shard = hash(_routing) % (num_of_primary_shards)
默认使用文档ID作为_routing值,也可以通过API指定_routing值分片节点收到请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache(磁盘缓存),这个从Momery Buffer到Filesystem Cache(磁盘缓存)的过程就叫做Refresh。在写入Buffer的同时,会同时写一个Transaction Log(每个分片会有一个Log文件)。当做refresh时,Buffer会被清空,Transaction Log不会清空。
ES每30分钟,会有一个Flush操作。该操作会调用fsync,将Filesystem Cache中的数据写入segment文件,旧的translog将被删除并开始一个新的translog
ES会自动进行Merge操作,该操作会将多个Segment文件合并,在.del文件中被标记为删除的文档将不会被写入Segment,并且清空.del文件中的内容
ES Refresh

- 创建索引时,数据并不会直接写入Segment文件,而是会先写入一个Index Buffer缓冲区。而将Index Buffer中数据写入Segment文件的过程就叫Refresh.
- Refresh的频率默认是每秒一次。可通过index.refresh_interval进行配置。Refresh后,数据就可被搜索到了,这也是为什么ES被称为近实时搜索。
- Index Buffer被占满时,也会触发Refresh,默认值是JVM的10%
Transaction Log

Segment写入磁盘的过程相对耗时,所以借助文件系统缓存,Refresh时,先将Segment写入文件缓存中,以开放查询。但为了保证数据不会丢失,所以在创建索引时,会同时写Tansaction Log,类似操作日志。
在ES进行Refresh时,Index Buffer会被清空,Transaction Log不会清空。
ES Flush

Flush操作:
- 调用refresh,清空index buffer
- 清空transaction log
Flush触发条件:
- 默认30分钟调用一次
- Transaction Log 默认达到512M
ES Merge & Force Merge API
随着索引的不断创建,Segments文件会越来越多。ES会自动进行merge操作,将多个segments文件合并,以提高查询效率。但Merge是很重的操作,对磁盘有频繁IO操作,会对系统性能有影响。
除此之外,我们还可以通过api强制merge:
POST /my_index/_forcemerge?max_segements_num=1
// max_num_segments:最终合并成多少个segments
我们还可以通过配置refresh的频率(refresh_interval),来适当减少Segments产生的数量。