<meta charset="utf-8">
一、es基本组成
elasticsearch设计的理念就是分布式搜索引擎,底层其实还是基于lucene的,核心思想就是在多台机器上启动多个es进程实例,组成了一个es集群。
es中存储数据的基本单位是索引,eg:现在要在es中存储一些订单数据,你就应该在es中创建一个索引,order_idx,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是mysql里的一张表。 es基本组成单元,index -> type -> mapping -> document -> field。
index:想当mysql的一张表。
tyep:type其实没法跟mysql做对比,一个index里面可能又多个type,比如说:订单相关的表,有实体订单(手机、衣服、鞋子),又虚拟订单表(游戏币、点卡)等。一个index下面的的多个type里面基本大部分字段都一样,只有个别字段不一样。(很多情况下,一个index里可能就一个type,但是确实如果说是一个index里有多个type的情况,你可以认为index是一个类别的表,具体的每个type代表了具体的一个mysql中的表)
mapping:每个type都有一个mapping,如果把type认定为已个具体的一张表,那么mapping就可以看成是type的这张表的表结构。
document:表(type)里面的一条数据就叫document,这个和mongo很像;
field:每个document都有多个field,每个field代表了这个document中的一个字段的值。
二、es的基本架构
一个index,在es里面可以拆分成多个shard(相当于kafka的partition(分区)),每个shard里面存储部分数据。正是由于这个shard构成了分es分布式架构的基础。
每个shard都有一个primary shard,负责写入数据,对应的还有几个replica shard(primary shard的副本),primary shard数据写入之后,会同步到replica shard。
通过这个replica shard数据的备份,实现了es的高可用架构。
注:每个节点的primary shard的副本replica shard不会和primary shard 在同一个节点上,如下图。es集群会选举一个master节点,这个master节点负责管理工作,例如维护索引拉去元数据,负责切换primary shard 和replica shard的身份。如果master节点宕机,会重新选举一个master节点。之前master节点的primary shard 对应的replica shard会被新选举出来的master指定为replica shard,如果之前的机器恢复,会重新给现在的primary shard重新分配一个replica shard,接着会把数据给同步过去,让集群恢复正常。
[图片上传失败...(image-826fd9-1573635121131)]
三、es操作数据的工作原理?
客户端会选择一个节点发送请求过去,这个节点叫做协调节点(coordinating node),协调节点会对数据进行路由到对应的primary shard上。
写数据原理步骤:
1)、通过协调节点写入primary shard。先写入buffer,数据在buffer里面是不会被搜索到的,写入buffer的同时会把数据吸入translog日志文件。
2)、buffer内存不足时,或者到一定的时间(这个可以设置,默认是一秒),es就会把buffer的数据refresh到一个新的segment file中,但是在refresh到segement file 之前,es会将buffer里缓存的数据写入 os cache(cpu 高速缓存)。这个时候用户就可以在客户端搜索到数据了。这个过程叫refresh。(也可以手动调用es 的refresh的api)
为什么叫es是准实时的?NRT,near real-time,准实时。默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。
3)、重复1、2的步骤数据会不断的被写入buffer和translog,buffer的数据会不断的生成一个个的segment file,每次refresh,buffer会被清空,tanslog 保留,随着这个过程不断的推进,translog会达到一定的长度,就会触发commit操作。
4)、commit的第一步,就会先把buffer的数据刷新的os cache,然后清空buffer。
5)、es会将一个commit point写入磁盘文件,里面标记着这次commit对应的segment file。(这点类似于kafka的offset)
6)、commit 会将os cache里面的数据强行fsync到磁盘中。
translog日志文件用来记录还没有被持久化到segment file 里的数据,一旦机器重启,es 会读取translog里面的数据,恢复到buffer和os cache中去。
7)、将现有的translog清空,然后在重启一个tanslog,此时整个commit才算完成。默认每隔30分钟会自动执行一次commit,但是如果translog过大,也会触发commit。整个commit过程叫flush操作。(也可以通过收到调用es api 触发commit)
8)、写入translog之前其实也是先写入os cache的,默认每隔5秒会写入translog,这样就有可能会造成五秒数据的丢失,当然 也可以手动操作,直接fsync到磁盘,但是这样性能会很差。
删除数据操作原理:
1)、删除的时候同样客户端会随机到一个节点上,这个时候会生成一个 .del文件,将docId标记为deleted状态。
2)、如果是更新操作,就是将原来的一条数据标记为deleted 状态,然后重新写入一条数据。
3)、buffer每次refresh一次,就会产生一个segment file,默认情况下是一秒,segment file 会越来越多,此时会定期执行merge(合并segment file,多个小的segment file合并成一个大的segment file),这时会将标记为deleted 数据给删掉,这里其实也是一个commit point的操作。
整个es 的写入操作,包含四个核心概念:refresh、flush、translog、merge
读数据操作原理:
1)、数据写入的时候会自动创建一个docId,当然整个docId也可以手动指定,比如说orderId或者uesrId等。同时在写入primary shard的时候也是根据docId进行hash 计算,然后路由到对应的 primary shard的 。
2)、客户端发动请求到任意一个节点时,协调节点会根据docId进行路由,将请求转发到对应的节点,会使用round-robin随机轮训算法,在primary shard 和replica shard中随机选择一个去负载,然后把搜到的docId反还给协调节点,然后又协调节点去进行数据的合并、排序、分页等操作,然后在由协调节点根据docId去个节点拉去实际的document的信息,最终把数据返回给客户端。
3)、搜索的底层原理是倒排索引。
[图片上传失败...(image-b6189b-1573635121131)]
四、es搜索优化
根据es 数据的写入原理分析:
在往es写数据的时候,实际上都是写入 os cache,只有在os cache里面的数据才能被客户端给搜索到。
es 的底层严重依赖filesystem cache,所以具体优化方案如下:
1)、filesystem cache内存不够大,那么我们就在数据写入的时候只把可能会出现搜索的字段非存入es,例如:商品信息,我们可以存商品的名称,价格等相关信息。这样在读数据的时候同样的内存就可以读取更多的数据。(多余的数据存入mysql或者采用 es + hbase等架构),所以es写入的数据要小于等于或者略微大于es的filesystem file的容量。
2)、数据预热
比如:微博可以事先把一些大V,或者说平时看的人很多的数据,通过后台的一个系统,每隔一段时间就去自己触发搜索,让数据保持在filesystem file中。
3)、冷热分离
关于es性能优化,数据拆分,将大量不搜索的字段,拆分到别的存储中去,这个就是类似于后面我最后要讲的mysql分库分表的垂直拆分。
es可以做类似于mysql的水平拆分,就是说将大量的访问很少,频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。将冷数据写入一个索引中,然后热数据写入另外一个索引中,这样可以确保热数据在被预热之后,尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉
4)、document模型设计
es的联合查询:
写入es的时候,搞成两个索引,order索引,orderItem索引
order索引,里面就包含id order_code total_price
orderItem索引,里面写入进去的时候,就完成join操作,id order_code total_price id order_id goods_id purchase_count price
写入es的java系统里,就完成关联,将关联好的数据直接写入es中,搜索的时候,就不需要利用es的搜索语法去完成join来搜索了(相当于一张表事先冗余关联表的id字段,这个和mongo很像)
5)、分页性能优化
因为es 是分布式的所有的数据是不同的primary shard上存储,假如说要查询第一百页的数据(每页十条),假如又三台机器,那么每个节点,在查询的时候都得把自己节点primary shard的前一百条数据给查出来,然后交给协调节点去重新整合,最终拿出第一百页的十条数据,这样的话越往后越慢。
方案:
a、不允许深度分页。
b、似于app里的推荐商品不断下拉出来一页一页的,这种可以采用scroll api 去实现分页。
五、es生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?
1)es生产集群我们部署了5台机器,每台机器是6核64G的,集群总内存是320G
2)我们es集群的日增量数据大概是2000万条,每天日增量数据大概是500MB,每月增量数据大概是6亿,15G。目前系统已经运行了几个月,现在es集群里数据总量大概是100G左右。
3)目前线上有5个索引(这个结合你们自己业务来,看看自己有哪些数据可以放es的),每个索引的数据量大概是20G,所以这个数据量之内,我们每个索引分配的是8个shard,比默认的5个shard多了3个shard。