04.ES原理常识原理
Elasticsearch(ES)的核心原理包括搜索引擎的工作机制、倒排索引、索引的创建和更新、并发控制等
搜索引擎通过查询分析、分词、关键词匹配和搜索排序完成一次检索
倒排索引将关键词与文档建立映射关系,实现高效搜索
ES的索引具有不变性,通过创建新段(Segment)更新数据并发控制中,ES主要使用乐观锁机制确保一致性
ES写入流程包括写入内存、磁盘和提交操作,结合translog日志文件保障数据的持久性
# 01.ES 原理
# 1、搜索引擎原理
- 一次完整的搜索从用户输入要查询的关键词开始
- 比如想查找 Lucene 的相关学习资料,我们都会在 Google 或百度等搜索引擎中输入关键词
- 比如输入“Lucene ,全文检索框架”,之后系统根据用户输入的关键词返回相关信息
一次检索大致可分为四步
第一步:查询分析
- 正常情况下用户输入正确的查询,比如搜索“里约奥运会”这个关键词
- 用户输入正确完成一次搜索,但是搜索通常都是全开放的,任何的用户输入都是有可能的
- 很大一部分还是非常口语化和个性化的,有时候还会存在拼写错误,用户不小心把“淘宝”打成“涛宝”
- 这时候需要用自然语言处理技术来做拼写纠错等处理,以正确理解用户需求
第二步:分词技术
- 这一步利用自然语言处理技术将用户输入的查询语句进行分词
- 如标准分词会把“lucene全文检索框架”分成 lucene | 全 | 文|检|索|框|架|
- IK分词会分成: lucene|全文|检索|框架|,还有简单分词等多种分词方法
第三步:关键词检索
- 提交关键词后在倒排索引库中进行匹配,倒排索引就是关键词和文档之间的对应关系,就像给文档贴上标签
- 比如在文档集中含有 "lucene" 关键词的有文档1 、文档 6、文档9
- 含有 "全文检索" 关键词的有文档1 、文档6 那么做与运算
- 同时含有 "lucene" 和 "全文检索" 的文档就是文档1和文档6,在实际的搜索中会有更复杂的文档匹配模型
第四步:搜索排序
- 对多个相关文档进行相关度计算、排序,返回给用户检索结果
# 2、倒排索引
- 在构建索引时,首先会对文档进行分词处理,得到一系列的词项
- 然后将这些词项添加到词项字典中,并更新相关的Posting List
- 在此过程中,还会更新Term Index,以保证可以快速定位到词项字典中的词项
- 在进行搜索时,首先会在Term Index中查找词项
- 然后通过Term Dictionary找到对应的Posting List,从而找到包含该词项的所有文档
# 3、ES 创建索引
# 0)索引不变性
索引的不变性
- 倒排索引一旦写入磁盘后,不会再修改,这避免了多进程的并发修改问题
不变性有重要的价值
无需锁
:由于数据不变,不需要额外的锁机制来保证一致性缓存优化
:索引读入内存缓存后,只要有足够空间,后续读请求直接来自内存,不会触发磁盘访问持久缓存
:例如过滤缓存,在索引生命周期内无需重建
# 1)动态更新索引
更新索引原理
- 通过增加新的索引段(Segment)来反映更新,而非修改已有的倒排索引
Lucene 段(Segment)
索引以
段(Segment)
为单位管理,每个段包含部分文档的倒排索引
新文档会创建新段,且一旦创建,段内容不可修改
段会定期合并,未读取的段会删除
ES段搜索
- Elasticsearch继承Lucene设计,查询时并行对多个段进行搜索,合并结果,提高搜索效率
- 举例
- 假设我们有一个包含1亿篇文档的大型索引,这个索引被分成了10个段,每个段包含1000万篇文档
- 当我们进行搜索时,Elasticsearch会启动10个线程,对这10个段分别进行搜索
- 当所有线程都搜索完成后,Elasticsearch会将这10个结果按照一定的规则合并,得到最终的搜索结果
Lucene 提交点(Commit Point)
提交点
在Lucene中起到了版本控制
和索引状态管理
的作用当索引发生变化时
,例如添加、删除或更新了文档
,Lucene会写入新的文件来反映这些变化
- 在Lucene中,索引数据是存储在多个不同的文件中的
- 假设你在初始状态下有三个索引文件:file1, file2, file3
- 然后你添加了一些文档,Lucene生成了新的文件file4
- commit后,Lucene生成新的提交点,这个提交点文件列表就是[file1, file2, file3, file4]
- 如果此后你再删除了一些文档,Lucene可能会生成新的文件file5
- 然后你又执行了commit操作,新的提交点的文件列表就可能是[file1, file2, file3, file4, file5]
提交点也被用来支持索引的版本控制
- Lucene可以保存多个提交点,每个提交点代表了索引在不同时间点的状态
- 这样,就可以回滚到旧的提交点,也就是说,可以恢复到旧的索引状态
# 2)ES写入流程
1)写入内存
数据先写入内存buffer,并同时写入translog(事务日志)
数据写入内存buffer后不可搜索,直到刷新到磁盘
2)写入磁盘
- refresh(写入到os cache)
- 当
buffer满或定时
,数据刷新到OS缓存
,变为可搜索
- 当
- 写入磁盘
定时
将数据写入新的segment文件,数据刷新至磁盘
- 并且数据在
translog日志文件里面持久化到磁盘了一份
此时
就可以让这个segment file的数据对外提供搜索
了
- refresh(写入到os cache)
3)commit操作
重复1~2步骤
新的数据不断进入buffer和translog
,不断将buffer数据写入一个又一个新的segment file中去
每次refresh完,buffer就会被清空,同时translog保留一份日志数据
随着这个过程推进,translog文件会不断变大
当translog文件达到一定程度时,就会执行commit操作
commit操作发生第一步,就是
将buffer中现有数据refresh到os cache中去,清空buffer
4)commit完成
将一个
commit point 写入磁盘文件
,里面标识着这个 commit point 对应的所有 segment file
同时强行将
os cache 中目前所有的数据都 fsync 到磁盘文件中去
将
现有的translog清空
,然后再次重启启用一个translog,此时commit操作完成
translog日志文件的作用是什么?
在你执行commit操作之前,数据要么是停留在buffer中,要么是停留在os cache中
无论是buffer还是os cache都是内存,一旦这台机器死了,内存中的数据就全丢了
因此需要
将数据对应的操作写入一个专门的日志文件,也就是translog日志文件
一旦此时机器宕机,再次重启的时候,es会
自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去
综上可以看出
- es是准实时的,因此数据写入1秒后才可以搜索到
- 如果translog是异步写入的话,es可能会丢失数据
# 3)ES名称解释
translog
写入ES的数据首先会被写入translog文件,该文件持久化到磁盘
保证服务器宕机的时候数据不会丢失,由于顺序写磁盘,速度也会很快
同步写入
- 每次写入请求执行的时候,
translog在fsync到磁盘之后,才会给客户端返回成功
- 每次写入请求执行的时候,
异步写入
- 写入请求缓存在内存中,
每经过固定时间之后才会fsync到磁盘,写入量很大
- 对于数据的完整性要求又不是非常严格的情况下,可以开启异步写入
- 写入请求缓存在内存中,
refresh
- 经过固定的时间,或者手动触发之后,将
内存中的数据构建索引生成segment,写入文件系统缓冲区
- 经过固定的时间,或者手动触发之后,将
commit/flush
- 定期触发,将内存数据刷新到磁盘,清理translog,生成新的提交点
merge
合并多个小segment成一个大segment,优化存储并减少查询延迟
merge的过程大致描述如下
磁盘上两个小segment:A和B,内存中又生成了一个小segment:C
A,B被读取到内存中,与内存中的C进行merge,生成了新的更大的segment:D
触发commit操作,D被fsync到磁盘
创建新的提交点,删除A和B,新增D
# 4、文档更新与删除
删除
段是不可改变的,所以既
不能从把文档从旧的段中移除
,也不能修改旧的段来进行反映文档的更新磁盘上的
每个segment都有一个.del文件与它相关联
当发送删除请求时,该文档未被真正删除,
而是在.del文件中标记为已删除
此文档可能仍然能被搜索到,但会
从结果中过滤掉
当segment合并时,在.del文件中标记为已删除的文档不会被包括在新的segment中
也就是说merge的时候会真正删除被删除的文档
更新
创建新文档时
,Elasticsearch将为该文档分配一个版本号
- 对文档的每次更改都会产生一个新的版本号
- 当执行更新时,旧版本在.del文件中被标记为已删除,并且新版本在新的segment中写入索引
- 旧版本可能仍然与搜索查询匹配,但是从结果中将其过滤掉
# 5、并发控制
# 1)ES乐观锁
- Elasticsearch主要使用乐观并发控制,每个文档都有一个版本号,当文档被修改时,版本号会自动递增
- 我们可以在更新文档时指定版本号,如果指定的版本号和服务器上的版本号不匹配,那么更新操作就会失败
- 这种机制相当于实现了乐观锁,假设冲突不会经常发生,只在发生冲突时才处理
# 2)ES悲观锁(无)
- Elasticsearch的设计是无锁的,它并不支持传统意义上的悲观锁
- 也就是说,它不会在数据被访问时就加锁
- 但是,通过使用特定的API和字段(如
_seq_no
和_primary_term
) - 可以实现类似悲观锁的效果,这需要在应用层面进行处理
# 6、批量操作
bulk API 允许在单个步骤中进行多次 create 、 index 、 update 或 delete 请求
如果你需要索引一个数据流比如日志事件,它可以排队和索引数百或数千批次
bulk 请求不是原子的: 不能用它来实现事务控制
每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求
整个批量请求都需要由接收到请求的节点加载到内存中,因此该请求越大,其他请求所能获得的内存就越少
批量请求的大小有一个最佳值,大于这个值,性能将不再提升,甚至会下降
但是最佳值不是一个固定的值
它完全取决于硬件、文档的大小和复杂度、索引和搜索的负载的整体情况
# 7、ES数据类型
Elasticsearch 支持如下简单域类型
字符串:
string
整数 :
byte
,short
,integer
,long
浮点数:
float
,double
布尔型:
boolean
日期:
date
映射
为了能够将时间域视为时间,数字域视为数字,字符串域视为全文或精确值字符串
Elasticsearch 需要知道每个域中数据的类型,这个信息包含在映射中
索引中每个文档都有 类型 ,每种类型都有它自己的 映射 ,或者 模式定义
映射定义了类型中的域,每个域的数据类型,以及Elasticsearch如何处理这些域
映射也用于配置与类型有关的元数据
当你索引一个包含新域的文档(之前未曾出现),Elasticsearch 会使用 动态映射
通过JSON中基本数据类型,尝试猜测域类型
使用如下规则
# 02.ES新增Doc过程介绍
# 1、ES增加Doc过程(新增一条数据)
- 插入数据 → 存入内存
buffer
,记录到translog
,尚不可搜索- 刷新(refresh) → 数据从
buffer
刷新到文件系统缓存(OS cache),数据可搜索,但尚未持久化到磁盘- 持久化(flush) → 刷新到磁盘,生成新的段并创建新的提交点
- translog 作用 → 保证在系统宕机时,数据不丢失,可以从
translog
中恢复
1)插入数据到内存
首先,文档(
Doc
)会被写入到 内存中的 buffer(通常是一个内存缓存同时,这个写入操作会记录到 translog(事务日志),用于确保数据的持久性,即便系统宕机,数据也不会丢失
2)刷新至系统缓存
刷新操作(refresh)
- 当
buffer
达到一定的大小或在经过一定的时间间隔(通常是 1 秒)后,Elasticsearch 会触发 刷新 操作 - 这时,数据从内存
buffer
被 刷新到文件系统缓存(OS cache) 中,但尚未持久化到磁盘 - 此时,数据已经可搜索,但并没有完全写入磁盘
- 当
新段(Segment)
- 刷新操作会创建新的 段(Segment),这个段会存储刷新后的数据并被放入文件系统缓存中
- 数据在文件系统缓存中的存在使得它能够被搜索到,尽管它尚未写入磁盘
搜索可见性(实时搜索)
由于数据已经刷新到文件系统缓存中(即 OS cache),在 Elasticsearch 中,这些数据就变得 可搜索 了
即使数据尚未持久化到磁盘,只要它在系统缓存中,就可以被查询到。
这是 Elasticsearch 被称为 近实时搜索的关键原因,意味着插入的数据在几乎实时的情况下即可被搜索
3)数据持久化到磁盘(flush)
Flush 操作:
- 当数据刷新到系统缓存后,Elasticsearch 会在后台周期性地执行 flush 操作,将 文件系统缓存 中的 段(Segment) 持久化到磁盘
- 此时,数据被完全写入磁盘
commit 操作:
- 在
flush
时,会创建一个新的 提交点(commit point),该提交点记录了当前所有段的状态 - 此时,文件系统中的段成为正式的、持久化的索引数据
- 在
4)事务日志(translog)与数据持久性
translog
用于记录所有未刷新到磁盘的数据修改,确保在系统崩溃时可以恢复数据默认情况下,
translog
是 异步写入 的:Elasticsearch 在内存中缓存translog
数据,周期性地将其刷写到磁盘在某些配置下,
translog
可以进行 同步写入,即每次操作都会等到translog
被 fsync 到磁盘后才返回成功
避免数据丢失
为了防止在两次
flush
操作之间出现数据丢失,Elasticsearch 会保证每个操作(增、删、改)会被记录到
translog
中。translog
会在每次refresh
后被 fsync 到磁盘,以确保即便系统宕机,数据仍能恢复。
# 2、ES删除Doc过程(删除、合并、更新)
1)ES Doc删除
删除一个ES文档
不会立即从磁盘中删除,它只是被标记成已删除
- 因为段是不可变的,所以文档既不能从久文档中移除,旧的段也不能更新以反映文档的版本
2)ES Doc的合并
- 通过每秒自动刷新创建新的段,很快的数量就越来越多,每个段消费大量资源
- 每次搜索请求都需要依次检查每个段,段越多查询越慢
ES利用段合并在后台选择一些小的段合并成大的段
- 合并后新的段可以被搜索,就的段被删除
3)ES Doc的更新
- 当一个
文档被更新,旧版的文档被标记为删除,新版本的文档在新的段中索引
- 该
文档的不同版本都会匹配一个查询,但是较旧的版本会从结果中删除
- 当一个