这篇文章我们探索Elasticsearch的写入流程,Elasticsearch的写入跟数据库的写入是完全不同的。
数据库中的写入只是单纯的写入行,Elasticsearch中的写入则是建立索引文件,可以理解成数据即索引。下面对比分析Elasticsearch与NoSQL数据库区别。
Elasticsearch并不支持事务。
实时性
Elasticsearch的主要应用场景就是实时,但Elasticsearch本身并非实时而是near-real-time(近实时)。Elasticsearch默认写入的数据会在1秒后,才可以被检索到。
NoSQL数据库则可以做到real-time(实时),一般情况下写入成功后是马上可以被查询到的。Elasticsearch中的Get请求也能保证是实时的,因为Get请求会直接读内存中尚未Flush到磁盘的TransLog。但是Get请求只支持通过doc_id进行查询,所以对于条件查询依然无法实现实时。
关于Get和Search请求:ElasticSearch原理(四):查询流程
可靠性
一种情况是我们从一个原始数据源,比如数据库,导入数据到Elasticsearch。这种情况对Elasticsearch的可靠性要求可能就没那么高,主要的功能还是搜索。因为原始数据存储是一个数据保障,即使Elasticsearch发生数据丢失,重新导入就行了。但有一种场景就是数据直接写Elasticsearch的,并且不允许丢失,这个时候对Elasticsearch的可靠性要求就很高。
关于Elasticsearch可靠性:ElasticSearch干货(五):Elasticsearch如何保证数据不丢失?
NoSQL数据库本身作为一款数据库,数据库对于数据可靠性一般都非常的高,但这也是数据库性能瓶颈的所在。如何使用Elasticsearch来中和两者,既保证数据可靠有保证性能就尤为重要。
众所周知,Elasticsearch是基于Lucene实现的,并且在Lucene的基础上做了功能上的修改和优化。下面我们开始探讨Lucene的写入和Elasticsearch的写入有什么区别。
Lucene的写入
Lucene的写入主要是index、update、和delete。因为segment是不支持修改的,所以update和delete其实都是写入。
Lucene中写操作主要是通过IndexWriter类实现,IndexWriter提供三个接口:
public long addDocument();
public long updateDocuments();
public long deleteDocuments();
只要Doc通过IndesWriter写入后,后面就可以通过IndexSearcher搜索了。但也存在几点缺陷:
- Lucene并不是分布式
- Lucene只有在数据生成索引文件(segment)之后,才会被查询到,做不到实时
- 如何保证segment的可靠性
- segment不支持更新,一旦创建不能修改,所以不支持针对分档进行部分更新
Elasticsearch的写入
首先针对上述提到的Lucene的缺陷一一介绍Elasticsearch是怎么弥补优化的。
Elasticsearch为了实现分布式,引入了shard,将segment分布在不同的机器上。并且根据_routing来决定如何分配到指定的shard。
Elasticsearch中每个index由多个shard组成,默认是5个,每个shard分布在不同的机器上。shard分为主分片和副本分片,在文档写入时,会根据_routing来计算(OperationRouting类)得出文档要写入哪个分片。这里的写入请求只会写主分片,当主分片写入成功后,会同时把写入请求发送给所有的副本分片,当副本分片写入成功后,会传回返回信息给主分片,主分片得到所有副本分片的返回信息后,再返回给客户端。
在写入时,我们可以在Request自己指定_routing,也可以在Mapping指定文档中的Field值作为_routing。如果没有指定_routing,则会把_id作为_routing进行计算。由于写入时,具有相同_routing的文档一定会分配在同一个分片上,所以如果是自定义的_routing,在查询时,一定要指定_routing进行查询,否则是查询不到文档的。这并不是局限性,恰恰相反,指定_routing的查询,性能上会好很多,因为指定_routing意味着直接去存储数据的shard上搜索,而不会搜索所有shard。
从上述可以看出,Elasticsearch文档写入主要是写主分片和写副本分片。所以副本分片的个数就直接决定了写入的性能。合理