倒排索引
所谓的倒排索引,就是把你的数据内容先分词,每句话分成一个一个的关键词,然后记录好每个关键词对应出现在了那些id标识的数据里.
那么你要搜索包含关键词的帖子,直接扫描这个倒排索引,在倒排索引里找到这个关键词对应的那些数据的id就好了.
然后你可以从其他地方根据这几个id找打对应的数据就可以了,这个就是倒排索引的数据格式以及搜索的方式,上面这种利用倒排索引查找数据的方式,也被称为全文检索.
Elasticsearch的数据结构
物理存储
Cluster
集群是一个或者一个以上的按节点(服务器)的集合,并在所有节点上提供联合的索引和搜索功能,集群由唯一的名称标识,默认情况下是”elasticsearch”.该名称很总要,因为如果节点设置为通过其名称加入集群,则节点只能是集群的一部分.
Node
节点是作为集群一部分的单一服务器,存储您的数据,并参与集群的索引和搜索功能,就像一个集群一样,一个节点由一个名称来标识,默认情况下使用一个随机的通用唯一标识符UUID,它在启动时分配给该家电,所有的节点通过设置集群名cluster.name来确定数据某个集群
Shard
由于索引存在存储可能超过单个节点的硬件限制的大量数据,例如,占用1TB硬盘空间的10亿个文档的单个索引可能不适合单个节点的硬盘,.或者可能太慢,无法单独从单个节点提供搜索请求,因此分片就是为了解决这个问题,每个索引被分成若干个分片,分片被存储在不同的节点中,分片很重要,使用分片的作用:
允许水平分割/缩放内容,提高拓展能力
允许在分片(可能在多个节点上)分布和并行操作,从而提高性能/吞吐量
Replica
副本是分片的复制,副本的作用:
如果分片/节点出现故障,则提供高可用性
允许拓展搜索量/吞吐量.因为可以对所有副本并行执行搜索
逻辑存储
Index
索引是具有某种相似特征的文档的集合
Type
类型是您的索引的逻辑类别/跟去,允许您将不同类型的文档存储在同一索引中
Document
文档是可以索引的基本信息单元
Fieid
多个字段组成一个文档,一个索引中的所有文档类型中对于具有相同名称的字段必须是同一种数据类型
Mapping
映射就是字段即字段和字段类型的对应关系,映射机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型.
比如说index这个东西,他是索引的意思,其实他有点类似数据库里的一张表,大概对应表的那个概念.比如你搞一个专门存放帖子的索,然后他有id,title,content这个几个field.这个field大致就是他的一个字段.
然后还有一个概念,就是document,这个就代表了index中的一条数据,是es中的最小数据单元.
下面就是一条document,这个document可以写到index中去,算是index里的一条数据.
而且写到es之后,这条数据的内容就会拆分为倒排索引的数据格式来存储
Elasticsearch的分布式结构原理
所谓的分布式搜索引擎,就是把大量的索引的数据拆散成多块,每台机器放一部分,然后利用多台机器对分散后的数据进行检索,所有操作全部是分布在多台机器上进行,形成了完整的分布式的架构.
使用shard分片,将数据分为多个shard存储在不同的机器上,在搜索的时候,多台机器上的shard直接可以分布式的并行对一部分数据进行搜索,起到一个分布式搜索的效果,.大幅度提升海量数据的搜索性能和吞吐量.
ElasticSearch的高可用
对于高可用,使用了Replica多副本数据冗余机制,在elasticsearch中,就是支持对每个index设置一个replica数量的,也就是每个shard对应的replica副本的数量.
比如说你现在有一个index有三个shard,你设置对每个shard做一个replica副本,那么此时每个shard都会有一个replica shard
这个初始的shard就是primary shard.而且primary shard 和replica shard 是绝对不会放在一台机器上的,避免一台机器宕机直接一个shard的副本也同时丢失了.
在这个replica机制下,每个primary shard都有一个replica shard 在别的机器上,任何一台机器宕机,都可以保证数据不会丢失,分布式搜索引擎可以继续使用.
在写数据的时候,会将数据写入primary shard 中,primary shard会将数据同步到replica shard上去.
在多台机器的多个es进程中,会选举出来一个master节点,这个节点主要做一个维护工作,比如切换primary shard 和replica shard,如果master宕机了,会在其他节点再选举出一个master节点,新的master会感知到,自己机器上的replica shard对应的primary shard没了.,就把自己机器上的replica shard 提拔为primary shard
读取数据的时候,可以从primary shard读,也可以在replica shard中去读
ElasticSearch默认是支持每个index是5个primary shard ,每个primary shard有一个replica shard 作为副本
Elsaticsearch的数据写入流程
当有数据要写入的时候,数据会随机进入多个机器上的es进程中,这个es进程叫协调节点,他中的primary shard会接到这个数据,然后做一个Hash计算,计算这个数据是属于哪个机器上的es进程的primary shard 的,然后把这个数据路由到计算出来的es进程中,写入对应的primary shard ,然后再同步给对应的replica shard,都写完后,协调节点会返回一个写入成功给客户端.
数据写入primary shard 的内部过程
- 数据先写入内存buffer中,在buffer里的时候数据客户端是搜索不到的,同时将数据写入translog日志文件,防止在这个过程中宕机,造成内存buffer中的数据丢失
- 如果内存buffer快满了,或者到了固定的一段时间,es就会将内存buffer中的数据refresh到一个新的segment file磁盘文件中,默认是每隔1秒refresh,这个segment file存储的就是最近1秒中内存buffer中存储的数据
如果buffer中没有数据,就不会执行refresh操作,不会每秒创建一个空的segment file文件
在refresh到磁盘文件的时候,不是直接写入的,操作系统中,磁盘文件都有一个os cache的操作系统缓存,就是说数据写入磁盘文件之前,要先进入os cache中去
只要内存buffer中的数据被refresh操作,刷入os cache中,就代表这个数据可以被客户端搜索到了,内存buffer中的数据就清空了,因为不需要保留内存buffer了, translog保留,随着这个过程的推进,tanslog会变的越来越大,当translog达到一定长度的时候,就会触发commit操作.
所以说es为什么是准实时的,NRT near real-time,准实时,因为往es中写的数据要1秒后才能搜索到,也可以通过API手动将内存buffer中的数据刷入os cache中,立马可以让搜索到
3. 这个commit操作,会将一个commit point写入磁盘文件,这些commit point 标识着所有的segment file文件,然后将os cache数据fsync强刷到磁盘上去,然后将现有的translog文件清空,然后再次重启启用一个translog文件,此时,commit操作就完成了.默认会每隔30分钟会自动执行一次,但是如果translog过大,也会触发commit,整个commit过程,叫做flush操作,我们也可以通过es的API,手动执行flush操作,手动将os cache中的数据强刷到磁盘上去,记录commit point文件,.写入到translog文件
Tanslog日志文件的作用是什么?
就是在你执行commit操作之前,数据要么停留在内存buffer中,要么在os cache中,一旦机器宕机,这些内存中的数据就全部丢失了.
所以需要将数据对应的操作写入一个专门的日志文件,tanslog日志文件中,一旦此时机器宕机,再次重启的时候,es会自动读取tanslog日志文件中的数据,恢复到内存buffer和os cache中
Translog文件的写入是这样的,primary shard会先将数据写入os cache 中,os cache会每隔5秒中将数据刷到translog文件磁盘中
基于上面的结构,es是有可能丢失数据的,最多丢失5秒的数据.原因是这样的:当数据停留在buffer,translog的os cache,segment file的os cache中的时候,突然宕机,这样数据是不在磁盘的,会导致丢失数据
如果希望已经不丢失数据的话,设置参数,要求每次有数据写入,都是写入buffer,同时写入磁盘上的translog,但是这样会导致性能下降
4. 如果是删除数据
.del的磁盘文件,删除数据就是把数据写入这个.del文件,标识一下,客户端要搜索一条数据的时候,一旦发现数据在.del文件中标识为删除状态,就不会被搜索出来
5. 如果是更新数据
就把原来的数据在.del文件中标识,然后再新增一条数据
6. 内存Buffer没refresh一次,就会产生一个segment file,随着segment file越来越多,就会定期执行merge,
每次merge的时候,就会将多个segment file文件合并成一个,原来的segment file文件就删掉了,同时这里会将标识为deleted的doc给物理删除掉,
7. es中的核心操作:refresh,flush,merge,translog
8. 数据在写入segment file后,就已经建立好倒排索引了
es读取数据的过程
查询,GET某一条数据,写入了某个document,这个document会自动给你分配一个全局唯一的id,doc id同时也是根据doc id进行hash路由到对应的primary shard上面去,也可以手动指定doc id,比如订单id
你可以通过doc id来进行查询,根据doc id进行hash,判断出来当时把doc id分配到了那个shard上面去,从哪个shard去查询,此时,会使用round-robin随机轮询算法,在被分配的primary shard 和多个replica shard中随机选择一个,让读请求负载均衡,然后将读取到的数据返回给协调节点,然后协调节点拿到这条数据返回给客户端
es搜索数据的过程
客户端将搜索数据的请求发到节点中,这个节点就变成了协调节点,将请求发送到每个shard中,搜索完毕后,返回给客户端
es在数据量很大的情况下(数十亿级别),如何提高查询效率
es性能优化是没有银弹的,不是随手调一个参数就可以做到万能的应对所有的性能慢的场景,也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是不是所有的场景都是这样.
1. 性能优化的杀手锏:filesystem cache,这是对es性能影响最大的因素
(这个性能优化的原理:es减少数据量仅仅放要用于搜索的几个关键字段即可,尽量写es的数据量跟es机器的filesystem cache是差不多就可以了,其他的不用检索的数据放在Hbase中,或者放在mysql中)
es严重依赖底层的filesystem cache,如果给filesystem cache更多的内存,尽量让内存容纳所有的sgement file索引数据文件,那么你搜索的时候就基本是走内存查找的,性能会非常高.
性能差距可以有多大:如果走磁盘肯定上秒了,内存是毫秒级别
客户端在进行查询搜索的时候,会先去filesystem cache中查询有无这条数据,没有的话再去磁盘中去查找数据,然后将数据缓存到filesystem cache中,再返回给客户端.在客户端第二次查询同一条数据的时候,就直接去filesystem cache中去查到了,这样速度可以快一点
最佳的性能下,机器的内存,要至少容纳总数量的一半才行
还可以尽可能的减少往filesystem cache中写的数据的字段,只写入要用来检索的少数几个字段就可以了,其他的字段甚至可以存在mysql中,我们一般是建议用es+hbase的这么一个架构,
Hbase的特点是适用于海量数据的在线存储,就是对hbase可以写入海量数据,不要做复杂的搜索,就是做很简单的一些根据id或者范围进行查询的这么一个操作就可以了
从es中根据name和age去搜索,拿到的结果可能就有20个docid,然后根据doc id和hbase里查询完整的数据,查出来返回客户端.
最好是写入es 的数据小于等于,或者是略微大于es的filesystem cache的内存容量,然后,你从es检索可能也就花费几十毫秒,然后在根据es返回的id去hbase里去查询,可能也就花费几十毫秒
Hbase,全称是Hadoop Database,是一个面向列存储的分布式存储系统,它的优点在于可以实现高性能的并发读写操作,同时Hbase还会对数据进行透明的切分,这样就使得存储本身具有了水平伸缩性.
Hbase的优点
1.列的可以动态增加,并且列为空就不存储数据,节省存储空间
2.Hbase自动切分数据,使得数据存储自动具有水平scalabllity
3.Hbase可以提供高并发读写操作的支持
Hbase的缺点
1.不能支持条件查询,值支持按照Row key来查询
2.暂时不能支持Master server的故障切换,当Master宕机后,整个存储系统就会挂掉
2. 缓存预热
假如说,哪怕你就是按照方案一去做了,es集群中每个机器写入的数据量还是超过了filesystem cache一倍,比如说你写入一台机器60g内存,结果filesystem cache就30g,还是有30g数据留在了磁盘上.
缓存预热就是你提前把一些热数据,做一个后台搜索,自己把这些热数据刷到filesystem cache中去,用户再去搜索这些热数据,就直接去内存中去搜索了,性能比较快.
哪些是热数据呢?
举个例子:比如说微博,可以把一些大v,平时看的人很多的数据给提前后台搞个系统,每隔一会,后台自己去搜索一下热数据,把这些热数据刷到filesystem cache中去,用户再去搜索就是去内存中搜索的数据,比较快,
举个例子:电商,你可以将平时查看最多的一些商品,热数据提前搞个后台程序,每隔一段时间自己搜索一次,刷到filesystem cache中
3. 冷热分离
关于es的性能优化,其实就是在做数据拆分,那把这些大量的不搜索的字段,拆分到别的存储中去,这个就是类似于mysql的分库分表的垂直拆分.
es可以做类似于mysql 的水平拆分,就是将大量访问很少,频率很低的数据,单独写一个索引,将访问很频繁的热数据单独写一个索引
因为不同的索引,es经过hash计算后会把数据放在不同的shard上面,也就是不同的机器上面.
4. document模型设计
就是es中最小存储单元的设计,就是一条数据的字段的设计
在es中,如果使用复杂的查询语法,性能一般
那么怎么解决复杂的查询呢,比如说mysql中两个表的join查询,在es中尽量不要和mysql一样建立两个索引,应该建立一个索引,把两个表的数据都提前join好再写入这个索引,在写入es的java系统里就完成join操作,后面在搜索的时候,就不需要es的复杂搜索语法了,性能不会变差
5. 分页的性能优化
es的分页是比较坑的,举个例子,假设每页10条数据,那么你要查询100页的时候,实际上会把每个shard上存储的前1000条数据都查到一个协调节点上,假设有5个shard,就要查询出5000条数据,接着协调节点还要对这5000条数据进行一些合并,排序,筛选等处理操作,再获取到最终第100页的10条数据,所以翻页的时候,翻的越深,查询的性能就越差
针对这个问题,
1.直接设置es不允许深度分页/或者默认
2.如果是类似推荐的热点商品的不断下拉出来一页一页的,或者是贴吧微博热点,下拉刷一页一页的热点文章,你可以使用scroll api
scroll会一次性的给你生成所有数据的一个快照,然后每次翻页就是通过游标移动,获取下一页,性能会高很多,无论翻多少页性能都是毫秒级
但是scroll api是只能一页一页往后翻的,不能跳页,所以天然适合贴吧微博等热点文章往下拉的时候,所以很多热点商品不能随意翻页
生产环境的问题
- es生产环境的部署架构是什么?
一般中小型公司,es生产集群我们部署5台机器,每台机器是6核64G内存,集群总内存是320G - 每个索引的数据量大概有多少?
一般中小型公司,es集群的日增量数据大概是2000万条,每天日增量数据大概是500MB,每月增量数据大概是6亿条,15G.目前系统已经运行了几个月,现在es集群里的数据总量大概是100G - 每个索引大概有多少个分片?
目前线上有5个索引,每个索引的数据大概是20G,所以这个数据量之内,我们每个索引分配的是8个shard,比默认的5个shard多了3个shard