一、倒排索引
- 倒排索引
词项(term) : 搜索时的一个单位,代表文本中某个词
倒排索引结果是一种将词项映射得到文档的数据结构 - 倒排索引建立步骤
a. 提取词项
首先对文档分词,英文文档用空格分隔
去除无实际意义的词,如is、a、in、as等
对单词统一大小写
单复数,过去式、进行时转换
过滤标点符号
【本步骤,将无用的过滤掉并统一词项格式】
b. 建立倒排索引
将词项映射到文档
【将提取的词项,映射到文档,一个词项可对应多个文档】
c. 建立词项出现的频率、位置和偏移量
文档ID:用于根据词项定位文档的原始信息
频率:记录词项在文档出现的次数,用于搜索相关性算分
位置:记录在文档中是第几个关键字,用于词组查询
偏移量:记录开始和结束的位置,用于做高亮显示
二、 底层内部原理
- 倒排索引不可变性
倒排索引写进磁盘永不会被改变
-
因此无需锁,不必担心多进程同时修改数据问题
-
大部分读请求直接请求内存,不命中磁盘
-
不会因为数据改变而重建缓存中数据
-
单个大的倒排索引允许数据被压缩
-
不支持部分文档更新
-
新文档可被搜索,需重建整个倒排索引,限制索引包含数据量,索引更新频率
- Lucene按段搜索(per-segment)
- 段文件(segment file):存储倒排索引的文件,每个segment是一个倒排索引。文档写入lucene,并且生成完整的segment后才能被搜索
- 提交点(Commit point):记录所有已知段的文件
- 事务日志(translog):防止es宕机导致数据丢失,es每次写入数据同时写入translog。
ps:在倒排索引的不可变前提下,实现倒排索引的数据增删改
- 使用更多索引,按段搜索
- 一个lucene索引包含:segment的集合、Commit point文件
- 按segment搜索,每个segment是一个倒排索引,通过增加新的索引反应最近的修改,而不重写整个倒排索引,当有数据查询请求,每个倒排索引都被遍历,从最早的开始,查询完后对结果合并。
- 实现按段搜索,索引工作流程
-
- 当新文档被索引时,先收集到内存缓存
-
- (提交缓存)将缓存中对象转换为segment存储到磁盘,在Commit point 记录新段信息
-
- (开启新段)新的段被open,此时新段包含的文档可被搜索
-
- 清空内存缓存数据,等接受新文档
- 文档的删除和更新
-
- 每个Commit point 包含一个.del文件,该文件会列出这些被删除文档的段信息
-
- 当一个文档被删时,实际只在.del文件中被标记删除,被标记删除的文档依然可被查询得到,但在最终结果被返回前从结果集中删除
-
- 当一个文档更新时,旧文档被标记删除,新文档被索引到一个新的段,两个文档可能被一个查询匹配到,但在结果返回前会在结果集中删除
- ps:段合并后,标记删除的文档会移除
- 查询流程
-
- 查询被触发
-
- 从Commit point 获取已知段信息,按顺序查询所有段
-
- 聚合所有段的查询结果
-
- 较小成本实现新文档添加到索引
- 按段搜索的问题
-
- 每次commit一个新segment,需将数据fsync到物理磁盘,fsync代价大,每次索引一个文档就fsync一次,代价很大
- 近实时
-
为解决按segment搜索的问题,先将写入内容缓存的lucene数据转换为segment存入文件系统缓存,等合适时机再将文件系统缓存中的数据提交,fsync到磁盘持久化
-
- 先将文档写入内存缓存
-
- 将内存缓存中数据转为segment,存入文件系统缓存,重新reopen,该过程是refresh(一般1秒refresh一次)
-
- 等合适时机,将文件系统缓存中数据提交,fsync到磁盘持久化
-
引入文件系统缓存的遗留问题
-
- 没flush到磁盘的数据,若发生断电、宕机,缓存数据会丢失
-
断电、宕机数据不丢失
-
es引入translog模块,将将数据同时写入内存缓存(lucene)和translog,数据发生丢失时,可从translog重新将数据恢复
- es索引数据完整流程
-
- 写lucene和translog
- 当索引文档(写数据时),先将文档写入lucene,此时lucene在内存中,然后写translog,写成功后将请求发给用户
-
- 使搜索可见
- 将内存中对象转为segment,再reopen,该操作为refresh
-
- 刷Translog日志
- 将内存中的translog刷到磁盘,默认5秒一次
-
- 执行lucene提交(flush)
- 将内存中segment刷到磁盘,更新commit point,清空translog,打开新的translog,默认translog达到512M,flush一次
- 段合并
- 自动refresh,每秒会创建一个新的segment,不加处理会导致搜索变慢
- 段合并流程
-
- 合并进程选一部分大小相似的segment,在后台 合并到更大的segment,不会中断索引和搜索
-
- 合并结束后,老segment被删,新segment被flush到磁盘,写入一个包含新segment且排除旧的和较小的segment,新的commit point
-
- 新的segment被用来搜索,老的segment被删