一般来说,使用ES都是将其作为分布式搜索系统或者是分布式NoSQL数据库。
从这两个角度分别来说一下 ES 的写操作。
在分析一个分布式系统的写操作时,一般要考虑以下几个点:
ES 内部使用了 Lucene 来完成索引的创建以及搜索功能,Lucene 的写操作是通过一个叫做 IndexWriter 类来实现的,IndexWriter 提供三个接口:
public long addDocuments();
public long updateDocuments();
public long deleteDocuments();
通过这三个接口来完成单个文档的写入,更新,删除。只要 doc 通过 IndexWriter 类写入后,就可以通过 IndexSearcher 进行搜索查询了。
这里简单的功能已经实现了,但是没有解决一些问题:
上述问题在 lucene 里并没有解决,而在 ES 中引入了多重机制来解决这些问题。
ES 采用多 shard 的方式来解决分布式问题。将数据集通过配置的 routing 规则分成多个数据子集,每个数据子集都有子集独立的索引与检索功能。在写入一个新的数据时,根据规则将数据发送到指定的 shard 上创建索引等,这样就实现了分布式功能。
ES 架构也采用了一主多副的架构。每个index由多个Shard组成,在每个Shard中又有一个主节点以及多个副本节点,这个副本个数可以配置。每次写入新数据的时候,写入请求会根据routing规则选择发送给哪个shard,请求里可以配置以什么Field作为路由参数,如果没有配置则使用Mapping里的配置,如果Mapping也没有配置,那么使用_id作为路由参数,通过这个参数计算出哈希值,从而找到对应的shard上的主节点。
请求将会发送给主节点,在主节点执行成功后,从主节点在发送给其他的副本节点。当其他的副本节点执行完成返回给主节点后,写入请求成功,返回结果给客户端。
这种方法的优缺点显而易见,每次执行的延时最小也是两次写入的延时总和,写入效率较低。但是这种方法,能够尽可能的保证数据可靠性。
采用这种多副本的方式,避免了单机或者磁盘故障时,对已经持久化的数据造成损害。
ES为了减少磁盘IO保证读写性能,一般是每隔一段时间才会把Lucene里的Segement 进行持久化,对于没有写入内存,还没有Flush到磁盘的Lucene数据,如果发生宕机,如何保证不丢失呢?
ES借鉴数据库的处理方式,增加了TransLog模块。
在每一个shard中,写入流程分为两个部分,先写入lucene,再写入TransLog。
写入请求到达shard后,先写lucene文件,创建好索引后,此时索引还在内存里,接着去写TransLog,写完TransLog后,刷新TransLog数据到磁盘上,写磁盘成功后,请求返回给客户端。与数据库不同,ES先写内存再写TransLog,数据库则相反,原因可能是ES的写入Lucene操作过于复杂,很容易失败,为了避免TransLog中有大量无效的记录,减少回滚的复杂度以及提高速度,所以这样。
在这里,写入Lucene后,是无法被搜索到的。需要通过refresh把内存的数据转成完整的segment后,才可以被搜索到。这个refresh 的时间可以进行配置,默认是 1s,最快100ms。如果这时候使用GetById的方式来查询,可以直接在TransLog里查询,这时候是可以搜索到的。每隔一段时间,Lucene会把内存的中生成的新segment刷新到磁盘上,刷新后此时数据以及持久化了,则清空旧的TransLog。
这里是一个写入的逻辑图,在lucene调用write系统调用的时候,会将数据写入操作系统的内存缓冲区,调用refresh时,是将内存缓冲区内的数据,刷到文件系统的页cache里,此时在查询的时候,在lucene查询时,调用系统调用的read时,就会读到这张页,进而可以进行搜索查询。所以说在refresh之前,数据还在内存缓冲区内,此刻无法搜索读取。接着在指定的时间内(默认为30分钟),lucene会调用flush将文件系统内的页cache上的数据刷到硬盘上,此刻持久化完成,接着清理 TransLog 内的旧数据。
Lucene在写 TransLog 时,从TransLog里,可以进行flush到硬盘上,这个flush的时间间隔可以配置。
为什么不直接从Lucene 里 flush 到硬盘上还要调用refresh以及写入 TransLog呢?是因为 flush 会调用系统调用 fsync,这个 fsync 操作是将文件系统的脏页刷新到磁盘上,这个操作非常的耗时,频繁的调用不靠谱。所以最终采用refresh + TransLog 的方式来做到提升性能 + 保证可靠性。
Lucene里不支持部分字段的更新,于是ES自己实现了一套更新的逻辑,具体流程如下:
本文地址:https://blog.csdn.net/liuchenxia8/article/details/107213929
如对本文有疑问, 点击进行留言回复!!
PostgreSQL select for update指定列(兼容oracle)
SQL语句中的WHERE、聚合函数(SUM、MIN、MAX、AVG、COUNT)、HAVING
sql server登录名、服务器角色、数据库用户、数据库角色、架构区别联系
网友评论