集群内的原理
空集群
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_an-empty-cluster.html
- ElastiSearch天生就是 分布式的 , 其强大之处在于水平扩容(增加节点)。
- 一个ElasticSearch实例就是一个节点,一个节点就是一个集群(只不过是空集群)。
- 拥有相同cluster.name的节点,组成一个集群,共同抗压。
- 选举主节点,主节点管理集群级别变更,增加删除节点、增加删除索引。从节点管理文档级别的操作。
- 每个节点都知道所有文档的存储位置,请求发送到任何节点都ok。
索引
索引是保存数
据的地方,是指向1或N个具体分片的逻辑命名空间(类似mysql schema)。
索引名必须小写,不能以下划线开头,不能包含逗号。
分片
- 一个分片存储一部分数据。
- 一个分片就是一个lucence实例。
- 分片被分配到集群的各个节点里。
- 扩容缩容会移动分片,故不影响数据查询。
- 一个分片可以是 主 分片或者 副本 分片。
- 缺失主分片的索引无法正常工作。
创建索引onionli,并指向3个主分片,且每个主分片一个副本分片,可见所有分片将会被分布在两个节点上。
PUT /onionli
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
添加故障转移(运维类)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_add_failover.html
水平扩容(运维类)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_scale_horizontally.html
- 水平扩容后,每个节点的分片数更少,分片是Lucense实例,其独占资源更多,故效率更高。
- 节点数量扩容,总结点数大于主分片+副本分片数,可以运行中调整索引副本数,副本越多,检索效率也越高。
- 节点数量不变,盲目增加分片数,会降低效率。
应对故障(运维类)
- 某节点宕机,其上某索引的部分主分片丢失,该索引无法正常工作,此时集群状态为红色(存在主分片无法正常工作则红)。
- 从新选举主节点,提升副本分片为主分片,副本分片减少,此时集群状态为黄色(部分副本分片丢失或异常)。
- 等节点重启,集群尝试恢复,恢复成功后,集群状态回归绿色。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/_coping_with_failure.html
集群的健康
- green 健康
- yellow 主分片正常,部分副本分片异常
- red 存在主分片异常(某节点宕机,其上主分片丢失,缺失主分片的索引无法正常工作)
文档路由到分片
解释为何创建索引时要固定分片,文档会根据文档id进行hash后mod固定的分片数量,以确定文档位于哪个分片。所以若分片数变化,则之前分配好的文档就会都丢失了。
根据公式
shard = hash(routing) % number_of_primary_shards
所有的文档 API( get
、 index
、 delete
、 bulk
、 update
以及 mget
)都接受一个叫做 routing
的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中。
索引相关
获取索引的映射(索引中会存储哪些字段,每个字段是啥类型,是什么格式)
GET /onion/_mapping
文档元数据
_index(文档归属索引)
- 索引是保存数据的地方,是指向1或N个具体分片的逻辑命名空间(类似mysql schema)。
- 索引名必须小写,不能以下划线开头,不能包含逗号。
_type(文档类型)
- 文档虽属于同一索引,但是可以继续细分类型,_type可以用于同索引下的文档进行二次逻辑分类。
-
_type
命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符
_id
- ID 是一个字符串,当它和
_index
以及_type
组合就可以唯一确定 Elasticsearch 中的一个文档。 当你创建一个新的文档,要么提供自己的_id
,要么让 Elasticsearch 帮你生成。
对象指json,对象内部可嵌套对象。文档大部分等于对象,在存在嵌套时特指根对象。
插入文档
由于es7已经完全将type给废弃了,所以插入文档的语句中原本type的位置变为_doc或_create。
post /{index}/_doc/{id}
这种方式不论是否存在都能插入成功,不存在则纯插入,存在则更新文档和版本号。
post /{index}/_doc
这种方式系统会帮您自动创建id。
post /{index}/_create/{id}
这种方式会在插入前校验文档是否存在,存在则不插,不存在则插入,并会返回元数据和一个 201 Created
的 HTTP 响应码。
纯插
post /onion/_doc/id
{
"name":"lisi",
"birth":"2021-05-20"
}
查询文档
简单查询
拿所有字段slect *
GET /onionli/typeA/id1?pretty pretty表示json化打印
返回found=true
{
"_index": "onionli",
"_type": "typeA",
"_id": "id1",
"_version": 2,
"_seq_no": 1,
"_primary_term": 1,
"found": true,
"_source": {
"name": "28",
"sex": 1
}
}
拿部分字段select name,sex
先存入一个新文档
put /onionli/typeA/id2
{
"name":"magic",
"sex":"0",
"age":10
}
只查这个文档的name和sex两个字段
GET /onionli/typeA/id2?_source=name,sex
只拿业务数据(忽略文档元数据)
GET /onionli/typeA/id2/_source
返回
{
"name": "magic",
"sex": "0",
"age": 10
}
Query、Filter、Should、Must
https://blog.csdn.net/weixin_30446969/article/details/113491063
Query DSL 和 Filter DSL 的区别
先看案例,最外层是query,然后一个 bool 查询可以包含一个或多个查询字句 ,bool里面包含两部分:
- 查询部分 must、should、mustNot等,这部分被称为query DSL
- 过滤部分 filter,这部分被称为filter DSL
POST onion/_doc/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
],
"filter": [
{
"term": {
"name": {
"value": "lisi"
}
}
}
]
}
}
}
Query DSL 和 Filter DSL的差别如下
Query DSL
- 会进行全文检索。
- 会进行相关性检查,比如查询包含run单词,如果包含这些单词:runs、running、jog、sprint,也被视为包含run单词。
- 查询结果不会被缓存。
- 查询结果会计算分值。
除非需要搜索文本,若纯粹当数据库使用,效率更低。
Filter DSL
- 仅仅校验field与传入的待匹配值是否相等。
- 结果会缓存。
- 查询结果不会贡献算分。
所以存粹当数据库使用,filter效率更高。
Query DSL 中的must、should、must not
- must:必须匹配,贡献算分
- should:选择性匹配,贡献算分
- must_not:查询字句,必须不能匹配
- filter:必须匹配,不贡献算分
上图是一个 bool 查询,是对用户(user)进行搜索,城市必须是北京(beijing) ,性别必须是男(man),这个采用的是 filter,说明这个对算分是不会产生影响的,must_not 是一个 range 的查询:年龄大于等于 35 岁;should 里是一个数组,说明这个 should 中可以写多个条件,只要用户的名字是这两个中的一个就是满足条件的。
其实,bool 查询的子查询可以任意顺序出现,并且可以嵌套多个查询。
另外,should 的使用分两种情况:
- bool 查询中只包含 should,不包含 must 或 filter 查询:如果在 bool 查询中没有 must、filter 子句,should 中必须至少满足一条查询(可以通过 minimum_should_match 来设置满足条件的个数或者百分比)。
- bool 查询中同时包含 should 和 (must或filter) 查询:同时包含 should 和 (must或filter) 时,文档不必满足 should 中的条件,但是如果满足条件,会增加相关性算分。
查询模板
官网 https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-template.html
https://www.cnblogs.com/sanduzxcvbnm/p/12085136.html
往es里存两份文档
PUT twitter/_doc/1
{
"user" : "双榆树-张三",
"message" : "今儿天气不错啊,出去转转去",
"uid" : 2,
"age" : 20,
"city" : "北京",
"province" : "北京",
"country" : "中国",
"address" : "中国北京市海淀区",
"location" : {
"lat" : "39.970718",
"lon" : "116.325747"
}
}
PUT twitter/_doc/2
{
"user" : "虹桥-老吴",
"message" : "好友来了都今天我生日,好友来了,什么 birthday happy 就成!",
"uid" : 7,
"age" : 90,
"city" : "上海",
"province" : "上海",
"country" : "中国",
"address" : "中国上海市闵行区",
"location" : {
"lat" : "31.175927",
"lon" : "121.383328"
}
}
定义查询模板
- 使用
_scripts
端点将模板存储在集群状态中。 - 在search template中使用mustache语法。
{{#param1}}{{/param1} | 代表mybatis中的if标签,如果那个变量是false或null,则不会执行其中的片段。 |
{{#join}}array{{/join}} | 可以将数组转成逗号分隔的string |
{{#toJson}}parameter{{/toJson}} | 将map或array转成json格式 |
{
"from": {{from}}{{^from}}0{{/from}},
"size": {{size}}{{^size}}5{{/size}},
"sort": [{"price_effective_time": "asc"}],
"query": {
"bool": {
"filter": [
{
"exists": {
"field": "mer_item_no"
}
}
{{#startTime}}
,{
"range": {
"price_effective_time": {
"gte": "{{startTime}}",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
{{/startTime}}
{{#endTime}}
,{
"range": {
"price_effective_time": {
"lte": "{{endTime}}",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
{{/endTime}}
{{#midSet}}
,{
"terms": {
"merchandise_no": {{#toJson}}midSet{{/toJson}}
}
}
{{/midSet}}
{{#goodsNoSet}}
,{
"terms": {
"goods_no": {{#toJson}}goodsNoSet{{/toJson}}
}
}
{{/goodsNoSet}}
{{#merItemNoSet}}
,{
"terms": {
"mer_item_no": {{#toJson}}merItemNoSet{{/toJson}}
}
}
{{/merItemNoSet}}
{{#brandStoreSnSet}}
,{
"terms": {
"brand_store_sn": {{#toJson}}brandStoreSnSet{{/toJson}}
}
}
{{/brandStoreSnSet}}
]
}
}
}
使用模板查询
GET twitter/_search/template
{
"id": "my_search_template",
"params": {
"my_field": "city",
"my_value": "北京"
}
}
获取查询模板
GET _scripts/<templateid>
删除查询模板
DELETE _scripts/<templateid>
调试查询模板(验证模板生成的DSL)
GET _search/template
{
"id": "你的templateId",
"params": {
"startTime":"2021-05-01 11:00:00",
"endTime":"2021-08-01 11:00:00",
"midSet":["1","2"],
"goodsNoSet":["11","22"],
"merItemNoSet":["111","222"],
"brandStoreSnSet":["1111","2222"]
}
}
调试的渲染结果
{
"from": 0,
"size": 5,
"sort": [
{
"price_effective_time": "asc"
}
],
"query": {
"bool": {
"filter": [
{
"exists": {
"field": "mer_item_no"
}
},
{
"range": {
"price_effective_time": {
"gte": "2021-05-01 11:00:00",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
},
{
"range": {
"price_effective_time": {
"lte": "2021-08-01 11:00:00",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
},
{
"terms": {
"merchandise_no": [
"1",
"2"
]
}
},
{
"terms": {
"merchandise_no": [
"1",
"2"
]
}
},
{
"terms": {
"goods_no": [
"11",
"22"
]
}
},
{
"terms": {
"goods_no": [
"11",
"22"
]
}
},
{
"terms": {
"mer_item_no": [
"111",
"222"
]
}
},
{
"terms": {
"mer_item_no": [
"111",
"222"
]
}
},
{
"terms": {
"brand_store_sn": [
"1111",
"2222"
]
}
},
{
"terms": {
"brand_store_sn": [
"1111",
"2222"
]
}
}
]
}
}
}
删除文档
成功则返回200,找不到文档则返回404
DELETE /onionli/typeA/id1
批量删除
bulk批量删除(不是批量越大越好,上线前请先调优最佳批量大小)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/bulk.html
批量处理
原文链接:https://blog.csdn.net/weixin_39723544/article/details/109237175
es中的批量处理就是使用bulk进行操作。
bulk的操作类型
- create 如果文档不存在就创建,但如果文档存在就返回错误
- index 如果文档不存在就创建,如果文档存在就更新
- update 更新一个文档,如果文档不存在就返回错误
- delete 删除一个文档,如果要删除的文档id不存在,就返回错误
- 其实可以看得出来index是比较常用的。还有bulk的操作,某一个操作失败,是不会影响其他文档的操作的,它会在返回结果中告诉你失败的详细的原因。
准备操作
- 测试数据准备
- 索引及映射结构准备
PUT example
PUT example/docs/_mapping
{
"properties": {
"id": {"type": "long"},
"name": {"type": "text"},
"counter": {"type": "integer"},
"tags": {"type": "text"}
}
}
批量插入
插入的action为index。
POST example/docs/_bulk
{"index": {"_id": 1}}
{"id":1, "name": "admin", "counter":"10", "tags":["red", "black"]}
{"index": {"_id": 2}}
{"id":2, "name": "张三", "counter":"20", "tags":["green", "purple"]}
{"index": {"_id": 3}}
{"id":3, "name": "李四", "counter":"30", "tags":["red", "blue"]}
{"index": {"_id": 4}}
{"id":4, "name": "tom", "counter":"40", "tags":["orange"]}
批量修改
修改的action为update。
POST example/docs/_bulk
{"update": {"_id": 1}}
{"doc": {"id":1, "name": "admin-02", "counter":"11"}}
{"update": {"_id": 2}}
{"script":{"lang":"painless","source":"ctx._source.counter += params.num","params": {"num":2}}}
{"update":{"_id": 3}}
{"doc": {"name": "test3333name", "counter": 999}}
{"update":{"_id": 4}}
{"doc": {"name": "test444name", "counter": 888}, "doc_as_upsert" : true}
批量删除
修改的action为delete。
POST example/docs/_bulk
{"delete": {"_id": 1}}
{"delete": {"_id": 2}}
{"delete": {"_id": 3}}
{"delete": {"_id": 4}}
批量的混合操作
不过一般不推荐这种使用,项目中也用的极少。
POST _bulk
{ "index" : { "_index" : "example", "_type" : "docs", "_id" : "1" } }
{ "name" : "value11111" }
{ "delete" : { "_index" : "example", "_type" : "docs", "_id" : "2" } }
{ "create" : { "_index" : "example", "_type" : "docs", "_id" : "3" } }
{ "tags" : "value333" }
{ "update" : {"_id" : "4", "_type" : "docs", "_index" : "example"} }
{ "doc" : {"name" : "混合444"}
并发控制(高级部分)
处理冲突
https://www.elastic.co/guide/cn/elasticsearch/guide/current/version-control.html
乐观并发控制
https://www.elastic.co/guide/cn/elasticsearch/guide/current/optimistic-concurrency-control.html
部分文档更新
https://www.elastic.co/guide/cn/elasticsearch/guide/current/partial-updates.html
更新文档
es提供了put 和 update两个api进行文档更新。
update较重,是从原文档生出新文档,再删除原文档,索引新文档。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/update-doc.html
一切都是json
Mysql与es中名词的对应关系
- Index 对应 MySQL 中的 Database
Type 对应 MySQL 中的 Table- Document 对应 MySQL 中表的记录
- field对应MySQL中表的column
es在后台将每个索引划分成多个分片,分片可以在集群中不同服务器之间进行转移。
standalone模式的es,一个人就是一个集群,其集群名字默认为elasticSearch。
es文档的特点(Document的特点)
- document同时包含key:value。
- document可以嵌套包含另一个文档。
- document没有固定的字段,可以动态新增或忽略字段。
字段的特点(fields的特点)
es会特别关注并存储每个field的类型,这个类型被称为映射类型。
未归类
一个集群至少一个节点,一个节点是一个es进程。
节点可以有多个默认索引,创建新索引(库)默认产生10个分片,5个主分片(primary shard)5个副本分片(replica shard)。
一个es索引代表一个es的库,库里面包含多个lucense索引文件,每个文件包含<标签,含有标签的document>
一定要保证es的maven相关包和当前使用的es版本保持一致。
集群的健康状况为 yellow
则表示全部 主 分片都正常运行(集群可以正常服务所有请求),但是 副本分片没有全部处在正常状态。