准备
了解下lucene是什么
全文索引是什么
es中存储数据的基本单位是索引,相当于mysql里的一张表。<br>mapping代表了表的结构定义。
倒排索引
传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。<br><br>而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。有了倒排索引,就能实现 o(1)时间复杂度的效率检索文章了,极大的提高了检索效率。
es接收到一个文档后,进行字符过滤->分词->归一化(停用词,大小写,单数和复数,相近词(相似词))
BKD树(K-D数和B+树)
如何实现master选举
ZenDiscover模块负责
对所有可以成为master的节点(node.master: true)根据nodeId字典排序,每次选举每个节点都把自己所知道的节点排一次序,然后选出第一个节点,暂且认为它是master节点。<br>
如果对某个节点的投票数达到一定值并且该节点自己也选举自己,那这个节点就是master
脑裂问题
主节点作用
负责集群层面的相关操作,管理集群变更,如创建删除索引,跟踪哪些节点是集群的一部分,并决定哪些分片分配给相关的节点。
es的分布式架构原理能说一下么<br>(es是如何实现分布式的)
1.核心思想就是在多个机器上启动多个es进程实例,组成一个集群。<br>2.创建一个索引,这个索引可以被分成多个shard(分片),每个shard存储一部分数据<br>3.多台机器中设置其中一台作为master node主节点,shared分片也会有主分片primary副分片replica<br>4.主副分片均匀分布在各个机器上<br>5.数据都是写入到主分片,然后主分片同步写入到副分片上。读数据则可读取主分片或者副分片的数据。<br>6.如果某台机器进程宕机,master进程宕机,选举其他进程作为master,并且将宕机的进程里的主分片的副分片转为主分片。<br>7.宕机的进程好了以后,便不再是master 节点,里面的主分片parimary shard转为副分片 replica shard。
es写入数据的工作原理是什么?<br>es查询数据的工作原理是什么?
写数据
基本写入流程<br>1.首先客户端随便选择一个节点去写,此时这个节点称为协调节点<br>2.协调节点对写的数据进行hash,确定这个数据属于哪个shard(分片)<br>3.发现当前协调节点不存在数据应该存储分片的主分片(primary shard),那么就对数据进行路由,到有pimary shard的节点上去<br>4.主节点同步数据到从节点,如果主节点和从节点都写完了,那么协调节点会返回写成功的响应给客户端。<br>
primary shard存储底层原理<br>(refresh,flush,translog,merge)
1.数据写入shard的时候,先写入内存buffer里,同时它会写入到translog日志文件里。<br>(此时如果客户端要查询数据是查不到的)<br>
2.如果buffer快满了或者每隔一段(默认1s)时间,es会把内存buffer中的数据 refresh刷到到一个新的segment file,每隔1秒就会产生一个新的segment文件(存入这1s的数据)<br> 但是如果buffer里面此时没有数据,就不会执行refresh。<br><font color="#c41230">数据在写入segment file之后,同时就建立好倒排索引了。</font>
3.操作系统中,磁盘文件其实都有一个东西,叫os cache,操作系统缓存。<br>就是说数据写入磁盘文件之前,会先进入os cache。<br><font color="#c41230"> 只要buffer里的数据写入到了os cache里面,客户端就能搜索到这部分数据了。</font><br>
为什么es是准实时的?<br>因为写入1s后才会刷到os cache里。<br> 写入到os cache里之后,buffer里的数据就会清空,translog会保留。<br>
translog也是磁盘文件,所以也是先写入os cache里的,默认5秒刷新数据到磁盘中
4.当translog不断变大,大到一定阈值,或者30分钟 就会触发commit(flush)操作。<br>(默认30分钟会自动执行)整个commit过程叫flush,手动根据es api也可以执行flush。<br><br> commit操作: 1.写commit point 2.将os cache fsync强刷到磁盘上去 3。清空translog日志文件<br><ul><li> 1.将buffer里的数据都写入os cache里面去,然后清空buffer。</li><li> 2.将一个commit point文件写入到磁盘,里面标示着之前写入的所有segment file,但是数据还是在os cache中。</li><li> 3.把os cache缓冲的所有的数据都fsync到磁盘上面的每个segment file中去。</li><li> 4.刷完以后会删除并新建translog</li></ul><br>
translog日志作用:<br>数据一般都是存储在buffer或者os cache内存里,一旦服务器宕机重启,内存中的数据就会丢失。<br>所以将es操作日志存储在translog里,es重启时通过translog将数据恢复到buffer及os cache中。
删除数据写入.del文件中标识一下这个数据被删除了,里面某个doc标识为deleted状态<br>客户端搜索某条数据,一旦发现这条数据在.del文件中找到这条数据被标识成删除状态了,就不会搜索出来。<br>
在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
由于每隔1s生成一个segment file,当文件多到一定程度的时候,es会merge成一个大的segment file,然后删除旧的文件<br><font color="#c41230">在merge的时候,会看一下如果某条数据在.del文件中标识为删除,那么merge后的新文件里这条数据就没了(物理删除)</font>
数据是准实时的
1s后才将数据刷新到os cache中
丢失数据情况
默认5s才会将translog从os cache写入到磁盘文件中,所以会有5s数据丢失的可能
解决:可以设置个参数,官方文档。每次写入一条数据,都是写入buffer,同时写入磁盘上的translog。<br> 但是会导致写性能,写入吞吐量下降一个数量级。本来1s可以写入2000条,现在1s钟可能只能写200条。
读数据过程<br>根据doc id查询
查询,GET某一条数据。数据写入了某个document,这个document会自动给你分配一个全局唯一的id (doc id),<br>同时也是根据doc id进行hash路由到对应的primary shard上去的。也可以手动指定doc id,比如用户id,订单id。<br>
<ul><li><span style="font-size: inherit;">客户端发送一个请求到任意一个node,成为协调节点</span></li><li><span style="font-size: inherit;">协调节点对doc id进行hash,找到对应shard,然后对document进行路由,请求转发到对应的节点,此时会使用round-robin随机轮询算法,在primary shard及所有replica shard中随机选择一个,让读请求负载均衡。</span></li><li><span style="font-size: inherit;">接收请求的节点 返回 document 给协调节点(coordinate node)</span></li><li><span style="font-size: inherit;">协调节点返回 document 给客户端</span></li></ul>
搜索数据过程
es最强大的是做全文检索,比如三条数据<br>java真好玩,java好难学,j2ee真好<br>根据java搜索出来
<ul><li>客户端发送一个请求到协调节点</li><li>协调节点将搜索请求转发到所有的shard对应挑选的primary shard或者replica shard也可以。</li><li>query phase:每个shard将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并,排序,分页等操作,产出最终结果。</li><li>fetch phase:接着由协调节点,根据doc id去各个节点上拉取实际的document数据,最终返回给客户端。</li></ul>
es调优
es在数据量很大的情况下(数十亿级别),es怎么提高查询性能?
十亿数据,第一次5~10s,第二次就快了
es性能优化是没有什么银弹的。不要期待随手调一个参数,就可以万能的应对所有性能慢的场景。<br>有些场景换个参数,或者调整个语法就能搞定,但是绝对不是所有场景都是这样的。
1.性能优化杀手锏 filesystem cache
第一次从磁盘查出数据会存到内存的fileSystem Cache,es搜索引擎严重依赖底层的os cache。
如果走磁盘一般肯定上秒, 但是如果走filesystem cache,走纯内存,那么基本上就是毫秒级的。从几毫秒到几百毫秒不等。
<font color="#c41230">1.如果要es性能好,最佳情况下,机器的内存要容纳你总数据量的一半。</font>
比如es中要存储1T数据,那么你多台机器留给filesystem cache的内存要加起来综合到512g。
<font color="#c41230">2.往es里存少量的数据,比如30个字段只用到了三个就存三个。让内存留给filesystem cache的大小跟数据量一致。</font>性能就会非常高,一般可以在1s以内
<font color="#c41230">3.其他字段的数据可以存在mysql里面,建议采用es+hbase</font><br>hbase的特点就是适用于海量数据的在线存储,就是可以对hbase写入海量数据,不要做复杂的搜索,就是做很简单的一些根据id或者范围查询的操作
总结:最好写入es数据小于 fileSystem cache内存大小
2.缓存预热
假如说,按照上面的方案去做了,es集群中每个机器写入的数据量还是超过了filesystem cache的一倍,60g数据,filesystem cache就30g,还有30g在磁盘中
可以自己后台搞个系统,每隔一会就去搜索一下热数据,刷到filesystem cache中。后面用户搜索热数据就是直接去内存里查了
3.冷热分离
1.将大量不搜索的字段,拆分到别的存储引擎里去,这个类似于mysql分库分表的垂直拆分。
2.可以做类似mysql水平拆分,就是说将大量的访问很少,频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。
比如:6台机器,2个索引,一个放冷数据,一个放热数据,每个索引3个shard<br> 3台放热数据index;3台放冷数据index;<br> 这样的话,大量的时候是在访问热数据,热数据可能就占总数据的10%,此时数据量很少,几乎能确保数据全部保留在filesystem cache<br> 对于冷数据而言,是在别的index里面,跟热数据都不在同一个index机器上,如果有人访问冷数据,在磁盘上,此时性能差点就差点了。
子主题
4.document模型设计
<font color="#c41230">es里的复杂的关联查询,复杂的查询语法,尽量别用,一旦用了性能一般都不太好。<br></font>所以要好好设计es里的数据模型。
写入es的java系统里,就完成关联,将关联好的数据直接写入es中,搜索的时候就不需要利用es的搜索语法
比如 mysql两个表需要join<br>在写入es的时候java直接将join好的数据写入es,不用es的join语法查询
5.分页性能优化
es分页性能比较坑<br>假设每页10条数据,现在要查询第100页,<font color="#c41230">实际上是会把每个shard上存储前1000条数据都查到一个协调节点上,如果你有5个shard,那么就有5000条数据,接着协调节点对这5000条数据进行一些合并,处理。再获取到最终第100页的10条数据。</font><br>翻页的时候,翻的越深,每个shard返回的数据就越多,协调节点处理数据时间越长,非常坑爹。<br>
1.不允许深度分页/默认深度分页性能很差。
系统不允许翻那么深的页,或者告诉产品默认翻的越深性能越差
2.类似于app里的推荐商品或者微博,不断下拉出现一页一页的。<br>可以用scroll api来进行处理<br>scroll会一次性给你生成所有数据的快照,每次翻页通过游标移动,获取下一页这样子,性能会比上面说的那种分页性能高很多。<br>无论分多少页,性能基本上都是毫秒级的。<br>因为scroll api 只能一页一页往后翻,不允许先第十页再120页。
es生产集群的部署架构是什么?<br>每个索引的数据量大概有多少?<br>每个索引大概有多少分片?
基本的版本,不要虚,说完就过去了
(1)es生产集群我们部署了5台机器,每台机器是6核64G的,集群总内存是320G<br>(2)我们es集群的日增量数据大概是2000万条,每天日增量数据大概是500MB,每个月数据量大概在6亿,15G。<br> 目前系统已经运行了几个月了,现在集群数据总量大概是100G左右。<br>(3)目前线上有5个索引(结合业务来),每个索引的数据量大概是20G,所以这个数据量内,我们每个索引分配的是8个shard,比默认的5个shard多了3个shard。