一.什么是 Elasticsearch
ElasticSearch 是一个基于 Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch 是用 Java开发的,并作为 Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。
二.Elasticsearch应用场景
(1).大型分布式日志分析系统ELK elasticsearch(存储日志)+logstash(收集日志)+kibana(展示数据)
(2).大型电商商品搜索系统、网站站内搜索、网盘搜索引擎等
三.Elasticsearch基本概念
(1)Near Realtime(NRT):近实时,两个意思,从写入数据到数据可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级
(2)Cluster:集群,包含多个节点,每个节点属于哪个集群是通过一个配置(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常
(3)Node:节点(简单理解为集群中的一个服务器),集群中的一个节点,节点也有一个名称(默认是随机分配的),节点名称很重要(在执行运维管理操作的时候),默认节点会去加入一个名称为“elasticsearch”的集群,如果直接启动一堆节点,那么它们会自动组成一个elasticsearch集群,当然一个节点也可以组成一个elasticsearch集群
(4)Index:索引(简单理解就是一个数据库),包含一堆有相似结构的文档数据,比如可以有一个客户索引,商品分类索引,订单索引,索引有一个名称。一个index包含很多document,一个index就代表了一类类似的或者相同的document。比如说建立一个product index,商品索引,里面可能就存放了所有的商品数据,所有的商品document。
(5)Type:类型(简单理解就是一张表),每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,比如博客系统,有一个索引,可以定义用户数据type,博客数据type,评论数据type。
(6)Document&field:文档(就是一行数据),es中的最小数据单元,一个document可以是一条客户数据,一条商品分类数据,一条订单数据,通常用JSON数据结构表示,每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段。
(7)shard:单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个shard都是一个lucene index。
(8)replica:任何一个服务器随时可能故障或宕机,此时shard可能就会丢失,因此可以为每个shard创建多个replica副本。replica可以在shard故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5个primary shard,5个replica shard,最小的高可用配置,是2台服务器。
(9)index -> type -> mapping -> document -> field
index:mysql数据库
type:就像一张表。ES5.X中一个index可以有多个type、
ES6.X中一个type只能有一个type、
ES7.X中移除了type这个概念,此时的index就像一张表了。
mapping:定义了每个字段的类型等信息。相当于关系型数据库中的表结构。
document:一条document就代表了mysql表里的一条记录。
field:每个field就代表了这个document中的一个字段的值。
四.倒排索引
查询有个算法叫倒排序:简单的说就是:通过分词把词语出现的id进行记录下来,再查询的时候先去查到哪些id包含这个数据,然后再根据id把数据查出来。
五.路由算法
首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。实际上,这个过程是根据下面这个公式决定的:
shard = hash(routing) % number_of_primary_shards
routing默认是文档的id,也可以自定义,hash(routing)后得到一个数字,再对主分片个数取余,这个数字肯定是0~number_of_primary_shards-1 之间的余数,就是我们文档所在的位置。所以我们在创建索引的时候就要确定好主分片个数,并永远不修改,否则就找不到之前的数据了。
六.Elasticsearch分布式架构原理
(1)一个index包含多个shard,也就是一个index存在多个服务器上
(2)每个shard都是一个最小工作单元,承载部分数据,比如有三台服务器,现在有三条数据,这三条数据在三台服务器上各方一条.
(3)增减节点时,shard会自动在nodes中负载均衡
(4)primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica shard中,不可能存在于多个primary shard
(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载
(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改
(7)primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard
(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上
七.Elasticsearch写入
(1)简单流程
1.客户端选择一个node发送请求过去,这个node就是coordinating node (协调节点)
2.coordinating node,对document进行路由,将请求转发给对应的node
3.实际上的node上的primary shard处理请求,然后将数据同步到replica node
4.coordinating node,如果发现primary node和所有的replica node都搞定之后,就会返回请求到客户端
这个路由简单的说就是取模算法,比如说现在有3台服务器,这个时候传过来的id是5,那么5%3=2,就放在第2台服务器
(2)文档写入流程
新文档创建或者更新时,进行如下流程:
更新不会修改原来的 segment,更新和创建操作都会生成新的一个 segment。数据哪里来呢?先会存在内存的 bugger 中,然后持久化到 segment 。
数据持久化步骤如下:write -> refresh -> flush -> merge
1 write 过程
一个新文档过来,会存储在 in-memory buffer 内存缓存区中,顺便会记录 Translog。
这时候数据还没到 segment ,是搜不到这个新文档的。数据只有被 refresh 后,才可以被搜索到。那么 讲下 refresh 过程
2 refresh 过程
refresh 默认 1 秒钟,执行一次上图流程。ES 是支持修改这个值的,通过 index.refresh_interval 设置 refresh (冲刷)间隔时间。refresh 流程大致如下:
in-memory buffer 中的文档写入到新的 segment 中,但 segment 是存储在文件系统的缓存中。此时文档可以被搜索到
最后清空 in-memory buffer。注意: Translog 没有被清空,为了将 segment 数据写到磁盘
文档经过 refresh 后, segment 暂时写到文件系统缓存,这样避免了性能 IO 操作,又可以使文档搜索到。refresh 默认 1 秒执行一次,性能损耗太大。一般建议稍微延长这个 refresh 时间间隔,比如 5 s。因此,ES 其实就是准实时,达不到真正的实时。
3 flush 过程
上个过程中 segment 在文件系统缓存中,会有意外故障文档丢失。那么,为了保证文档不会丢失,需要将文档写入磁盘。那么文档从文件缓存写入磁盘的过程就是 flush。写入磁盘后,清空 translog。
translog 作用很大:
保证文件缓存中的文档不丢失
系统重启时,从 translog 中恢复
新的 segment 收录到 commit point 中
具体可以看官方文档:https://www.elastic.co/guide/...
4 merge 过程
上面几个步骤,可见 segment 会越来越多,那么搜索会越来越慢?怎么处理呢?
通过 merge 过程解决:
就是各个小段文件,合并成一个大段文件。段合并过程
段合并结束,旧的小段文件会被删除
.liv 文件维护的删除文档,会通过这个过程进行清除
5 写入总结
如这个图,ES 写入原理不难,记住关键点即可。
write -> refresh -> flush
write:文档数据到内存缓存,并存到 translog
refresh:内存缓存中的文档数据,到文件缓存中的 segment 。此时可以被搜到
flush 是缓存中的 segment 文档数据写入到磁盘
八.Elasticsearch查询
(1)查询过程
1.客户端发送一个请求给coordinate node(协调节点)
2.协调节点将搜索的请求转发给所有的shard对应的primary shard 或replica shard
3.query phase:每一个shard 将自己搜索的结果(其实也就是一些唯一标识),返回给协调节点,有协调节点进行数据的合 并,排序,分页等操作,产出最后的结果
4.fetch phase ,接着由协调节点,根据唯一标识去各个节点进行拉去数据,最总返回给客户端
(2)查询原理
查询过程大体上分为查询和取回这两个阶段,广播查询请求到所有相关分片,并将它们的响应整合成全局排序后的结果集 合,这个结果集合会返回给客户端。
查询阶段
1.当一个节点接收到一个搜索请求,这这个节点就会变成协调节点,第一步就是将广播请求到搜索的每一个节点的分片拷 贝,查询请求可以被某一个主分片或某一个副分片处理,协调节点将在之后的请求中轮训所有的分片拷贝来分摊负载。
2.每一个分片将会在本地构建一个优先级队列,如果客户端要求返回结果排序中从from 名开始的数量为size的结果集,每 一个节点都会产生一个from+size大小的结果集,因此优先级队列的大小也就是from+size,分片仅仅是返回一个轻量级 的结果给协调节点,包括结果级中的每一个文档的ID和进行排序所需要的信息。
3.协调节点将会将所有的结果进行汇总,并进行全局排序,最总得到排序结果。
取值阶段
1.查询过程得到的排序结果,标记处哪些文档是符合要求的,此时仍然需要获取这些文档返回给客户端
2.协调节点会确定实际需要的返回的文档,并向含有该文档的分片发送get请求,分片获取的文档返回给协调节点,协调节 点将结果返回给客户端
九.Elasticsearch并发解决方案
为什么会出现并发问题,简单的说就是多个线程去操作同一个数据.
假如现在下单系统下2单,都要去减库存(100件),第一个线程进去减1件100-1=99,这时候还没更像到ES中,第二线程进去了,也要减一个库存100-1=99.现在系统卖出去2个,可是库存却还有99个,应该是98个
解决方案-悲观锁
解决方案-乐观锁
温馨提示,乐观锁会出现ABA情况
下面是2种解决方案,在网上找的图片:
在index后有一个refresh_interval默认1秒,在这个时间间隔内search是不可见的。
解决办法有两种:
1,通过ui层解决。比如更新成功后,弹出提示框;
2,设置刷新策略,有以下三种;
public static enum RefreshPolicy implements Writeable {
NONE("false"), // 默认;异步刷新,立即返回;
IMMEDIATE("true"), // 立即刷新,对es集群压力会比较大
WAIT_UNTIL("wait_for"); // 等刷新时返回,响应性不太好,
Elasticsearch操作数据后马上更新的办法,在写入、更新、删除后立即执行刷新数据,通过设置.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);即可。
示例如下:
update后立即更新数据
UpdateRequest request = new UpdateRequest(ES_INDEX, ES_TYPE, keyId).doc(body, XContentType.JSON); //写入完成立即刷新 request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); UpdateResponse updateResponse = Proxy.getRestHighLevelClient(host).update(request);