最近面试过程中遇到问Elasticsearch的问题不少,这次总结一下,然后顺便也了解一下Elasticsearch内部是一个什么样的结构,毕竟总不能就只了解个倒排索引吧。本文标题就是我遇到过的两个问题,所以此次基本上只是围绕着这两个问题来总结。
Clouster:集群,由一到N个Elasticsearch服务节点组成。
Node:节点,组成Elasticsearch集群的基本单元,单个集群内节点名称唯一。通常一个节点中分配一道多个分片。
Shards:分片,当ES的索引数据过大时,会进行水平拆分,拆分出来的每一个单元都称为分片。在进行写入数据的时候,会通过路由来确定具体写到哪个分片上,所以在创建索引的时候就要确定好分片数量,并且一旦确定不可更改。索引数据在经过分片后,在数据管理和性能上都有很大提升,并且每一个分片都是一个Lucende的索引,每个分片都必须有一个主分片和零到多个副分片。
Replicas:副本或备份,副本是指对主分片的备份分片,无论是主分片还是副本分片都可以对外提供查询服务。但是写入操作时是先写入主分片,然后再分发到副本上。
当主分片不可用时会在副本分片上选举一个作为主分片,因此副本不仅可以保证系统的高可用性,还可以提升搜索时的并发性能(主副分片都可以提供查询)。但并不是副本越多越好,副本数量过多会导致数据同步的负担过大。
分片数 (副本数+1)= 所需的最大节点数
举例:你计划5个分片和1个副本,那么所需要的最大的节点数为:5(1+1)=10个节点。
Index:索引,由一个和多个分片组成,单个集群内索引名字是唯一的。
Type:类型,指索引内部的逻辑分区,一般是通过Type的名字来进行分区,若是查询条件中没有该值,则说明在整个索引中执行查询。
Document:文档,ES索引中的每一条数据都称为一个Document,基本上和关系型数据库中的一个记录意思相同,通过_id,在Type内进行唯一标识。
Settings:对集群中索引的设定,例如默认的分片数量,副本数等信息。
Mapping:这里的Mapping类似于,关系型数据库的表结构信息,这里面包含了索引中字段的存储类型,分词方式,是否分词等信息。
Elasticsearch中的Mapping是可以动态识别的,Elasticsearch字段的数据格式识别它的类型,但是若是需要对Filed字段进行特殊设置时,就需要手动创建Mapping了。注意:一个Mapping一旦创建成功后,若是已经存储了数据了,就不可以修改了。
Analyzer:字段的分词方式的定义,一个Analyzer,通常由一个Tokenizer,零到多个Filter组成。例如默认的标准Analyzer包含一个标准的Analyzer和三个Filter(Standard Token Filter、Lower Case Token Filter、Stop Token Filter)。
还有就是集群节点有三个颜色状态:绿色、黄色、红色。
Elasticsearch写入数据到索引的过程大致是这样的:
shard = hash(routing)%number_of_primary_shards
),计算出数据应该落到那个shard中,根据coordinate节点上维护的shard信息,将请求发送到Node1上。在主分片上执行写入请求的过程如下:
refresh
到FileSystemCache
中,生成segment文件,一旦生成segment文件,就能通过索引查询到了。因为Elasticsearch的这个刷盘机制,也说明并非是一个实时的搜索引擎。
在早期的全文检索中为整个文档建立了很大的倒排索引,并将其写入到磁盘。如果索引有更新,就需要重新全量创建一个索引来替换原来的索引。
这种方式在数据量很大时效率很低,并且由于创建一次索引的成本很高,所以对数据的更新不能过于频繁,也就不能保证实效性。
在搜索中引入了段(segment)的概念(将一个索引文件拆分为多个子文件,则每个子文件叫做段),每个段都是一个独立的可被搜索的数据集,并且段具有不变性,一旦索引的数据被写入硬盘,就不可修改。
由于Elasticsearch中的每个分片包含多个segment(段),每一个segment都是一个倒排索引;因此在在查询的时,会把所有的segment查询结果汇总归并为最终的分片查询结果返回。
segment优点
读写都不用加锁,不直接更新数据,所以不用考虑多线程下读写不一致的问题。
长驻内存,由于segment的不可变性,这样只要空间足够大,数据都是直接存储在内存中的。因此查询数据时直接访问内存,不用频繁操作磁盘。
增量创建,分段可以做到增量创建索引,即轻量级的对索引进行改变,不用操作整个索引文件,这样在频繁更新数据时,使系统接近实时更新。
segment缺点
删除,对段数据进行删除时,旧数据在.del文件中并不会马上删除。而旧数据只有等到段合并时才会被删除,这样会造成大量的空间浪费。
更新,因为更新操作是有删除和新增组合而成,若是频繁的更新也会造成大量的空间浪费。
新增,由于每次新增数据都是新建一个段,当段的数量过多时,对服务器的资源的消耗会非常大,查询性能也会受到影响。
过滤,查询后的结果再汇总时需要对已删除的数据进行过滤,增加了系统的处理负担。
这里要注意一点,并不是每新增一条记录(Document)就创建一个段(segment)的,而是数据先落到Memory Buffer中后,批量刷到Segment中的。
由于Memory Buffer中的数据在很短的时间(1s)内不断的进行刷盘,这样就会造成短时间内段的数量暴增,当索引中的段数量太多时不仅会严重消耗服务器资源,还会影响检索性能。
所以必须进行定期段合并操作,小的段被合并到大的段,然后这些大的段再被合并到更大的段。
段合并的主要动作有两个:
还有一个逻辑是段合并的时候会将那些旧的已删除文档从文件系统中清除。
segment合并的好处
减少索引段的数量,提升的内存空间,从而提高了检索速度。
减少索引的容量(文档数)——段合并会移除被标记为已删除的那些文档。提高了全文检索的速度,并移除了旧版本的数据。
segment合并带来的问题
磁盘IO操作的代价;因为段合并操作是非常耗I/O的,因为需要从旧的索引段中读取数据然后合并到新的索引段。
查询性能有一定影响;虽然说索引段合并的操作是异步进行的,但由于合并操作非常耗I/O,若合并时,正好也在进行大量的查询操作,在那些I/O处理速度慢的系统中,系统性能会受到影响。
通过对上面索引的分段存储和索引段合并的介绍,已经可以清楚的知道,在更新索引数据的时候,其实都是在操作索引段,对一段的索引数据进程操作,这样就能实现快速更新索引数据了。
Elasticsearch在接收到写请求时,是先将数据写入到主分片的,然后再将写请求同步到各个副本分片,但是同步这些副本分片的时间是无序的。
这个时候,Elasticsearch 就会采用乐观并发控制(Optimistic Concurrency Control)来保证新版本的数据不会被旧版本的数据覆盖。
这个乐观并发控制,就类似于Java的CAS机制,就是比较交换的意思。
乐观并发控制(OCC)认为事务间的竞争并不激烈,所以任何事务来了就先执行,等到提交的时候再 检查一下数据有没有变化,若没有变化就直接提交,如果有变化就直接重试再提交。这种适用于写冲突比较少的场景。
写一致性
Elasticsearch 集群保证写一致性的方式是在写之前检查有多少分片可供写入,若符合条件则执行写操作,否则会进行等待,等待符合条件,默认等待时间1分钟。
满足写入分片的条件有如下三个配置:
Elasticsearch 集群保证读写一致性的方式是,将副本分片的同步方式设置为replication=Sync(默认值),指的是只有主分片和所有副本分片都写入成功后才返回请求结果。
这种方式,可以保证后面的读请求无论请求到那个分片,返回的数据都是最新的版本。
但是为了提升写的效率也可以通过设置replication=async,这种模式是指,只要写入主分片成功,就返回写请求结果,降低了数据一致性提升了写的效率。
参考:
掌握它才说明你真正懂 Elasticsearch
《深入理解Elasticsearch》
**作者:**纪莫
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/jimoer/p/15573952.html
内容来源于网络,如有侵权,请联系作者删除!