transportclient:通过监听9300端口tcp进行数据传输,它可以触摸到es的API和结构,此客户端对ES的版本兼容性较差,并且它在高并发环境下会有性能问题。
restclient:restclient就是采用http协议进行交互,它相比transportclient最大的好处就是对ES版本兼容性较好。restclient也分为high-level和low-level两种,两者原理基本一致,区别最大的就是封装性。low-level各种操作都要你自己封装,并且java本身不支持json还需要引用第三方包。而high-level是针对elasticsearch的api进行高级封装,和elasticsearch的版本关联大一些。
spring-data-elasticsearch:spring官方提供的框架,使用起来非常方便,3.2.0 版本之前是基于transportclient封装的,在此之后是基于HighLevelRestClient进行封装的,因此建议使用3.2.0 及以后的版本。
spring-boot-starter-data-elasticsearch:springboot官方提供的客户端,内部使用spring-data-elasticsearch,springboot-2.2(对应spring-data-elasticsearch-3.2.0)
该如何选择客户端?
- 若使用的是springboot项目(spring-boot-starter-data-elasticsearch启动器),建议使用springboot-2.2及以后版本
- 若只是一个普通spring项目,使用spring-data-elasticsearch-3.2.0以后版本。
- 若以上两种都不是,建议使用HighLevelRestClient,而非transportclient
2.1 分析创建索引
目标:理解如何分析数据并创建索引库
需求:将数据库中Goods表的数据导入到ElasticSearch中(资料中有sql脚本)
创建脚本如下:
字段说明:
- title:商品标题
- price:商品价格
- num:商品库存
- category:商品类别
- brand:品牌名称
添加文档进行测试:
2.2 代码实现
1)创建maven工程
2)添加相关依赖包
3)创建启动类
4)创建并编写application.yml文件
5)编写实体类Goods
6)创建GoodsDao数据访问层
7)在resources目录下创建mappers文件夹,创建并编写GoodsMapper.xml文件
8)编写单元测试用例
项目结构:
运行结果:
注解说明
- @document(indexName = “goods”, type = “_doc”) // 指定对应索引库以及类型
- @Id // 指定对应ES中_id的字段
- @Field(type = FieldType.Text, analyzer = “ik_smart”) // 指定字段的数据类型,以及分词器
- 添加文档前,先创建索引库、类型、映射信息
3.1 matchAll 查询
应用场景:当查询列表的页面初始化时,没有任何查询条件
1)改造Goods实体类,添加toString方法
2)编写测试用例
运行结果:
3.2 term查询
应用场景:不想对搜索关键字进行分词,搜索的结果更加精确。
执行搜索可以发现结果为空,为何?在前一天我们其实已经学过,term搜索是将搜索关键字的整个内容作为词条去倒排索引中进行词条的等值匹配。如果倒排索引中并没有分出"老人手机"这个词,就搜索不到。我们可以通过ES提供的接口看看某字符串按某分词器分出的效果:
3.3 match查询
应用场景:想对搜索关键字进行分词,搜索的结果更全面
-
会对查询条件进行分词
-
然后将分词后的查询条件和词条进行等值匹配
-
默认取并集(OR)
若想要结果取交集(既包含手机,又包含老人),可以如下进行操作
3.4 模糊查询(了解)
应用场景:当使用match搜索仍然查询不到数据,可以尝试使用模糊查询,范围更广
样例:
可以发现查询的结果中,那些title包含"华为"的数据查不出来,因为那些数据,没有分出"华"这一个字,而分出的就是"华为",这个时候我们若想把包含"华为"的数据都查出来,就可以使用模糊查询。
wildcard查询特点
-
会对查询条件进行分词
-
分出的词和索引库的词条进行模糊匹配,可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
-
默认取结果并集
3.5 范围查询
应用场景:当想对数值类型的字段做区间的搜索,例如商品价格。
3.6 字符串查询(了解)
应用场景:当不知道搜索的内容存储在哪个字段时,可以使用字符串搜索
- 会对查询条件进行分词
- 将分词后的查询条件和词条进行等值匹配
- 默认取并集(OR)
- 可以指定多个查询字段
1)不指定字段
2)指定字段
3.7 布尔查询
应用场景:当存在多个查询条件时
语法
如果想词条查询品牌为华为,或title包含手机的数据,即如下所示:
3.8 分页与排序
3.9 聚合查询
- 指标聚合:相当于MySQL的聚合函数。max、min、avg、sum等
- 桶聚合:相当于MySQL的 group by 操作。(不要对text类型的数据进行分组,会失败)
3.10 高亮查询
分析:高亮显示如何实现?
高亮三要素:
- 高亮字段
- 前缀
- 后缀
应用场景:随着业务需求的变更,结构可能发生改变。ES的索引一旦创建,只允许添加字段,不允许改变字段(因为改变字段,需要重建倒排索引,影响内部缓存结构,性能太低),那么此时,就需要重建一个新的索引,并将原有索引的数据导入到新索引中。
操作步骤
1)创建一个索引库student_index_v1,并指定映射信息
2)往student_index_v1索引库添加数据
3)此时,由于业务需求变更,需要往birthday字段添加一个1990年01月01日的数据
毫无疑问添加失败,因为birthday的类型是date,不支持这种数据格式,那我们就需要修改birthday字段的数据类型为text或keyword,但是前面也提过,ES是不准我们修改字段的,因此就需要用到重建索引
4)再创建一个索引库student_index_v2,并指定映射信息
5)将student_index_v1中的数据导入到student_index_v2中
6)查看student_index_v2可以发现数据已经导过来了,而且此时也可以添加新数据比如1990年01月01日,操作如下:
7)现在仍然存在一个问题,比方说我们之前的java代码中已经写死了索引库名称,如果重建了索引,新数据还往旧的索引库里插入肯定是不行的,这个时候就需要用到另一个操作:索引别名
这个时候,操作student_index_v2索引库,可以用student_index_v2,也可以用student_index_v1
应用场景:当数据库的数据发生了增删改,需要同步数据至索引库。
代码实现
1)新增文档/修改文档
2)删除文档
ElasticsearchRepository 是spring-data框架提供的一个接口,封装了ES的一些增删查改的基本API,使用起来比较方便。
使用步骤
1)创建GoodsRepository接口,让其继承ElasticsearchRepository
2)在调用GoodsRepository时,可以发现里面多了一些CRUD的方法
7.1 集群及分布式介绍
- 集群:多个人做一样的事。
- 分布式:多个人做不一样的事。
说明:在一个系统中,往往分布式和集群是并存的。
7.2 相关概念
- 节点(node) :集群中的一个 Elasticearch 服务实例。在Elasticsearch中,节点的类型主要分为如下几种:
- master eligible节点:有资格参加选举成为Master的节点,默认为true(可以通过node.master: false设置)。
- data节点:保存数据的节点,默认为true(可以通过node.data: false设置)。
- Coordinating 节点:客户端节点。负责接收客户端请求,将请求发送到合适的节点,最终把结果汇集到一起返回,默认为true。
- 集群(cluster):一组拥有相同集群名称的节点,集群名称默认是elasticsearch。
- 索引(index) :es存储数据的地方,相当于关系数据库中的database。
- 分片(shard):索引库可以被拆分为不同的部分进行存储,称为分片。在集群环境下,一个索引库的不同分片可以拆分到放到不同的节点中,分片的好处有如下两点。
- 提高查询性能(多个节点并行查询)
- 提高数据安全性(鸡蛋不要放在一个篮子里)
- 主分片(Primary shard):相对于副本分片的定义。
- 副本分片(Replica shard):即对主分片数据的备份,每个主分片可以有一个或者多个副本,数据和主分片一样,副本的好处有如下两点:
- 数据备份,防止数据丢失
- 一定程度提高查询的并发能力(同一份完整的索引库的数据,分成了两份,都可以查询)
说明:主分片和副本分片永远不会分配在同一个节点上
7.3 集群搭建
请参考
7.4 JavaAPI 操作集群
1)spring-boot-data-elasticsearch,修改yml配置即可
2)HighLevelRestApi
7.5 分片配置
-
在创建索引时,如果不指定分片配置,ES6默认主分片5,副本分片1,而ES7默认主分片1,副本分片1。
-
在创建索引时,可以通过settings设置分片
-
分片与自平衡:当节点挂掉后,挂掉的节点分片会自平衡到其他节点中
-
在Elasticsearch 中,每个查询在每个分片的单个线程中执行,但是可以并行处理多个分片。
-
分片数量一旦确定好,不能修改。
查看分片分布情况步骤如下:
常用配置:
1、每个分片推荐大小10-30GB
2、分片数量推荐 = 节点数量 * 1~3倍
思考:比如有1000GB数据,应该有多少个分片?多少个节点?
分片数:1000 / 20 = 50
节点数:50 / 2 = 25
7.6 路由原理
- 文档存入对应的分片,ES计算分片编号的过程,称为路由。
- Elasticsearch 是怎么知道一个文档应该存放到哪个分片中呢?
- 查询时,根据文档id查询文档, Elasticsearch 又该去哪个分片中查询数据呢?
- 路由算法 :shard_index(分片编号) = hash(文档id) % number_of_primary_shards(主分片个数)
假设有三个节点,三个主分片,三个副本分片
现在有个 id=5 文档要进行存储,会先会id进行hash运算得到一个数字17,17对3(分片数量)取模运算:17 % 3 = 2
最终决定存储在编号为2的分片上,即放到ES-node-3上,并且在ES-node-2节点上的副本分片上进行数据备份。
当要查询 id = 5 的文档,同样也要先进行hash计算,计算分片位置,路由到对应的分片进行数据查询。
说明:任何一个节点收到查询请求后,如果是一些词条搜索,也会根据倒排索引找到对应的id集合,再分别计算每个id的hash值,所存储的分片位置,再转发请求到分片所在的节点,最终汇总查询结果。
7.7 脑裂
何为脑裂?
- 一个正常es集群中只有一个主节点(Master),主节点负责管理整个集群。如创建或删除索引,并决定哪些分片分配给哪些节点。此外还跟踪哪些节点是集群的一部分。
- 脑裂就是一个集群出现多个主节点从而使集群分裂,使得集群处于异常状态。简单来说就是一个集群里只能有一个老大来指挥工作,如果有多个老大,就乱套了。
脑裂原因
- 网络原因:网络延迟
一般es集群会在内网部署,也可能在外网部署,比如阿里云。
内网一般不会出现此问题,外网的网络出现问题的可能性大些。 - 节点负载
主节点的角色既为master又为data。数据访问量较大时,可能会导致Master节点停止响应(假死状态)。 - JVM内存回收
当Master节点设置的JVM内存较小时,引发JVM的大规模内存回收,造成ES进程失去响应
避免脑裂
脑裂产生的原因:
- 网络原因:网络延迟较高
- 节点负载:主节点的角色既为master又为data
- JVM内存回收:JVM内存设置太小
避免脑裂:
-
网络原因:discovery.zen.ping.timeout 超时时间配置大一点。默认是3S
-
节点负载:角色分离策略
-
主节点配置:
-
数据节点配置:
-
-
JVM内存回收:修改 config/jvm.options 文件的 -Xms 和 -Xmx 为服务器的内存一半。
-
还可以在选举层面解决脑裂问题(即不让第二个老大产生):