elasticsearch设 计的理念就是分布式搜索引擎,底层其实还是基于lucene的。
- 核心思想就是在多台机器上启动多个es进程实例,组成了一个es集群。
- es中存储数据的基本单位是索引,比如说你现在要在es中存储一些订单数据,你就应该在es中创建一个索引,order_idx,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是mysql里的一张表。index(mysql表) -> type -> mapping(mysql建表语句) -> document(mysql中的行) -> field(mysql中的列)。
- index:mysql里的一张表
- type:没法跟mysql里去对比,一个index里可以有多个type,每个type的字段都是差不多的,但是有一些略微的差别。
- 好比说,有一个index,是订单index,里面专门是放订单数据的。就好比说你在mysql中建表,有些订单是实物商品的订单,就好比说一件衣服,一双鞋子;有些订单是虚拟商品的订单,就好比说游戏点卡,话费充值。就两种订单大部分字段是一样的,但是少部分字段可能有略微的一些差别。
- 所以就会在订单index里,建两个type,一个是实物商品订单type,一个是虚拟商品订单type,这两个type大部分字段是一样的,少部分字段是不一样的。
- 很多情况下,一个index里可能就一个type,但是确实如果说是一个index里有多个type的情况,你可以认为index是一个类别的表,具体的每个type代表了具体的一个mysql中的表
- 每个type有一个mapping,如果你认为一个type是一个具体的一个表,index代表了多个type的同属于的一个类型,mapping就是这个type的表结构定义,你在mysql中创建一个表,肯定是要定义表结构的,里面有哪些字段,每个字段是什么类型。
- mapping就代表了这个type的表结构的定义,定义了这个type中每个字段名称,字段是什么类型的,然后还有这个字段的各种配置。
- 实际上你往index里的一个type里面写的一条数据,叫做一条document,一条document就代表了mysql中某个表里的一行给,每个document有多个field,每个field就代表了这个document中的一个字段的值
- 接着你搞一个索引,这个索引可以拆分成多个shard,每个shard存储部分数据。
- 接着就是这个shard的数据实际是有多个备份,就是说每个shard都有一个primary shard,负责写入数据,但是还有几个replica shard。primary shard写入数据之后,会将数据同步到其他几个replica shard上去。
- 通过这个replica的方案,每个shard的数据都有多个备份,如果某个机器宕机了,没关系啊,还有别的数据副本在别的机器上呢。高可用了吧。
- es集群多个节点,会自动选举一个节点为master节点,这个master节点其实就是干一些管理的工作的,比如维护索引元数据拉,负责切换 primary shard 和 replica shard 身份之类的。
- 要是master节点宕机了,那么会重新选举一个节点为master节点。
- 如果是非master节点宕机了,那么会由master节点,让那个宕机节点上的primary shard的身份转移到其他机器上的replica shard。接着你要是修复了那个宕机机器,重启了之后,master节点会控制将缺失的replica shard分配过去,同步后续修改的数据之类的,让集群恢复正常。
其实上述就是elasticsearch作为一个分布式搜索引擎最基本的一个架构设计
4.3、lucence内部结构是什么?
面试官
:想了解你的知识面的广度和深度。
解答
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-66giyZEI-1668259821260)(https://user-gold-cdn.xitu.io/2019/1/22/16874bae6bc27a47?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)]
Lucene是有索引和搜索的两个过程,包含索引创建,索引,搜索三个要点。可以基于这个脉络展开一些。
4.4、es写入数据的工作原理是什么啊?es查询数据的工作原理是什么啊?
面试官心理分析
- 问这个,其实面试官就是要看看你了解不了解es的一些基本原理,因为用es无非就是写入数据,搜索数据。你要是不明白你发起一个写入和搜索请求的时候,es在干什么,那你真的就是gg了。
- 对es基本就是个黑盒,你还能干啥?你唯一能干的就是用es的api读写数据了。要是出点什么问题,你啥都不知道,那还能指望你什么呢? 是不是。
(1)es写数据过程
- 1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
- 2)coordinating node,对document进行路由,将请求转发给对应的node(有primary shard)
- 3)实际的node上的 primary shard 处理请求,然后将数据同步到replica node
- 4)coordinating node,如果发现primary node和所有replica node 都搞定之后,就返回响应结果给客户端
(2)es读数据过程
查询,GET某一条数据,写入了某个document,这个document会自动给你分配一个全局唯一的id,doc id,同时也是根据doc id进行hash路由到对应的primary shard上面去。也可以手动指定doc id,比如用订单id,用户id。
你可以通过doc id来查询,会根据doc id进行hash,判断出来当时把doc id分配到了哪个shard上面去,从那个shard去查询
- 1)客户端发送请求到任意一个node,成为 coordinate node
- 2)coordinate node 对 document 进行路由,将请求转发到对应的node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有replica中随机选择一个,让读请求负载均衡
- 3)接收请求的 node 返回 document 给 coordinate node
- 4)coordinate node 返回 document 给客户端
(3)es搜索数据过程
es最强大的是做全文检索,就是比如你有三条数据
java真好玩儿啊
java好难学啊
j2ee特别牛
你根据java关键词来搜索,将包含java的 document 给搜索出来
es就会给你返回:java真好玩儿啊,java好难学啊
- 1)客户端发送请求到一个 coordinate node
- 2)协调节点将搜索请求转发到所有的shard对应的 primary shard 或 replica shard 也可以
- 3)query phase:每个shard将自己的搜索结果(其实就是一些doc id),返回给协调节点,由协调节点进行数据的合并、排序、分页 等操作,产出最终结果
- 4)fetch phase:接着由协调节点,根据doc id去各个节点上拉取实际的document数据,最终返回给客户端
(4)搜索的底层原理,倒排索引,画图说明传统数据库和倒排索引的区别
(5)写数据底层原理
- 1)先写入buffer,在buffer里的时候数据是搜索不到的;同时将数据写入translog日志文件
- 2)如果buffer快满了,或者到一定时间,就会将buffer数据refresh到一个新的 segment file中,但是此时数据不是直接进入 segment file 的磁盘文件的,而是先进入os cache的。这个过程就是refresh。
- 每隔1秒钟,es将buffer中的数据写入一个新的segment file,每秒钟会产生一个新的磁盘文件,segment file,这个 segment file 中就存储最近1秒内 buffer 中写入的数据
- 但是如果buffer里面此时没有数据,那当然不会执行refresh操作咯,每秒创建换一个空的segment file,如果buffer里面有数据,默认1秒钟执行一次refresh操作,刷入一个新的 segment file 中
- 操作系统里面,磁盘文件其实都有一个东西,叫做os cache,操作系统缓存,就是说数据写入磁盘文件之前,会先进入os cache,先进入操作系统级别的一个内存缓存中去
- 只要buffer中的数据被refresh操作,刷入os cache中,就代表这个数据就可以被搜索到了
- 为什么叫es是准实时的?NRT,near real-time,准实时。默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。
- 可以通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。
- 只要数据被输入os cache中,buffer就会被清空了,因为不需要保留buffer了,数据在translog里面已经持久化到磁盘去一份了
- 3)只要数据进入os cache,此时就可以让这个segment file的数据对外提供搜索了
- 4)重复1~3步骤,新的数据不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完 buffer清空,translog保留。随着这个过程推进,translog会变得越来越大。当translog达到一定长度的时候,就会触发commit操作。
- buffer中的数据,倒是好,每隔1秒就被刷到os cache中去,然后这个buffer就被清空了。所以说这个buffer的数据始终是可以保持住不会填满es进程的内存的。
- 每次一条数据写入buffer,同时会写入一条日志到translog日志文件中去,所以这个translog日志文件是不断变大的,当translog日志文件大到一定程度的时候,就会执行 commit 操作。
- 5)commit操作发生第一步,就是将buffer中现有数据refresh到os cache中去,清空buffer;
- 6)将一个 commit point 写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file;
- 7)强行将os cache中目前所有的数据都fsync到磁盘文件中去
- translog日志文件的作用是什么?就是在你执行commit操作之前,数据要么是停留在buffer中,要么是停留在os cache中,无论是buffer还是os cache都是内存,一旦这台机器死了,内存中的数据就全丢了。
- 所以需要将数据对应的操作写入一个专门的日志文件,translog日志文件中,一旦此时机器宕机,再次重启的时候,es会自动读取 translog 日志文件中的数据,恢复到内存buffer和os cache中去。
- commit操作:
- 1、写commit point;
- 2、将os cache数据fsync强刷到磁盘上去;
- 3、清空translog日志文件
- 8)将现有的 translog 清空,然后再次重启启用一个 translog,此时commit操作完成。默认每隔30分钟会自动执行一次commit,但是如果 translog 过大,也会触发commit。整个commit的过程,叫做flush操作。我们可以手动执行flush操作,就是将所有os cache数据刷到磁盘文件中去。
- 不叫做commit操作,flush操作。es中的flush操作,就对应着commit的全过程。我们也可以通过es api,手动执行flush操作,手动将os cache中的数据fsync强刷到磁盘上去,记录一个commit point,清空translog日志文件。
- 9)translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
- 实际上你在这里,如果面试官没有问你es丢数据的问题,你可以在这里给面试官炫一把,你说,其实es第一是准实时的,数据写入1秒后可以搜索到;可能会丢失数据的,你的数据有5秒的数据,停留在buffer、translog os cache、segment file os cache中,有5秒的数据不在磁盘上,此时如果宕机,会导致5秒的数据丢失。
- 如果你希望一定不能丢失数据的话,你可以设置个参数,官方文档,百度一下。每次写入一条数据,都是写入buffer,同时写入磁盘上的translog,但是这会导致写性能、写入吞吐量会下降一个数量级。本来一秒钟可以写2000条,现在你一秒钟只能写200条,都有可能。
- 10)如果是删除操作,commit的时候会生成一个.del文件,里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc被删除了;
- 11)如果是更新操作,就是将原来的doc标识为deleted状态,然后新写入一条数据;
- 12)buffer每次refresh一次,就会产生一个segment file,所以默认情况下是1秒钟一个segment file,segment file会越来越多,此时会定期执行merge;
- 13)每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。
es里的写流程,有4个底层的核心概念,refresh、flush、translog、merge
当segment file多到一定程度时,es就会自动触发merge操作,将多个segment file给merge成一个segment file。
5、为什么比mysql快?
5.1、Elasticsearch 使用倒排索引结构来检索
- 采用倒排索引
5.2、Mysql使用b+tree来检索
- mysql 索引结构,采用b+tree实现,数据以主键聚集起来
1、Lucene的 Term index 和 Term Dictionary 其实对应的就是MySQL的B+Tree的功能,为关键字key提供索引。Lucene 多了一个term index(类似字典中的目录),所以快;
2、**Term index 在内存中是以FST(finite state transducers)的形式保存的,**其特点是非常节省内存,根据FST快速找到 Term Dictionary 所在相比MySQL的 B+Tree 需要读磁盘要快;
- PS:mysql buffer pool 会将索引信息缓存在内存,具体性能看命中率
3、Term dictionary在磁盘上是以分block的方式保存的,一个block内部利用公共前缀压缩,比如都是Ab开头的单词就可以把Ab省去。这样Term dictionary可以比B+tree更节约磁盘空间。
4、Lucene对不同的数据类型采用了不同的索引方式,比如 bkd tree , geo hash等。
5、在 mysql中给两个字段独立建立的索引无法联合起来使用,必须对联合查询的场景建立复合索引,而Lucene可以任何AND或者OR组合使用索引进行检索。既一个query可使用多个索引;
6、分布式
6、es在数据量很大的情况下(数十亿级别)如何提高查询性能啊? (8分)
面试官心理分析
- 问这个问题,是肯定的,说白了,就是看你有没有实际干过es,因为啥?es说白了其实性能并没有你想象中那么好的。很多时候数据量大了,特别是有几亿条数据的时候,可能你会懵逼的发现,跑个搜索怎么一下5秒 ~ 10秒,坑爹了。第一次搜索的时候,是5~10秒,后面反而就快了,可能就几百毫秒。
- 你就很懵,每个用户第一次访问都会比较慢,比较卡么?
所以你要是没玩儿过es,或者就是自己玩玩儿 demo,被问到这个问题容易懵逼,显示出你对es确实玩儿的不怎么样
说实话,es性能优化是没有什么银弹的,啥意思呢?就是不要期待着随手调一个参数,就可以万能的应对所有的性能慢的场景。也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是绝对不是所有场景都可以这样。
在这个海量数据的场景下,如何提升es搜索的性能,也是我们之前生产环境实践经验所得
6.1、主机
- 使用SSD
- 除进程外,至少保留一半内存给文件系统缓存(filesystem cache)
- os cache,操作系统的缓存,你往es里写的数据,实际上都写到磁盘文件里去了,磁盘文件里的数据,操作系统会自动将里面的数据缓存到os cache里面去;
- es的搜索引擎严重依赖于底层的filesystem cache,你如果给filesystem cache更多的内存,尽量让内存可以容纳所有的index segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。
- 性能差距可以有大,我们之前很多的测试和压测,如果走磁盘一般肯定上秒,搜索性能绝对是秒级别的,1秒,5秒,10秒。但是如果是走filesystem cache,是走纯内存的,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。
- 案例:之前有个学员,一直在问我,说他的搜索性能,聚合性能,倒排索引,正排索引,磁盘文件,十几秒
- 比如说,es节点有3台机器,每台机器,看起来内存很多,64G,总内存,64 * 3 = 192g
- 每台机器给es jvm heap是32G,那么剩下来留给filesystem cache的就是每台机器才32g,总共集群里给filesystem cache的就是32 * 3 = 96g内存
- 我就问他,ok,那么就是你往es集群里写入的数据有多少数据量?
- 如果你此时,你整个磁盘上索引数据文件,在3台机器上,一共占用了1T的磁盘容量,你的es数据量是1t,每台机器的数据量是300g,你觉得你的性能能好吗?filesystem cache 的内存才100g,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。
- 当时他们的情况就是这样子,es在测试,弄了3台机器,自己觉得还不错,64G内存的物理机。自以为可以容纳1T的数据量。
- 归根结底,你要让es性能要好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半
- 比如说,你一共要在es中存储1T的数据,那么你的多台机器留个 filesystem cache 的内存加起来综合,至少要到512G,至少半数的情况下,搜索是走内存的,性能一般可以到几秒钟,2秒,3秒,5秒
- 如果最佳的情况下,我们自己的生产环境实践经验,所以说我们当时的策略,是仅仅在es中就存少量的数据,就是你要用来搜索的那些索引,内存留给 filesystem cache 的,就100G,那么你就控制在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内
- 比如说你现在有一行数据 id name age …30个字段
- 但是你现在搜索,只需要根据id name age三个字段来搜索
- 如果你傻乎乎的往es里写入一行数据所有的字段,就会导致说70%的数据是不用来搜索的,结果硬是占据了es机器上的filesystem cache的空间,单挑数据的数据量越大,就会导致filesystem cahce能缓存的数据就越少
- 仅仅只是写入es中要用来检索的少数几个字段就可以了,比如说,就写入es id name age三个字段就可以了,然后你可以把其他的字段数据存在mysql里面,我们一般是建议用es + hbase的这么一个架构。
- hbase的特点是适用于海量数据的在线存储,就是对hbase可以写入海量数据,不要做复杂的搜索,就是做很简单的一些根据id或者范围进行查询的这么一个操作就可以了
- 从es中根据name和age去搜索,拿到的结果可能就20个doc id,然后根据doc id到hbase里去查询每个doc id对应的完整的数据,给查出来,再返回给前端。
- 你最好是写入es的数据小于等于,或者是略微大于es的 filesystem cache 的内存容量;
- 然后你从es检索可能就花费20ms,然后再根据es返回的id去hbase里查询,查20条数据,可能也就耗费个30ms,可能你原来那么玩儿,1T数据都放es,会每次查询都是5~10秒,现在可能性能就会很高,每次查询就是50ms。
- elastcisearch减少数据量仅仅放要用于搜索的几个关键字段即可,尽量写入es的数据量跟es机器的filesystem cache是差不多的就可以了;其他不用来检索的数据放hbase里,或者mysql。
- 所以之前有些学员也是问,我也是跟他们说,尽量在es里,就存储必须用来搜索的数据,比如说你现在有一份数据,有100个字段,其实用来搜索的只有10个字段,建议是将10个字段的数据,存入es,剩下90个字段的数据,可以放mysql,hadoop,hbase,都可以,这样的话,es数据量很少,10个字段的数据,都可以放内存,就用来搜索,搜索出来一些id,通过id去mysql,hbase里面去查询明细的数据
- 禁用swap
Elasticsearch在部署时,对Linux的设置有哪些优化方法
面试官
:想了解对ES集群的运维能力。
解答
:
- 1)关闭缓存swap;
- 2)堆内存设置为:Min(节点内存/2, 32GB);
- 3)设置最大文件句柄数;
- 4)线程池+队列大小根据业务需要做调整;
- 5)磁盘存储raid方式——存储有条件使用RAID10,增加单节点性能以及避免单节点存储故障。
6.2、JVM、GC
- JVM heap 不要超过32G
- 选择合适的GC 策略,必要时调整GC参数
- -XX:+AlwaysPreTouch 启动时分配(ES jvm 参数默认)
- -Xmx 、-Xms 一样大小
6.3、写入优化
- 手动指定mapping
- 字段不搜索、不排序等,index、doc_values 改为false;或者mapping 里dynamic=false,手动对需要索引、排序的字段进行配置
- 批量写入,建议每批100-1000,数据大小不要超过2M
- 可以多线程写入,但注意 RejectedExecutionException
- 调整刷新时间index.refresh_interval ,可调整为30s
- 迁移数据等时,index.refresh_interval -1不刷新,直到buffer满, index.number_of_replicas 0无副本
- 插入时,可以的话不要指定文档id,如果指定es还要判断是否已有
- 异步translog刷盘,但重启可能会丢失数据。
- “index.translog.durability”: "async”
- “index.translog.sync_interval”: “5s” 默认
6.4、搜索优化
- 避免使用nested(慢几倍)、父子关系(慢百倍)
- 数值类型不会做range查询,只做term查询,就用keyword
- 尽量不用脚本
- 低峰期做force merge,segment越少,搜索越快
- 不评分则放在filter子句里
- 避免、禁用模糊查询
- 限制模糊查询字段长度
- 使用standard analyzer 单字符分词,再用match phrase搜索
- 7.9版本对模糊查询有一定性能优化
- 当单shard size达到20G左右时,需要扩容shard
- 避免在搜索高峰期写入较多数据
- ES会自觉(无法暂停,只能限速)merge segemnt,merge期间占用资源较多,会影响搜索
- 分页性能优化
- es的分页是较坑的,为啥呢?举个例子吧,假如你每页是10条数据,你现在要查询第100页,实际上是会把每个shard上存储的前1000条数据都查到一个协调节点上,如果你有个5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并、处理,再获取到最终第100页的10条数据。
- 分布式的,你要查第100页的10条数据,你是不可能说从5个shard,每个shard就查2条数据?最后到协调节点合并成10条数据?你必须得从每个shard都查1000条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第100页的数据。
- 你翻页的时候,翻的越深,每个shard返回的数据就越多,而且协调节点处理的时间越长。非常坑爹。所以用es做分页的时候,你会发现越翻到后面,就越是慢。
- 我们之前也是遇到过这个问题,用es作分页,前几页就几十毫秒,翻到10页之后,几十页的时候,基本上就要5~10秒才能查出来一页数据了
- 1)不允许深度分页/默认深度分页性能很惨
- 你系统不允许他翻那么深的页,pm,默认翻的越深,性能就越差
- 2)类似于app里的推荐商品不断下拉出来一页一页的
- 类似于微博中,下拉刷微博,刷出来一页一页的,你可以用scroll api,自己百度
- 原理:scroll会一次性给你生成所有数据的一个快照,然后每次翻页就是通过游标移动,获取下一页下一页这样子,性能会比上面说的那种分页性能也高很多很多
- 针对这个问题,你可以考虑用scroll来进行处理,scroll的原理实际上是保留一个数据快照,然后在一定时间内,你如果不断的滑动往后翻页的时候,类似于你现在在浏览微博,不断往下刷新翻页。那么就用scroll不断通过游标获取下一页数据,这个性能是很高的,比es实际翻页要好的多的多。
- 但是唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。同时这个scroll是要保留一段时间内的数据快照的,你需要确保用户不会持续不断翻页翻几个小时。
- 无论翻多少页,性能基本上都是毫秒级的
- 因为scroll api是只能一页一页往后翻的,是不能说,先进入第10页,然后去120页,回到58页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻
6.5、设计阶段调优
- 1)根据业务增量需求,采取基于日期模板创建索引,通过roll over API滚动索引;
- 2)使用别名进行索引管理;
- 3)每天凌晨定时对索引做 force_merge 操作,以释放空间;
- 4)采取冷热分离机制,热数据存储到SSD,提高检索效率;冷数据定期进行shrink操作,以缩减存储;
- 关于es性能优化,数据拆分,将大量不搜索的字段,拆分到别的存储中去,这个就是类似于后面我最后要讲的mysql分库分表的垂直拆分。
- es可以做类似于mysql的水平拆分,就是说将大量的访问很少,频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引
- 你最好是将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉。
- 你看,假设你有6台机器,2个索引,一个放冷数据,一个放热数据,每个索引3个shard
- 3台机器放热数据index;另外3台机器放冷数据index
- 然后这样的话,你大量的时候是在访问热数据index,热数据可能就占总数据量的10%,此时数据量很少,几乎全都保留在filesystem cache里面了,就可以确保热数据的访问性能是很高的。
- 但是对于冷数据而言,是在别的index里的,跟热数据index都不再相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就10%的人去访问冷数据;90%的人在访问热数据。
- 5)采取curator进行索引的生命周期管理;
- 6)仅针对需要分词的字段,合理的设置分词器;
- 7)Mapping阶段充分结合各个字段的属性,是否需要检索、是否需要存储等
- 8)数据预热
- 假如说,按照filesystem cache 占用内存的一半,es集群中每个机器写入的数据量还是超过了filesystem cache一倍,比如说你写入一台机器60g数据,结果filesystem cache就30g,还是有30g数据留在了磁盘上。
- 举个例子,就比如说,微博,你可以把一些大v,平时看的人很多的数据给提前你自己后台搞个系统,每隔一会儿,你自己的后台系统去搜索一下热数据,刷到filesystem cache里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。
- 电商,你可以将平时查看最多的一些商品,比如说iphone 8,热数据提前 后台搞个程序,每隔1分钟自己主动访问一次,刷到filesystem cache里去。
- 对于那些你觉得比较热的,经常会有人访问的数据,最好做一个专门的缓存预热子系统,就是对热数据,每隔一段时间,你就提前访问一下,让数据进入filesystem cache里面去。这样期待下次别人访问的时候,一定性能会好一些。
6.6、写入调优
- 1)写入前副本数设置为0;
- 2)写入前关闭refresh_interval设置为-1,禁用刷新机制;
- 3)写入过程中:采取bulk批量写入;
- 4)写入后恢复副本数和刷新间隔;
- 5)尽量使用自动生成的id。
6.7、查询调优
- 1)禁用 wildcard;
- 2)禁用批量terms(成百上千的场景);
- 3)充分利用倒排索引机制,能keyword类型尽量keyword;
- 4)数据量大时候,可以先基于时间敲定索引再检索;
- 5)设置合理的路由机制。
6.8、其他调优
部署调优,业务调优等。
上面的提及一部分,面试者就基本对你之前的实践或者运维经验有所评估了。
7、QA
7.1、谈谈分词与倒排索引的原理
面试官
:想了解你对基础概念的认知。解答
:通俗解释一下就可以。
传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。有了倒排索引,就能实现o(1)时间复杂度
的效率检索文章了,极大的提高了检索效率。
倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。lucene从4+版本后开始大量使用的数据结构是FST。FST有两个优点:
- 1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;
- 2)查询速度快。O(len(str))的查询时间复7杂度。
7.2、说说分段存储的思想
Lucene 是著名的搜索开源软件,ElasticSearch 和 Solr 底层用的都是它。
分段存储是 Lucene 的思想。
早期,都是一个整个文档建立一个大的倒排索引。简单,快速,但是问题随之而来。文档有个很小的改动,整个索引需要重新建立,速度慢,成本高,为了提高速度,定期更新那么时效性就差。现在一个索引文件,拆分为多个子文件,每个子文件是段。修改的数据不影响的段不必做处理
7.3、谈谈你对段合并的策略思想的认识
分段的思想大大的提高了维护索引的效率。但是随之就有了新的问题。每次新增数据就会新增加一个段,时间久了,一个文档对应的段非常多。段多了,也就影响检索性能了。
检索过程:
- 查询所有短中满足条件的数据
- 对每个段的结果集合并
所以,定期的对段进行合理是很必要的。真是天下大势,分久必合合久必分。
策略:将段按大小排列分组,大到一定程度的不参与合并。小的组内合并。整体维持在一个合理的大小范围。当然这个大到底应该是多少,是用户可配置的。这也符合设计的思想
7.4、了解ES相关度评分算法 TF-IDF吗?
简单地说,就是你检索一个词,匹配出来的文章,网页太多了。比如 1000 个,这些内容再该怎么呈现,哪些在前面哪些在后面。这需要也有个对匹配度的评分。
TF-IDF 就是干这个的。
- TF = Term Frequency 词频,一个词在这个文档中出现的频率。值越大,说明这文档越匹配,正向指标。
- IDF = Inverse Document Frequency 反向文档频率,简单点说就是一个词在所有文档中都出现,那么这个词不重要。比如“的、了、我、好”这些词所有文档都出现,对检索毫无帮助。反向指标
TF-IDF = TF / IDF
7.5、能说说ElasticSearch 写索引的逻辑吗?
ElasticSearch 是集群的 = 主分片 + 副本分片。
写索引只能写主分片,然后主分片同步到副本分片上。但主分片不是固定的,可能网络原因,之前还是 Node1 是主分片,后来就变成了 Node2 经过选举成了主分片了。
客户端如何知道哪个是主分片呢? 看下面过程。
- 客户端向某个节点 NodeX 发送写请求
- NodeX 通过文档信息,请求会转发到主分片的节点上
- 主分片处理完,通知到副本分片同步数据,向 Nodex 发送成功信息。
- Nodex 将处理结果返回给客户端
7.6、ES的适用场景
优化流程如下:使用MySQL --》MySQL索引优化 --》使用分库分表策略 --》使用ES的适用场景
1、两个最常见的应用场景,一个是宽表、解决跨库 Join,另一个就是全文搜索。
分库分表在面对多维度查询时将变得力不从心,那该如何解决呢?
我们需要引入 Canal 数据同步工具,订阅 MySQL 的 Binglog,将增量数据同步到Elasticsearch 中,实现数据访问层面的读写分离。
我们以电商场景为例,用户在购买商品之前通常需要输入一些关键字搜索出符合自己期望的数据,例如商品表的表结构如下图所示
如果我们要查询关键字为“苹果电脑”,基于关系型数据库,我们通常会写出这样的 SQL 语句:
select * from goods a where a.goods_decribe like '%苹果电脑%';
上述语句并不会走索引,容易很快耗尽数据库链接而导致系统不可用.
因为 Elasticsearch 的底层是 Lucene,可以对需要查找的字段建立索引,中间还会进行分词处理,进行更智能的匹配。由于 Elasticsearch 底层会为字段建立倒排索引,根据关键字查询可以轻松命中缓存,从而能极大提升访问性能,实现低延迟访问
7.7、ES的deep paging问题如何解决?
- 1)不允许深度分页/默认深度分页性能很惨
- 你系统不允许他翻那么深的页,pm,默认翻的越深,性能就越差
- 2)类似于app里的推荐商品不断下拉出来一页一页的
- 类似于微博中,下拉刷微博,刷出来一页一页的,你可以用scroll api,自己百度
- 原理:scroll会一次性给你生成所有数据的一个快照,然后每次翻页就是通过游标移动,获取下一页下一页这样子,性能会比上面说的那种分页性能也高很多很多
- 针对这个问题,你可以考虑用scroll来进行处理,scroll的原理实际上是保留一个数据快照,然后在一定时间内,你如果不断的滑动往后翻页的时候,类似于你现在在浏览微博,不断往下刷新翻页。那么就用scroll不断通过游标获取下一页数据,这个性能是很高的,比es实际翻页要好的多的多。
- 但是唯一的一点就是,这个适合于那种类似微博下拉翻页的,不能随意跳到任何一页的场景。同时这个scroll是要保留一段时间内的数据快照的,你需要确保用户不会持续不断翻页翻几个小时。
- 无论翻多少页,性能基本上都是毫秒级的
- 因为scroll api是只能一页一页往后翻的,是不能说,先进入第10页,然后去120页,回到58页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻
7.8、ES上千万数据批处理如何解决?
8、ElasticSearch使用规范
8.1、elasticsearch 索引数据多了怎么办,如何调优,部署
面试官
:想了解大数据量的运维能力。
解答
:索引数据的规划,应在前期做好规划,正所谓“设计先行,编码在后”,这样才能有效的避免突如其来的数据激增导致集群处理能力不足引发的线上客户检索或者其他业务受到影响。如何调优,正如问题1所说,这里细化一下:
1、动态索引层面
基于模板+时间+rollover api滚动
创建索引,举例:设计阶段定义:blog索引的模板格式为:blog_index_时间戳的形式,每天递增数据。
这样做的好处:不至于数据量激增导致单个索引数据量非常大,接近于上线2的32次幂-1,索引存储达到了TB+甚至更大。
一旦单个索引很大,存储等各种风险也随之而来,所以要提前考虑+及早避免。
2、存储层面
冷热数据分离存储
,热数据(比如最近3天或者一周的数据),其余为冷数据。对于冷数据不会再写入新数据,可以考虑定期 force_merge 加 shrink 压缩操作,节省存储空间和检索效率。
3、部署层面
一旦之前没有规划,这里就属于应急策略。结合ES自身的支持动态扩展的特点,动态新增机器的方式可以缓解集群压力,注意:如果之前主节点等规划合理
,不需要重启集群也能完成动态新增的。
8.2、elasticsearch是如何实现master选举的
面试官
:想了解ES集群的底层原理,不再只关注业务层面了。
解答
:前置前提:
- 1)只有候选主节点(master:true)的节点才能成为主节点。
- 2)最小主节点数(min_master_nodes)的目的是防止脑裂。
这个我看了各种网上分析的版本和源码分析的书籍,云里雾里。核对了一下代码,核心入口为findMaster,选择主节点成功返回对应Master,否则返回null。
选举流程大致描述如下:
- 第一步:确认候选主节点数达标,elasticsearch.yml设置的值discovery.zen.minimum_master_nodes;
- 第二步:比较:先判定是否具备master资格,具备候选主节点资格的优先返回;若两节点都为候选主节点,则id小的值会主节点。注意这里的id为string类型。
题外话:获取节点id的方法。
1GET /_cat/nodes?v&h=ip,port,heapPercent,heapMax,id,name2ip port heapPercent heapMax id name3127.0.0.1 9300 39 1.9gb Hk9w Hk9wFwU
Action1:我们在电商的搜索框中搜索商品时,它都有一个搜索提示的功能,比如我输入“苹果”还没有点击搜索按钮的时候,搜索框下面会提示“苹果手机”、“苹果 11、苹果电脑”这些建议的搜索关键字,请你课后看一下 ES 的文档,想一下,如何用 ES 快速地实现这个搜索提示功能?
因为用户每输入一个字都可能会发请求查询搜索框中的搜索推荐。所以搜索推荐的请求量远高于搜索框中的搜索。es针对这种情况提供了suggestion api,并提供的专门的数据结构应对搜索推荐,性能高于match,但它应用起来也有局限性,就是只能做前缀匹配。再结合pinyin分词器可以做到输入拼音字母就提示中文。如果想做非前缀匹配,可以考虑Ngram。不过Ngram有些复杂,需要开发者自定义分析器。比如有个网址 www.geekbang.com,用户可能记不清具体网址了,只记得网址中有2个e,此时用户输入ee两个字母也是可以在搜索框提示出这个网址的。
以上是我在工作中针对前缀搜索推荐和非前缀搜索推荐的实现方案。
Action2:20210723-真线商品主搜集群升配
http://confluence.cai-inc.com/pages/viewpage.action?pageId=69910141
背景
商品主搜集群作为公司核心的ES搜索集群,目前承载了各前台电子卖场、App、小程序和部分二方业务的搜索需求。
近期ES集群在业务高峰期频繁触发CPU大于85%告警
数据统计&说明
- 商品数据增量
- 相对于去年7月份的3800w的总商品量,现在已经达6000w+,增幅58%
- QPS:观测单节点的线程池情况,去年同期的峰值在800左右,现在已经日常超过1.2~1.3k,同比增加50%+
- PS:grafana监控数据只保留两周,故去年的数据截图无法提供
- 业务场景
- 随着业务复杂度不断加深,前端商品展示组件化、多样化,让原来一个页面请求一次查询接口的场景翻了多倍
- 例如:
- 官网首页的5 ~ 6个商品展示组件,加上黑白名单配置会使请求翻倍,故一个用户进入一次首页会调用10~12次搜索接口;
- 搜索结果页多了聚合搜索,会多次调用商品
count
接口;
forcemerge
写入
- 原写入时间从凌晨3:00开始,6点前结束
- 现在写入时间提前到2:00,但是经常7:00未结束 --》 商品总量增多,更新量增多,导致forcemerge任务重
风险说明:forcemerge会占用很多的系统资源,所以目前在夜间定时触发,以免影响日间正常时段业务
- 需求
- 现状
- 目前ES有4个节点、14个索引、236个分片、文档数12亿
- 写可以达到 2 万
- bulk 1000 左右
硬件配置
- 3台master:4核8G
- 3台coordinator:8核32G
- 10台data:16核128G
数据、分片
- 50个主分片,副本分片数为1
- 单节点5个主分片+5个副本分片
- 每个分片大约14G~15G,整体存储占用1.4T
- 预估
- 现每月平均月增400w~600w商品左右,预计年底到明年初可接近1亿
- 升级
CPU升级:10个数据节点由16核升级至32核
费用预估:+2w/月
- 具体操作
- 各数据节点滚动升级即可,无特殊操作需求
- 后续Action
- 观测ES集群在业务高峰期的QPS和RT
- 观测ES集群在夜间的写入时间是否超时
- 成立慢查询专项,逐步优化各场景慢查询,遏制根源问题
- 继续和阿里云沟通,替换本地SSD磁盘(ES官方建议)