1. 写流程
Elasticsearch 是当前主流的搜索引擎,其具有扩展性好,查询速度快,查询结果近实时等优点,本文将对Elasticsearch的写操作进行分析。
1.1. lucene的写操作及其问题
Elasticsearch底层使用Lucene来实现doc的读写操作,Lucene通过
public long addDocument(...);
public long deleteDocuments(...);
public long updateDocument(...);
三个方法来实现文档的写入,更新和删除操作。但是存在如下问题
- 没有并发设计
lucene只是一个搜索引擎库,并没有涉及到分布式相关的设计,因此要想使用Lucene来处理海量数据,并利用分布式的能力,就必须在其之上进行分布式的相关设计。 - 非实时
将文件写入lucence后并不能立即被检索,需要等待lucene生成一个完整的segment才能被检索 - 数据存储不可靠
写入lucene的数据不会立即被持久化到磁盘,如果服务器宕机,那存储在内存中的数据将会丢失 - 不支持部分更新
lucene中提供的updateDocuments仅支持对文档的全量更新,对部分更新不支持
1.2. Elasticsearch的写入方案
针对Lucene的问题,ES做了如下设计
1.2.1. 分布式设计:
为了支持对海量数据的存储和查询,Elasticsearch引入分片的概念,一个索引被分成多个分片,每个分片可以有一个主分片和多个副本分片,每个分片副本都是一个具有完整功能的lucene实例。分片可以分配在不同的服务器上,同一个分片的不同副本不能分配在相同的服务器上。
在进行写操作时,ES会根据传入的_routing参数(或mapping中设置的_routing, 如果参数和设置中都没有则默认使用_id), 按照公式shard_num = hash(\routing) % num_primary_shards
,计算出文档要分配到的
分片,在从集群元数据中找出对应主分片的位置,将请求路由到该分片进行文档写操作。
1.2.2. 近实时性-refresh操作
当一个文档写入Lucene后是不能被立即查询到的,Elasticsearch提供了一个refresh操作,会定时地调用lucene的reopen(新版本为openIfChanged)为内存中新写入的数据生成一个新的segment,此时被处理的文档均可以被检索到。refresh操作的时间间隔由refresh_interval
参数控制,默认为1s, 当然还可以在写入请求中带上refresh表示写入后立即refresh,另外还可以调用refresh API显式refresh。
1.2.3. 数据存储可靠性
- 引入translog
当一个文档写入Lucence后是存储在内存中的,即使执行了refresh操作仍然是在文件系统缓存中,如果此时服务器宕机,那么这部分数据将会丢失。为此ES增加了translog, 当进行文档写操作时会先将文档写入Lucene,然后写入一份到translog,写入translog是落盘的(如果对可靠性要求不是很高,也可以设置异步落盘,可以提高性能,由配置index.translog.