分段
分段是Elasticsearch中的一个重要概念,其核心目的是更高效地执行搜索和更新操作。在Elasticsearch中,段是由一些倒排索引和一些元数据组成的。元数据包括与该段相关的信息,例如段的大小、创建日期和所包含的文档数量。分段是将索引分成多个小段的过程,每个分段包含一部分索引数据。查看索引中分段信息的方法如下。
GET products/_segments
索引、分片、分段的关系如图:
事务日志文件
为了防止Elasticsearch集群宕机造成数据丢失,为了保证可靠存储,一个文档被索引之后,Elasticsearch就会将其添加到内存缓冲区,并且同时写入事务日志文件(translog)中。
translog的作用是保证数据的可靠性和一致性。当Elasticsearch节点出现故障或崩溃时,translog可以用来恢复数据。在节点重新启动后,Elasticsearch会重新读取translog中的所有操作并将其应用到Lucene索引中,从而使数据保持一致。
倒排索引是不可变的
已写入磁盘的倒排索引永远不会改变。使用倒排索引的好处是无须锁定,不用担心多进程操作更改数据导致数据不一致问题。使用倒排索引的坏处是更新了词典词库后,老的索引不能生效,如果要使其可搜索,则必须重建整个索引或者借助reindex操作迁移索引。这也是经常被问到的一点。
段是不可变的
单个文档写入操作对应Index请求,批量写入操作对应Bulk请求。Index和Bulk这两类请求是相同的处理逻辑,会将请求统一封装到BulkRequest中。写入原理图如图所示。
写入过程拆解如下。
1)客户端向主节点1发送写数据请求。此时主节点1充当协调节点的角色。
2)主节点1使用文档的ID确定文档属于分片0。请求会被转发到节点3,因为分片0的主分片目前被分配在节点3上。使用路由算法来通过文档的ID确定文档所属分片。路由算法计算公式如下。
shard=hash(routing)%number_of_primary_shards
其中,routing表示文档ID,number_of_primary_shards表示主分片个数,shard表示文档ID所属分片号。
3)节点3在主分片上面执行写入操作。如果写入成功了,它将请求并行转发到主节点1和节点2的副本分片上。一旦所有的副本分片都报告成功,节点3将向协调节点报告写入成功,则协调节点会向客户端报告写入成功。
写入过程的注意点如下。
❑写操作必须在主分片执行成功后,才能复制相关的副本分片。
❑主分片写入失败,则整个请求会被认为是写失败的。
❑如果有部分副本写失败(前提是主分片写入成功),则整个请求会被认为是写成功的。
❑如果设置了副本,则数据会先写入主分片,主分片再同步到副本分片,同步操作会加重磁盘IO负担,间接影响写入性能。
在Elasticsearch中,可以通过在执行写入操作时设置write_consistency参数来配置写入一致性级别。
默认情况下,write_consistency参数的取值为"quorum",表示要求主副本和大多数副本(至少一半加1)成功写入才返回成功的响应。如果设置为"all",则表示要求主副本和所有副本都成功写入才返回成功的响应。如果设置为"one",则表示仅要求主副本成功写入即可返回成功的响应。
在执行写入操作时,可以将write_consistency参数作为一个选项传递给相关的API。例如,在使用Index API创建文档时,可以使用以下方式指定write_consistency参数:
POST /my_index/_doc?write_consistency=all
{
"field1": "value1",
"field2": "value2"
}
可以通过修改索引的设置,在索引级别上配置全局的write_consistency参数。例如,使用Update Index Settings API来更新索引的设置:
PUT /my_index/_settings
{
"index": {
"write": {
"consistency": "all"
}
}
}
Elasticsearch refresh和flush操作
refresh操作
将文档插入Elasticsearch时,文档会被写入内存缓冲区中,然后通过refresh(刷新)操作定期从该缓冲区刷新到内存段中。刷新频率由refresh_interval参数控制,默认1s刷新一次,所以说Elasticsearch是近实时的搜索引擎,而不是准实时。也就是说,新插入的文档在刷新到段(内存中)之前是不能被搜索到的,如图所示。
flush操作
新创建的文档会先进入内存缓冲区,与此同时会将操作记录在事务日志之中。当发生刷新时事务日志中的操作记录并不会被清除,而是在数据从文件系统缓存写入磁盘之后才会清空。从文件系统缓存写入磁盘的过程就是flush(该操作也翻译为“刷新”,为了避免和refresh混淆,这里保留了flush操作的写法,可理解为“持久化并清空事务日志”)。
flush操作的实现如下。
POST /my-index-000001/_flush
总结一下,当新的文档写入后,写入索引缓冲区(index buffer)的同时会写入事务日志。refresh操作使得写入文档搜索可见;flush操作将文件系统缓存(filesystem cache)写入磁盘,以达到持久化的目的。