ES生成倒排索引和搜索数据机制
ES中,所有的文档在存储之前都要首先进行分析。用户可根据需要定义如何将文本分割成token、哪些token应该被过滤掉,以及哪些文本需要进行额外处理等等。
下面给出一个生成倒排索引的例子。
- 索引名:article
- 字段名:title
- 搜索内容:Select * from article where title like ‘%搜索引擎%’
【文档】
文档ID | 文档内容 |
---|---|
1 | ES是最流行的搜索引擎 |
2 | Java是世界上最流行的语言 |
3 | 搜索引擎是如何诞生的 |
【文档 -> 单词词典】
单词词典:词词典是由文档集合中出现过的所有单词构成的字符串集合。单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。
单词 | 文档ID列表 |
---|---|
ES | 1 |
最流行 | 1,2 |
搜索引擎 | 1,3 |
Java | 2 |
世界 | 2 |
语言 | 2 |
如何 | 3 |
诞生 | 3 |
【单词词典 -> PostingList】
PostingList:倒排列表记录了单词对应的文档信息集合。每条记录称为一个倒排项。倒排项包括:文档id、单词频率(TF,Term Frequency)、位置(Postion)、偏移(Offset )。
DocId | TF | Postion | Offset |
---|---|---|---|
1 | 1 | 2 | <7,11> |
3 | 1 | 0 | <0,4> |
所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件被称之为倒排文件。倒排文件是存储倒排索引的物理文件。
ES节点结构
分片(Shard)
ES的分片机制可将一个索引内部的数据分布地存储于多个节点。
Shard有两种类型:primary和replica,即主shard及副本shard。
- Primary shard用于文档存储,每个新的索引会自动创建5个Primary shard,此数量可在索引创建之前通过配置自行定义,不过,一旦创建完成,其Primary shard的数量将不可更改。
- Replica shard是Primary Shard的副本,用于备份数据及提高搜索性能。
每个Primary shard默认配置了一个Replica shard,但也可以配置多个,且其数量可动态更改。ES会根据需要自动增加或减少这些Replica shard的数量。
ES集群可由多个节点组成,各Shard分布式地存储于这些节点上。ES可自动在节点间按需要移动shard,例如增加节点或节点故障时。
片段( segment )
ElasticSearch中的每个分片包含多个segment,每一个segment都是一个倒排索引;在查询的时,会把所有的segment查询结果汇总归并后最为最终的分片查询结果返回。
- 每个分片包含多个“分段”。每个分段是一个倒排索引。
- 默认每秒都会生成一个segment文件。
- 分段内的doc数量上限是2的31次方。
ES的CRUD
(1)ES写数据过程
- 客户端向Node1 发送索引写文档请求。
- Node1 根据文档ID(_id字段)计算出该文档应该属于shard0,然后请求路由到Node3的P0分片上。
- Node3在P0上执行了请求。如果请求成功,则将请求并行的路由至Node1,Node2的R0上。
- 当所有的Replicas报告成功后,Node3向请求的Node(Node1)发送成功报告,Node1再报告至Client。
- 当客户端收到执行成功后,操作已经在Primary shard和所有的replica shards上执行成功了。
写请求分片ID计算方法:shard = hash(routing) % number_of_primary_shards
routing默认是文档ID,可自定义。由于上面的路由规则,主分片数量不可变
ES写数据底层原理:
- 先写入内存 buffer,在 buffer 里的时候数据是搜索不到的。同时将数据写入 translog 日志文件。
- 重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 buffer 数据写入一个又一个新的 segment file 中去。每次 refresh 完 buffer 清空,translog 保留。
- 随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 commit 操作。
- buffer 快满了,或者到一定时间(默认是每隔 1 秒钟),就会将 buffer 中现有数据 refresh 到 os cache 中去,清空 buffer。
- 将一个 commit point写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file。将一个 commit point写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file。
- 最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。
- 默认 30 分钟自动执行一次 flush,但如果 translog 过大,也会触发 flush。
translog 日志文件的作用是什么?
- 保证在 cache中的数据不会因为elasticsearch重启或者宕机的时候丢失。
- 当系统重启时会从translog中恢复之前记录的操作。
(2)ES查询过程
读操作步骤(根据文档id查询):
- 客户端发送Get请求到NODE1。
- NODE1使用文档的_id决定文档属于shard0。shard0的所有分片位于所有3个节点上。这次,它将请求路由至NODE2。
- NODE2将文档返回给NODE1,NODE1将文档返回给客户端。
ES读数据底层原理:
- 客户端发送请求至对应文档的shard。
- 先在filesystem cache中进行获取。
- 如果不存在,再到对应segment的磁盘文件中寻找。
- 找到之后,数据缓存进filesystem cache。
- 通过shard返回给客户端。
- 对于读请求,请求节点(NODE1)将在每次请求到来时都选择一个不同的replica shard来达到负载均衡。使用轮询策略轮询所有的replica shards。
- 读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。
- 写请求是写入 primary shard,然后同步给所有的 replica shard。
(3)ES更新过程
- 客户端发送更新操作请求至NODE1。
- NODE1将请求路由至P0所在的NODE3。必须是Primary shard所在的位置。
- NODE3从P0读取文档,改变source字段的JSON内容,然后试图重新对修改后的数据在P0做索引。
- 如果NODE3成功更新了文档,它将并行的将新版本的文档同步到NODE1和NODE2的replica shards重新建立索引。
- 一旦所有的replica shards报告成功,NODE3向被请求的节点(NODE1)返回成功,然后NODE1向客户端返回成功。
如果此时这个文档已经被其他的进程修改了,那么它将重新执行3步骤,这个过程如果超过了retryon_conflict设置的次数,就放弃。
ES索引字段主要属性配置
index属性
- analyzed:分词后索引,可搜索。
- not_analyzed:字段值不分词,以单个关键词进行索引。
- no:字段不索引,当然也就不能搜索。
store属性
属性store默认false,当某个数据字段很大,我们可以指定其它字段store为true,这样就不用从_source中取数据。
_source 是源文档,当你索引数据的时候, elasticsearch 会保存一份源文档到 _source ,如果文档的某一字段设置了 store 为 true,这时候会在 _source 存储之外再为这个字段独立进行存储。
这么做的目的主要是针对内容比较多的字段,放到 _source 返回的话,因为_source 是把所有字段保存为一份文档,命中后读取只需要一次IO,包含内容特别多的字段会很占带宽影响性能,通常我们也不需要完整的内容返回(可能只关心摘要),这时候就没必要放到 _source 里一起返回了(当然也可以在查询时指定返回字段)。
网友评论