主要内容
o一 简介
o二 基本概念介绍
o三 底层实现原理&集群搭建(干货)
o四 对搜索系统的优化&问题解决方式(干货)
o五 参考资料
二 基本概念介绍
2.1 集群、分片、节点概念介绍
2.2 索引、类型、文档概念介绍
2.3 数据的写入过程
2.4 写入路由优化(重点)
2.5 分布式查询
2.6 深度翻页问题(重点)
2.1 集群、分片、节点概念介绍
l 集群:多台Es服务器的结合的统称叫ES集群。
l 节点:一个节点是你集群中的一台服务器,作为集群的一部分,它存储你的数据,参与集群的索引和搜索功能。
分片:将数据切分,分布的放在每个分片中,分片又被放到集群中的节点上。每个分片都是独立的lucene实例。
每个分片是独立索引的概念是,即使某个分片坏了,也不影响其他分片的查询。这样做的好处有点两点。
1、横向扩展,水平分割数据容量。 2、可以在分片上并行的进行操作。
另外:分片数量在创立索引的时候就确定了,一旦创建分片的数量就不能再更改,所以需要在一开始就预估数据规模,设置合理分片数量。数据量过大,分片会无法落到磁盘中(datacenter案例)
另外一种解决方式:自动拆分索引、或者重建索引。
分片:分为主分片和备份分片,副本分片数提高了数据冗余量,主分片挂掉以后能够自动由副本分片升为主分片,实现业务方无感知自我修复。备份分片还能够降低主分片的查询压力。但是也会消耗更多的系统性能。
分片数量合理设置:跟业务的实际使用场景有关,千万级别以下默认五个分片就足够。亿级需要压测,直至业务方认为查询延迟已经无法接受,此时认为分片数量已经不够。一个经验值就是每个分片不要超过30G,分片大小已经达到性能瓶颈,就需要考虑拆分索引。
l索引: 一个索引就是一个拥有几分相似特征的文档的集合(相当于一个数据库)。
l类型: 在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定(相当于数据库中的一个表)。
l文档:是一个可被索引的基础信息单元。文档以JSON格式来表示(一条数据)。
Es6.x版本已经移除type的概念,一个索引只对应一个type,思考下为什么?因为前面说了,分片数量一旦设置,就不能修改,只能通过重建的形式。如果某个type发现分片没有设置合理,重建索引,就会影响到其他type.
但考细粒度的索引维护和索引重建.看下右边的图,found为true代表文本找到了,_version需要着重注意一下,是es自带的版本号,用户每当对一条数据更新一次,那版本号加1,如果用户带着版本号来更新一条数据,那么es就抛出version conflict异常,可以利用这个特点来进行并发控制。
source就是源文本,soucrce设置为false可以降低存储空间,也就是说这些字段并不是全部都要显示,只需要显示一部分。比如弹幕数据 如果所有的弹幕每个字段都要存储,那么集群的磁盘占用空间就会急剧增加,因为索引更重要在于检索,所以只对id进行了store,检索出id后,再从mongo里拉取实际的数据显示给用户。
2.3 数据写入索引过程
(1)客户端向 Node 1 (协调节点)发送新建、索引或者删除请求。
(2)node1节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3
(3)Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。
(4)一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功
(5)协调节点向客户端报告成功。
2.4 写入路由优化(重点)
(1)默认的路由规则:数据随机打散将数据路由到不同的分片上,优点是避免了写入热点,但搜索时,需要在所有分片上执行,最后汇总。该方式查询效率较低。【适用于写多,查询少】
(2)指定路由规则:查询数据时也按路由规则查询,避免请求转发至无用的分片,提升了查询效率。缺点:容易出现写入热点,查询与具体业务耦合,需要根据实际场景优化。【适用于具体业务查询优化】
2.5 分布式检索
2.5.1 基本概念
优先队列:仅仅是一个存有 top-n 匹配文档的有序列表。优先队列的大小取决于分页参数 from 和 size 。例如,如下搜索请求将需要足够大的优先队列来放入100条文档:
GET /_search{"from": 90,"size": 10}
2.5.2数据检索过程
在es中分布式检索分为两个阶段:查询阶段和取回阶段。
查询阶段:
1.客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
2.Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
3.每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
取回阶段:
1.协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
2.每个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。
3.一旦所有的文档都被取回了,协调节点返回结果给客户端。
2.6 深度翻页问题
举例
以2.5节为例,用户只需要获取第90到100之间的10条数据,实际执行步骤为:
1.该索引有两个分片,对两个分片分别构造size=from+size=90+10=100的优先队列,协处理节点对两个优先队列进行排序汇总,相当于对200条数据进行排序汇总。
2.根据汇总的结果,只取出10条数据,向持有这10条数据的分片发起查询请求,将查询结果汇总返回。
现在假设我们请求第 1000 页--结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
结论:
协调节点需要根据 number_of_shards * (from + size) 排序文档,来找到被包含在 size 里的文档。可以看出,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 es默认限制对任何查询都不要返回超过 10000 个结果的原因。ES针对大规模数据查询需求,提供了滚动查询接口。
三 底层实现原理&集群搭建
3.1 文档索引的过程
3.2 底层数据结构(FST、倒排索引、bitMap)
3.3 机器选择&集群搭建
1.Es在公司搜索页面suggest功能展示
2.原文本到索引词的转化过程
3.1 文档索引的过程
分词器:一个分词由词元分解器和词元过滤器组成,用户可以根据自己的需要,通过模板来配置。也可以直接在git上下载使用通用的分词器如:ik、pinyin、mmseg等。
3.2 ES底层数据结构(倒排索引)
倒排索引:Elasticsearch 使用一种称为 倒排索引 的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。
3.2 ES底层数据结构(FST)
FST :有穷状态转换器。其功能类似于字典树(Trie)的功能,通过输入字符串构建最小有向无环图。当Term dictionary数据量较大时,其检索速度较慢,Term index使用FST作为数据结构,存储着与Term Dictionary的block之间的映射关系,可以使term index缓存到内存中。从term index查到对应的term dictionary的block位置之后,再去磁盘上找term,大大减少了磁盘随机读的次数。
HashMap、TreeMap与FST在100万数据下性能测试结果
从hashMap和FST对比来看,FST要比HashMap各方面的性能表现都要差一些,但是lucene为什么要选择FST作为其字典的存储结构呢?
因为FST最大的作用是节省存储空间,实际占用的存储空间大约是HashMap的1/10左右。对于存储在内存的数据来说,节省存储空间是至关重要的。
对于一个合格的词典结构来说,度量标准有以下几点。
目前各种数据库中比较常使用的数据结构就是B树、跳跃表、FST,对于数据结构来说没有最好的,而是针对于各种业务场景更适合的。
MySQL InnoDB索引 使用的数据结构是B+树,B+树是一种多路平衡查找树,查找复杂度是log(N),优点是外存索引,更新方便,快但是存储空间大,查询速度相对较慢。MySQL 这种关系型数据库,对实时性要求特别高,要求写入后能够立马检索到,因此采用B+树更为合适。
跳跃表:结构简单,级数可控,但是对模糊查询支持不太好,lucene3.x以下使用。
FST是es当前使用的数据结构,共享前缀支持模糊查询,但是其构造过程要求输入有序,索引过程较慢。对于es来说,适用的场景并不要求写入后能够立马能查询到,而是在满足业务需求的一定时间范围内能查询到,而对数据的查询速度有更高的要求,因此使用FST作为数据结构。
在我看来,es在性能和对硬件要求之间找到了一个均衡点,首先其在内存中使用FST作为存储结构,降低了内存消耗,但是其查询性能和hashMap也差不了太多,查询复杂度均为O(n),其次es将内存和磁盘结合,将term Index存储在内存中,将term dictionary存储在磁盘中,先在内存中快速定位term dictionary的位置,再term dictionary中利用二分搜索和倒排索引快速查找到term所对应的文档,提升了查询速度。
0.9版本基于Lucene 4.x版本,而5.1.1版本依赖于lucence6版本
针对数值类型数据提出了新的数据结构Block K-D trees,改变了其搜索和索引的方式,核心思想是将数字类型编码成定长的字节数组,对定长的字节数组内容进行编码排序,然后来构建二叉树,然后依次递归构建,目前底层支持8个维度和最多每个维度16个字节,基本满足大部分场景。
启动doc_vlaues是一种列式存储结构:es排序、聚合时需要占用大量的内存空间,启动doc_values后,使用硬盘而非内存存储fielddata,在索引时构建,降低了对内存的使用,进而降低了GC压力。
4.3ES使用优化
3.更改查询和写入逻辑:节点除了管理集群,还存储着元数据信息(包含索引id和别名之间的映射关系),之前搜索系统写入数据时,每次使用别名获取indexId,然后使用indexId写入,使得master节点写入高峰时负载较高。在大集群中master节点挂掉后需要长达十几分钟选举出新节点,期间对任何请求请求不做响应。修改后,写入数据时不再获取元数据信息,提高写入、查询性能,使得es Master节点cpu平均负载从60%左右降低到5%以下,提高系统稳定性,提升写入和查询性能。
五 参考资料
1.es中文官网:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
2.es优化建议:http://www.jianshu.com/p/29ffce0850af
3.入门材料:http://blog.csdn.net/leixingbang1989/article/details/75268293
5.elasticsearch中文社区公共号(推送es最新动态):elastic-cn
6.elasticsearch研究会:Lucene_Elasticsearch