一. ES基本使用 (es7)
1.HTTP方式
1.1.索引
1.1.1.创建索引
创建一个名为shopping的索引
请求
PUT http://127.0.0.1:9200/shopping
响应
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "shopping"
}
1.1.2.查询所有索引
查询所有索引信息
请求
GET http://127.0.0.1:9200/_cat/indices?v=
响应
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open shopping qBEYH-J-Qwyp4Oj26PYlUQ 1 1 3 0 10.3kb 10.3kb
响应字段说明
| 字段 | 含义 |
|---|---|
| health | 当前服务器健康状态: green(集群完整) yellow(单点正常、集群不完整) red(单点不正常) |
| status | 索引状态(开、关) |
| index | 索引名 |
| uuid | 索引编号 |
| pri | 主分片数量 |
| rep | 副本数量 |
| docs.count | 可用文档数量 |
| docs.deleted | 文档删除状态(逻辑删除) |
| store.size | 主分片和副分片整体占空间大小 |
| pri.store.size | 主分片占空间大小 |
1.1.3.查询单个索引
查询单个索引信息
请求
GET http://127.0.0.1:9200/shopping
响应
{
"shopping": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"creation_date": "1692944013337", //创建索引的时间戳
"number_of_shards": "1", // 主分片数量
"number_of_replicas": "1", // 副本分片数量
"uuid": "qBEYH-J-Qwyp4Oj26PYlUQ",
"version": {
"created": "7080099" // 版本号
},
"provided_name": "shopping" // 索引名称
}
}
}
}
1.1.4.删除索引
删除名称为shopping的索引
请求
DELETE http://127.0.0.1:9200/shopping
响应
{
"acknowledged": true
}
1.2.文档
1.2.1.文档创建
在shopping索引下创建文档,未指定索引唯一标识,由es自动生成
POST http://127.0.0.1:9200/shopping/_doc
请求
{
"title":"小米手机",
"category":"小米",
"images":"http://www.gulixueyuan.com/xm.jpg",
"price":3999.00
}
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "gfwjOooBJ0MTev-ZEfGK",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
在shopping索引下创建文档,指定索引唯一标识1,指定了唯一标识,此处也可以用PUT请求
POST http://127.0.0.1:9200/shopping/_doc/1
or
PUT http://127.0.0.1:9200/shopping/_doc/1
请求
{
"title":"小米手机",
"category":"小米",
"images":"http://www.gulixueyuan.com/xm.jpg",
"price":3999.00
}
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
1.2.2.文档主键查询
查询shopping索引下文档id为1的数据
GET http://127.0.0.1:9200/shopping/_doc/1
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 1,
"_primary_term": 1,
"found": true,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
}
1.2.3.文档全查询
查询shopping索引下文档的所有数据
GET http://127.0.0.1:9200/shopping/_search
响应
{
"took": 133,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "shopping",
"_type": "_doc",
"_id": "ANQqsHgBaKNfVnMbhZYU",
"_score": 1,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
},
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_score": 1,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999
}
}
]
}
}
1.2.4.文档全局修改
和新增文档一样,输入相同的 URL 地址请求,如果请求体变化,会将原有的数据内容覆盖.
POST http://127.0.0.1:9200/shopping/_doc/1
请求
{
"title":"华为手机",
"category":"华为",
"images":"http://www.gulixueyuan.com/hw.jpg",
"price":1999.00
}
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version": 2,
"result": "updated", //updated 表示数据被更新
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
1.2.5.文档局部修改
修改数据时,只修改某一条数据的局部信息
POST http://127.0.0.1:9200/shopping/_update/1
请求
{
"doc": {
"title":"小米手机",
"category":"小米"
}
}
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version": 3,
"result": "updated",//updated 表示数据被更新
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
1.2.6.文档条件查询
1.URL带参数查询
查找category为小米的文档
GET http://127.0.0.1:9200/shopping/_search?q=category:小米
2.请求体带参查询
查找category为小米的文档
GET http://127.0.0.1:9200/shopping/_search
{
"query":{
"match":{
"category":"小米"
}
}
}
3.带请求体方式的查找所有内容
GET http://127.0.0.1:9200/shopping/_search
{
"query":{
"match_all":{}
}
}
4.查询指定字段
查询文档中的title字段
GET http://127.0.0.1:9200/shopping/_search
{
"query":{
"match_all":{}
},
"_source":["title"]
}
1.2.7.文档分页查询
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"match_all":{}
},
"from":0,
"size":2
}
1.2.8.文档查询排序
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"match_all":{}
},
"sort":{
"price":{
"order":"desc"
}
}
}
1.2.9.文档多条件查询
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"bool":{
"must":[{
"match":{
"category":"小米"
}
},{
"match":{
"price":3999.00
}
}]
}
}
}
1.2.10.文档范围查询
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"bool":{
"should":[{
"match":{
"category":"小米"
}
},{
"match":{
"category":"华为"
}
}],
"filter":{
"range":{
"price":{
"gt":2000
}
}
}
}
}
}
1.2.11.全文检索
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"match":{
"category" : "小华"
}
}
}
1.2.12.完全匹配
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"match_phrase":{
"category" : "为"
}
}
}
1.2.13.高亮查询
GET http://127.0.0.1:9200/shopping/_search
请求
{
"query":{
"match_phrase":{
"category" : "为"
}
},
"highlight":{
"fields":{
"category":{} //高亮这字段
}
}
}
1.2.14.聚合查询
1.分组查询
GET http://127.0.0.1:9200/shopping/_search
请求
{
"aggs":{//聚合操作
"price_group":{ //名称,随意起名
"terms":{ //分组
"field":"price" //分组字段
}
}
}
}
2.求price的平均值
GET http://127.0.0.1:9200/shopping/_search
请求
{
"aggs":{
"price_avg":{ //名称,随意起名
"avg":{ //求平均
"field":"price"
}
}
},
"size":0
}
1.2.15.文档删除
删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)
DELETE http://127.0.0.1:9200/shopping/_doc/1
响应
{
"_index": "shopping",
"_type": "_doc",
"_id": "1",
"_version": 4,
"result": "deleted",//删除成功
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 4,
"_primary_term": 1
}
1.3.映射
1.3.1.映射
ES中的映射(mapping)相当于mysql中的table,用来约束索引下的文档字段信息。
1.3.2.创建映射
1.创建索引user
PUT http://127.0.0.1:9200/user
2.创建user索引的映射
PUT http://127.0.0.1:9200/user/_mapping
请求
{
"properties": {
"name":{
"type": "text", // text,可以被分词查询
"index": true
},
"sex":{
"type": "keyword",// keyword,不可以被分词查询,只能全匹配
"index": true
},
"tel":{
"type": "keyword",
"index": false // 代表不能被索引查询,即不能作为条件查询
}
}
}
二.ES原理
2.1.核心概念
2.1.1.索引
索引就是拥有相似特征的文档集合。在一个集群中,可以定义任意多的索引。
2.1.2.类型
- 类型是索引的一个逻辑上的分类/分区。在一个索引中,你可以定义一种或多种类型。
- 7.X版本后,默认不再支持自定义索引类型(默认类型为: _doc)
2.1.3.文档
一个文档是一个可被索引的基础信息单元,也就是一条数据。
2.1.4.字段
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。
2.1.5.映射
ES中的映射(mapping)相当于mysql中的table,用来约束索引下的文档字段信息。 如:某个字段的数据类型、默认值、分析器、是否被索引等等
ES数据类型
1.text
当一个字段需要被全文搜索(会被分词),比如产品的描述信息等。
2.keyword
当一个字段需要按照精确值进行过滤、排序、聚合等操作时, 就应该使用keyword类型。该类型的字段值不会被分析器处理(分词)
3.数字类型
byte、short、integer、long、float、double、half_float、scaled_float
4.boolean
可以使用boolean类型的(true、false)也可以使用string类型的(“true”、“false”)。
5.binary
二进制类型是Base64编码字符串的二进制值,不以默认的方式存储,且不能被搜索。
6.日期
使用双竖线||分隔指定多种日期格式,每个格式都会被依次尝试,直到找到匹配的。
curl -X PUT "localhost:9200/my_index?pretty" -H 'Content-Type: application/json' -d'
{
"mappings": {
"my_type": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
}'
2.1.6.分片
- 一个索引的数据超出硬件节点的限制或者数据量太大影响性能,可以把一个大的索引拆分成多份,每一份称之为分片。
- 每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
分片的作用
- 允许水平分割/扩展内容容量。
- 允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量。
2.1.7.副本
ES允许创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)。 副本的作用
- 在分片/节点失败的情况下,提供了高可用性。
- 复制分片从不与原/主要(original/primary)分片置于同一节点上
- 扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行。
2.2.ES节点类型
ES根据节点主要职能,大致有三种类型节点: master主节点、data数据节点、协调节点。每个节点都能设置三个类中的一个。
2.2.1.master节点
- 默认情况下任何节点都可以成为master节点。实际上master节点仅有一个, 但是可以设置多个候选的master节点。在节点的配置中设置node.master=true即可将节点设为master主节点。在当前master节点意外退出后, 集群会在候选节点中重新选举一个master主节点。
- master节点主要负责创建索引、节点健康状态监控、节点上下线等工作
2.2.2.数据节点
数据节点负责保存数据、执行数据相关操作。对节点的CPU、内存和IO要求较高。在节点的配置中设置node.data=true, node.master=false即可将节点设为数据节点。数据读写流程只和数据节点交互。
2.2.3.协调节点
协调节点用于客户端请求集群时使用,默认情况下任意节点都能成为协调节点,协调节点会在接受请求的时候根据情况转发给其他节点,并汇总结果返回给客户端,在节点的配置中设置node.data=false, node.master=false即可将节点设为协调节点。 协调节点在汇总数据时也可能需要很高CPU和内存,最好有单独协调节点
2.3.集群
2.3.1.水平扩容
总结
- 当集群中有了新的节点加入、或者节点宕机,ES会重新调整分片及副本的策略。
- 主分片在创建索引的时候分配之后,就不能在改变,这是由于ES的路由计算策略决定。
- 读操作-可以同时被主分片、副本分片所处理,所以当副本分片越多时,也能提升集群性能。
- 在运行的集群上是可以动态调整副本分片数目,从而提升集群性能。
调整分片副本数量
调整users索引的副本分片数量
PUT http://127.0.0.1:1001/users/_settings
{
"number_of_replicas" : 2
}
2.3.2.路由计算
ES通过路由计算来确定一个索引的读写是在那个主分片上
路由计算策略
shard = hash(routing) % number_of_primary_shards
1.routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。
2.routing 通过hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到余数
3.这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。
2.3.3.选举流程
2.3.3.1.ES7.X版本之前(基于Bully算法)
ES的ZenDiscovery模块负责选举,它采用Bully算法的思想进行。
Bully算法思路
- 它假定所有的节点都有一个唯一ID,使用该ID对节点进行排序。
- Leader节点就是排序的第一个节点。
Bully算法缺点: Leader节点因为负载过重假死,集群中第二位节点被选为Leader,这时原来的Leader恢复,再次被选举为Leader,然后又假死。导致集群异常。
ES的选举流程 ES针对Bully算法的缺点,做出以下优化
- ES 通过推迟选举,直到当前的 Master 失效来解决上述问题,只要当前主节点不挂掉,就不重新选主。
- 但是容易产生脑裂(双主),为此,再通过“法定得票人数过半”解决脑裂问题。在弹性搜索中,法定大小是一个可配置的参数。(一般配置成:可以成为master节点数n/2+1)
2.3.3.2.ES7.X版本开始(基于Raft算法)
Raft算法
Raft节点的三种状态
- Leader:领导者
- Candidate:候选人
- Follower:跟随者
正常情况下,集群中只有一个Leader,其他节点全是Follower。Follower 都是被动接收请求,从不主动发送任何请求。Candidate候选人是从Follower到Leader的中间状态。
Raft中引入任期(term) 的概念,每个term内最多只有一个Leader。term 在Raft算法中充当逻辑时钟的作用。服务器之间通信的时候会携带这个term,如果节点发现消息中的term小于自己的term,则拒绝这个消息;如果大于本节点的term,则更新自己的term。如果一个Candidate或者Leader发现自己的任期过期了,它会立即回到Follower状态。
Raft选举流程
- 增加当前节点本地的current term,切换到Candidate状态;
- 当前节点投自己一票,并且并行给其他节点发送RequestVote RPC (让大家投他)
然后等待其他节点的响应,会有如下三种结果:
- 如果接收到大多数服务器的选票,那么就变成Leader。成为Leader后,向其他节点发送心跳消息来确定自己的地位并阻止新的选举。
- 如果收到了别人的投票请求,且别人的term比自己的大,那么候选者退化为Follower;
- 如果选举过程超时,再次发起一轮选举;
ES的Raft实现
- ES中,候选人不先投自己,而是直接发起选举投票。这相当于候选人有投票给其它候选者的机会(比较term大小),从而避免多个节点同时成为候选人,都投自己,无法成功选举。
- ES不限制每个节点在某个Term上只能投一票,节点可投多票。这样会引起产生多个Leader的情况。
- 对于这种情况,ES的处理是让最后当选的Leader成功,作为Leader。如果收到RequestVote请求,他会无条件退出Leader状态。
2.4.读写机制
2.4.1.读机制
- 客户端发起查询请求到协调节点
- 协调节点计算数据所在的主分片及全部副本分片位置
- 为了能够负载均衡,轮询查询所有分片
- 请求转发给具体节点
- 节点返回查询结果并反馈客户端
2.4.2.写机制
- 客户端发起写入请求到协调节点
- 协调节点根据路由算法计算数据写入的主分片节点
- 转发请求到主分片节点
- 主分片保存数据,并发送数据给副本分片保存
- 副本保存完成后,主分片进行反馈结果
2.4.3.更新文档
-
因为ES使用了乐观锁,在更新某一条文档时会认为不会有其他进程对该文档进行修改, 所以不会加锁。
-
当更新某一个文档的时候,先回读取该文档, 获取version值, 然后将version和更新数据写回分片,这时候会对比分片文档中version和更新数据的中version是否相同,相同的话则更新成功version值加1, 不同的话更新失败。所以同时对一个文档进行多个更新,成功更新只能有一个。
2.5.倒排索引
- 正向索引是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程。
- 倒排索引是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id获取文档。是根据词条找文档的过程。
1.倒排索引的两个概念
- 文档:用来搜索的数据,每一条数据就是一个文档。
- 词条:对文档数据或者用户搜索数据,利用分词算法,得到具备含义的词语就是词条。
- 倒排表:用来存储词条和包含词条文档id的数据表。
2.倒排索引是对正向索引的一种特殊处理,流程如下:
- 将每一个文档的数据利用算法分词,得到一个个词条
- 创建倒排表,每行数据包括词条、词条所在文档id、位置等信息
- 因为词条唯一性,可以给词条创建索引,例如hash表结构索引
3.倒排索引的搜索流程
- 用户输入条件进行查询
- 对用户输入内容进行分词,得到相关词条
- 根据词条在倒排表中查询,得到包含词条的文档ID
- 根据文档ID到正向索引表中查找具体文档