目录
es, 倒排索引优点
倒排索引是不可更改的,一旦它被建立了,里面的数据就不会再进行更改。这样做就带来了以下几个好处:
1. 不用给索引加锁,因为不允许被更改,只有读操作,所以就不用考虑多线程导致互斥等问题。
2. 索引一旦被加载到了缓存中,大部分访问操作都是对内存的读操作,省去了访问磁盘带来的io开销。
3. 倒排索引具有不可变性,所有基于该索引而产生的缓存也不需要更改,因为没有数据变更。
4. 倒排索引可以压缩数据,减少磁盘io及对内存的消耗。
新增修改数据
新增数据流程
- 1. 文档数据写入到缓存
- 2.1 translog(文档放在buffer的同时,都会把文档记录在translog中)
- 2.2 refresh立即把缓存中的文档写入segment中(分片的refresh频率默认每秒1次,通过index.refresh_interval:100s参数控制,手动操作:curl -XPOST127.0.0.1:9200/_refresh)(如果要落地磁盘,需要使用fsync的操作来确保磁盘写入成功,这个过程是阻塞的,比较耗时,不使用 fsync 的方式落地磁盘)
- 4. segment写入文件系统缓存中(文件系统缓存中的数据可以被正常读取,ES利用这个特性,在segment被commit到磁盘之前,就打开对应的segment,这样存放在这个segment中的文档就可以立即被搜索到了)
- 5. 当达到一定的时间间隔,或者translog足够大时,就会执行commit行为,将所有缓存中的segment写入磁盘。确保写入成功后,translog就会被清空。 (在es中可以通过_flush api进行手动触发,手动操作:curl -XPOST127.0.0.1:9200/tcpflow-2015.06.17/_flush?v)
每个commit point都会维护一个.del文件,
当一个文档删除时,首先会在.del文件内记录在某个segment内某个文档已经被删除。在segment中,被删除的文档依旧是能够被搜索到的,不过在返回搜索结果前,会根据.del把那些已经删除的文档从搜索结果中过滤掉。
当一个文档发生更新时,首先会在.del中声明这个文档已经被删除,同时新的文档会被存放到一个新的segment中。这样在搜索时,虽然新的文档和老的文档都会被匹配到,但是.del会把老的文档过滤掉,返回的结果中只包含更新后的文档。
lucene将一个大的倒排索引拆分成了多个小的段segment。
每个segment本质上就是一个倒排索引。
在lucene中,同时还会维护一个文件commit point,用来记录当前所有可用的segment,当我们在这个commit point上进行搜索时,就相当于在它下面的segment中进行搜索,每个segment返回自己的搜索结果,然后进行汇总返回给用户。
Segment合并
每个segment都会占用独立的文件句柄/内存/消耗cpu资源,在查询的时候,需要在每个segment上都执行一次查询,这样是很消耗性能的。
es会自动定期的将多个小segment合并为一个大的segment,在segment合并的过程中,会自动将.del中的文档丢掉,从而实现真正意义上的删除操作。
当新合并后的segment完全写入磁盘之后,es就会自动删除掉那些零碎的segment,之后的查询都在新合并的segment上执行。
Segment的合并会消耗大量的IO和cpu资源,这会影响查询性能。
在es中,可以使用optimize接口,来控制segment的合并。
如:
POST/logtest-2021-01/_optimize?max_num_segments=1
这样,es就会将logtest-2021-01中的多个segment合并为1个。
对于那些更新比较频繁的索引,不建议使用optimize去执行分片合并,交给后台的es自己处理就好了。
fsync和fdatafsync
操作系统数据落地磁盘过程:
- 数据首先保存在内存中
- write 内存中的页缓存(page cache),更新到磁盘
- 脏页面不会立即更新到磁盘中,而是由操作系统统一调度,如由专门的flusher内核线程在满足一定条件时(如一定时间间隔、内存中的脏页达到一定比例)将脏页面同步到磁盘上(放入设备的IO请求队列)。
fsync 的功能是确保文件fd所有已修改的内容已经正确同步到磁盘上,该调用会阻塞等待直到设备报告IO完成。
文件的尺寸(st_size)如果变化,是需要立即同步的,否则OS一旦崩溃,即使文件的数据部分已同步,由于metadata没有同步,依然读不到修改的内容。而最后访问时间(atime)/修改时间(mtime)是不需要每次都同步的,只要应用程序对这两个时间戳没有苛刻的要求,基本无伤大雅。
fsync | fdatasync | |
io操作次数 | 2 | 1 |
同步修改内容(脏页) | 是 | 是 |
同步文件信息(metadata,包括size、访问时间st_atime & st_mtime等等) | 是 | 否 |
Berkeley DB处理日志文件方式:
1.每个log文件固定为10MB大小,从1开始编号,名称格式为“log.%010d"
2.每次log文件创建时,先写文件的最后1个page,将log文件扩展为10MB大小
3.向log文件中追加记录时,由于文件的尺寸不发生变化,使用fdatasync可以大大优化写log的效率
4.如果一个log文件写满了,则新建一个log文件,也只有一次同步metadata的开销
创建索引的注意事项
es的倒排索引默认对全部字段自动创建,创建索引的注意事项就是,不对那些字段进行创建
- 不需要索引的字段,一定要明确定义出来,因为默认是自动建索引的
- 同样的道理,对于String类型的字段,不需要analysis的也需要明确定义出来,因为默认也是会analysis的
- 选择有规律的ID很重要,随机性太大的ID(比如java的UUID)不利于查询