Elasticsearch入门
基本概念
文档
elasticsearch是面向文档的,文档是所有可搜索数据的最小单位
文档会被序列化成json格式
每个文档都有一个unique id
一篇文档包含一系列字段,类似数据库表中一条记录
json文档,格式灵活,不需要预先定义格式
字段的类型可以指定或通过es自动推算
支持数组/支持嵌套
![2019101115707263107291.png](http://pic.aipp.vip/2019101115707263107291.png)
文档的元数据
元数据用于标注文档的相关信息
_index 文档所属的索引名
_type 文档所属的类型名
_id 文档唯一id
_source 文档的原始json数据
_all 整合所有字段内容到该字段,已被废除
_version 文档的版本信息
_score 相关性打分
索引
索引是文档的容器,是一类文档的结合
index体现了逻辑空间概念: 每个索引都有自己的mapping定义,用于定义包含的文档的字段名和字段类型
shard体现了物理空间的概念: 索引中的数据分散在shard上
索引的mapping于settings
mapping定义文档字段的类型
setting定义不同的数据分布
type
在7.0之前,一个index可以设置多个type
6.0开始,type已经被废弃,一个索引只能创建一个type "doc"
抽象与类比
table index
row document
column field
shema mapping
sql dsl
index相关API
1234567891011121314151617181920212223242526
#查看索引相关信息GET kibana_sample_data_ecommerce#查看索引的文档总数GET kibana_sample_data_ecommerce/_count#查看前10条文档,了解文档格式POST kibana_sample_data_ecommerce/_search{}#_cat indices API#查看indicesGET /_cat/indices/kibana*?v&s=index#查看状态为绿的索引GET /_cat/indices?v&health=green#按照文档个数排序GET /_cat/indices?v&s=docs.count:desc#查看具体的字段GET /_cat/indices/kibana*?pri&v&h=health,index,pri,rep,docs.count,mt#How much memory is used per index?GET /_cat/indices?v&h=i,tm&s=tm:desc
分布式系统
分布式系统的可用性与扩展性
高可用性
服务可用性-允许有节点停止服务
数据可用性-部分节点丢失,不会丢失数据
可扩展性
请求量提升/数据不断增长
分布式特性
es分布式架构的优点
存储的水平扩容
提高系统的可用性,部分节点停止服务,整个集群服务不受影响
es的分布式架构
不同的集群通过不同的名称来区分
通过配置文件修改或在命令行中-E cluster.name=xxx进行设定
一个集群可以有一个或多个节点
节点
节点是一个es实例
本质是一个java进程
一个机器可以永兴多个es进程
每个节点启动后,会分配一个uid,保存在data目录下
节点类型
master-eligible nodes和master node
每个节点启动后,默认就是一个master eligible节点
可以设置node.master:false禁止
master-eligible节点可以参加选主,成为master节点
当第一个节点启动时,它会将自己选举成master节点
每个节点都保存了集群的状态,只有master节点才能修改集群的状态信息
集群状态,维护了一个集群的必要信息
所有节点信息
所有索引和其相关的mapping于setting信息
分片的路由信息
任意节点都能修改信息会导致数据的不一致性
data node和coordinating node
可以保存数据的节点叫做data node。负责保存分片数据。
coordinating node负责接受client请求,将请求分发到合适的节点,最终把结果汇集到一起
每个节点默认都起到了coordinating node的职责
hot & warm node
不同硬件配置的data node,用来实现hot &warm架构,降低集群部署的成本
machine learning node
负责跑机器学习的job,用来做异常检测
配置节点类型
开发环境中一个节点可以承担多个角色
生产环境中,应该设置单一的角色的节点
分片
主分片,用以解决数据水平扩展的问题。通过主分片,可以将数据分布到集群内的所有节点上
一个分片是一个运行的lucene实例
主分片数在索引创建时指定,后续不允许修改,除非reindex
副本,用以解决数据高可用问题,分片是主分片的拷贝
副本分片数,可以动态调整
增加副本数,可以一定程度上提高服务的吞吐
![20191011157073161716134.png](http://pic.aipp.vip/20191011157073161716134.png)
分片的设定
生产环境分片的设定需要提前做好规划
分片数设置过小
导致后续无法增加节点实现水平扩展
单个分片数据量太大,导致数据重新分配耗时
分片设置过大,7.0开始默认主分片设置为1,解决over-sharding问题
影响搜索结果的相关性打分,影响通偶记结果的准确性
单个节点上过多的分片,会导致资源浪费,同时也会影响性能
集群
查看集群健康状况
GET _cluster/health
green 主分片与副本分片都正常分配
yellow 主分片全部正常分配,有副本分片未能正常分配
red 有主分片未能分配
例如当服务器磁盘容量超过85%时,去创建一个新索引
API
123456789101112131415161718192021222324
CAT Nodes API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cat-nodes.htmlget _cat/nodes?vGET /_nodes/es7_01,es7_02GET /_cat/nodes?vGET /_cat/nodes?v&h=id,ip,port,v,mCluster API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cluster.htmlGET _cluster/healthGET _cluster/health?level=shardsGET /_cluster/health/kibana_sample_data_ecommerce,kibana_sample_data_flightsGET /_cluster/health/kibana_sample_data_flights?level=shards#### cluster stateThe cluster state API allows access to metadata representing the state of the whole cluster. This includes information such asGET /_cluster/state#cluster get settingsGET /_cluster/settingsGET /_cluster/settings?include_defaults=trueCAT Shards API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cat-shards.htmlGET _cat/shardsGET _cat/shards?h=index,shard,prirep,state,unassigned.reason
文档基本操作
create
支持自动生成文档id和指定文档id两种方式
通过调用"POST /usrs/_doc"系统会自动生成id
使用PUT user/_create/1创建时,URI中显示指定_create,此时如果id文档已经存在,操作失败
get
元信息
同一个id的文档,即使被删除,version也会不断增加
找不到文档返回404
index
与create不同点在于,如果文档不存在就索引新的文档。否则现有文档会被删除,新的文档被索引,同时版本信息+1
update
update不会删除原来的文档,而是实现真正的数据更新
post方法 payload需要包含在"doc"中
123456
POST users/_update/1{ "doc":{ "albums":["Album1","Album2"] }}
bulk api
一次rest请求中对不同的索引进行操作
支持四种类型操作
index/create/update/delete
可以在uri中指定index,也可以在请求的payload中进行
操作中哦单条操作失败,并不会影响其他操作
返回结果包含了每一条操作执行的结果
批量读取-mget
批量操作,减少网络连接产生的开销,提高性能
批量查询-msearch
常见错误返回
429 集群过于繁忙
4xx 请求体格式有误
500 集群内部错误
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
############Create Document#############create document. 自动生成 _idPOST users/_doc{ "user" : "Mike", "post_date" : "2019-04-15T14:12:12", "message" : "trying out Kibana"}#create document. 指定Id。如果id已经存在,报错PUT users/_doc/1?op_type=create{ "user" : "Jack", "post_date" : "2019-05-15T14:12:12", "message" : "trying out Elasticsearch"}#create document. 指定 ID 如果已经存在,就报错PUT users/_create/1{ "user" : "Jack", "post_date" : "2019-05-15T14:12:12", "message" : "trying out Elasticsearch"}### Get Document by ID#Get the document by IDGET users/_doc/1### Index & Update#Update 指定 ID (先删除,在写入)GET users/_doc/1PUT users/_doc/1{ "user" : "Mike"}#GET users/_doc/1#在原文档上增加字段POST users/_update/1/{ "doc":{ "post_date" : "2019-05-15T14:12:12", "message" : "trying out Elasticsearch" }}### Delete by Id# 删除文档DELETE users/_doc/1### Bulk 操作#执行两次,查看每次的结果#执行第1次POST _bulk{ "index" : { "_index" : "test", "_id" : "1" } }{ "field1" : "value1" }{ "delete" : { "_index" : "test", "_id" : "2" } }{ "create" : { "_index" : "test2", "_id" : "3" } }{ "field1" : "value3" }{ "update" : {"_id" : "1", "_index" : "test"} }{ "doc" : {"field2" : "value2"} }#执行第2次POST _bulk{ "index" : { "_index" : "test", "_id" : "1" } }{ "field1" : "value1" }{ "delete" : { "_index" : "test", "_id" : "2" } }{ "create" : { "_index" : "test2", "_id" : "3" } }{ "field1" : "value3" }{ "update" : {"_id" : "1", "_index" : "test"} }{ "doc" : {"field2" : "value2"} }### mget 操作GET /_mget{ "docs" : [ { "_index" : "test", "_id" : "1" }, { "_index" : "test", "_id" : "2" } ]}#URI中指定indexGET /test/_mget{ "docs" : [ { "_id" : "1" }, { "_id" : "2" } ]}GET /_mget{ "docs" : [ { "_index" : "test", "_id" : "1", "_source" : false }, { "_index" : "test", "_id" : "2", "_source" : ["field3", "field4"] }, { "_index" : "test", "_id" : "3", "_source" : { "include": ["user"], "exclude": ["user.location"] } } ]}### msearch 操作POST kibana_sample_data_ecommerce/_msearch{}{"query" : {"match_all" : {}},"size":1}{"index" : "kibana_sample_data_flights"}{"query" : {"match_all" : {}},"size":2}### 清除测试数据#清除数据DELETE usersDELETE testDELETE test2
倒排索引
正排索引与倒排索引
![20191011157073351468491.png](http://pic.aipp.vip/20191011157073351468491.png)
倒排索引核心组成
两部分
单词词典,记录所有文档的单词,记录单词到倒排列表的关联关系
单词词典一般比较大,可以通过B+树或哈希拉链法实现,以满足高性能的插入与查询
倒排列表,记录了单词对应的文档结合,由倒排索引项组成
倒排索引项
文档ID
词频TF 该词在文档中出现的次数,用于相关性打分
位置 单词在文档中分词的位置,用于语句搜索(phrase query)
便宜 记录单词的开始结束位置,实现高亮显示
elasticsearch的倒排索引
es的json文档中的每个字段,都有自己的倒排索引
可以指定对某些字段不做索引
通过analyzer进行分词
analysis与analyzer
analysis文本分析是把全文本转换成一系列单词(term/token)的过程,也叫恩赐
analysis是通过analyzer来实现的
可使用es内置的分析器/或按需定制分析器
除了在数据写入时转换词条,匹配quey语句时候也需要用相同的分析器对查询语句进行分析
![20191011157073396692105.png](http://pic.aipp.vip/20191011157073396692105.png)
analyzer的组成
分词器是专门处理分词的组件,analyzer由三部分组成
character filters(针对原始文本处理,例如去除html)、tokenizer(按照规则切分单词)、token filter(将切分的单词进行加工,小写,删除stopwords,增加同义词)
![20191011157073408175529.png](http://pic.aipp.vip/20191011157073408175529.png)
es内置的分词器
使用_analyzer API
直接指定analyzer进行测试
指定索引字段进行测试
自定义分词器进行测试
![2019101115707342294258.png](http://pic.aipp.vip/2019101115707342294258.png)
standard analyzer
默认分词器
按词切分
小写处理
simple analyzer
按照非字母切分,非字母的都被去除
小写处理
whitespace analyzer
按空格切分
stop analyzer
相比simple analyzer多了stop filter 会把the a is等修饰词去除
keyword analyzer
不分词,直接将数据当一个term输出
pattern analyzer
通过正则表达式分词
默认\W+,非字符的符号进行分隔
language analyzer
中文分词的特点
中文橘子,切分成一个一个词(不是一个一个字)
英文中,单词有自然的空格作为分隔
一句中文,在不同的上下文,有不同的理解
一些例子
他说的确实在理/这事确定不下来
elasticsearch-plugin install analysis-icu
ik
thulac
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
#Simple Analyzer – 按照非字母切分(符号被过滤),小写处理#Stop Analyzer – 小写处理,停用词过滤(the,a,is)#Whitespace Analyzer – 按照空格切分,不转小写#Keyword Analyzer – 不分词,直接将输入当作输出#Patter Analyzer – 正则表达式,默认 \W+ (非字符分隔)#Language – 提供了30多种常见语言的分词器#2 running Quick brown-foxes leap over lazy dogs in the summer evening#查看不同的analyzer的效果#standardGET _analyze{ "analyzer": "standard", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}#simpeGET _analyze{ "analyzer": "simple", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}GET _analyze{ "analyzer": "stop", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}#stopGET _analyze{ "analyzer": "whitespace", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}#keywordGET _analyze{ "analyzer": "keyword", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}GET _analyze{ "analyzer": "pattern", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}#englishGET _analyze{ "analyzer": "english", "text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."}POST _analyze{ "analyzer": "icu_analyzer", "text": "他说的确实在理”"}POST _analyze{ "analyzer": "standard", "text": "他说的确实在理”"}POST _analyze{ "analyzer": "icu_analyzer", "text": "这个苹果不大好吃"}
searchAPI概览
URI search
在URL中使用查询参数
Request body search
使用elasticsearch提供的,基于json格式的更加完备的query domain specific language
指定查询的索引
/_search 集群上所有的索引
/index1/_search index1
/index1,index-2/_search index1和index2
/index*/_search
URI查询
使用q,置顶查询字符串
curl -XGET xxx:9200/kibana_sample/_search?q=customer_name:eddie
Request body
curl -XGET "xxx:9200/kibana_data/_search" -H 'Content-Type:application/json' -d '
{
"query":{
"match_all":{}
}
}'
搜索response
![20191011157073521159047.png](http://pic.aipp.vip/20191011157073521159047.png)
搜索的相关性relevance
搜索是用户和搜索引擎的对话
用户关心的是搜索结果的相关性
是否可以找到所有相关的内容
有多少不相关的内容被返回了
文档的打分是否合理
结合业务平衡排名
web搜索
page rank算法
不仅仅是结果
更重要是内容的可信度
电商搜索
搜索引擎扮演销售的角色
提高用户购物体验
提升网站销售业绩
去库存
衡量相关性
precision(查准率) 尽可能返回较少的无关文档
recall(查全率) 尽量返回较多的光管文档
ranking 是否能够按照相关度进行排序
precision & recall
![20191011157073545190872.png](http://pic.aipp.vip/20191011157073545190872.png)
URI Search
通过URI query实现搜索
GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s
{
"profile":true
}
q指定查询语句,使用query string syntax
df默认字段,不指定时会对所有字段进行查询
sort排序/from和size用于分页
profile可以查看查询是如何被执行的
query string syntax
指定字段 vs 泛查询
q=title:2012/q=2012
term vs phrase
beautiful mind等效于beautiful or mind
"beautiful mind"等效于beautiful and mind。phrase查询,还要求前后顺序保持一致
分组与引号
title:{beautiful and mind}
title="beautiful mind"
布尔操作
AND/OR/NOT 或者 &&/||/!
必须大写
title:(matrix NOT reloaded)
分组
+ 表示must
- 表示must not
title:(+matrix -reloaded)
范围查询
区间表示: []闭区间,{}开区间
year: {2019 TO 2018}
year: [* TO 2018]
算数符号
year: >2010
year:(>2010 && <=2018)
year:(+>2010 +<=2018)
通配符查询(通配符查询效率低,占用内存大,不建议使用)
?代表1个字符,*代表0或多个字符
title:mi?d
title:be*
正则表达
title:[bt]oy
模糊匹配与近似查询
title:befutifl~1
title:“lord rings”~2
demo
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
GET 2012&df=title&sort=year:desc&from=0&size=10&timeout=1s _search?q=GET 2012&df=title _search?q={ "profile":"true"}GET 2012 _search?q={ "profile":"true"}GET 2012&sort=year:desc&from=0&size=10&timeout=1s _search?q=title:{ "profile":"true"}GET _search?q=title:Beautiful Mind{ "profile":"true"}GET 2012 _search?q=title:{ "profile":"true"}GET "Beautiful Mind" _search?q=title:{ "profile":"true"}GET _search?q=title:(Beautiful Mind){ "profile":"true"}GET _search?q=title:(Beautiful AND Mind){ "profile":"true"}GET _search?q=title:(Beautiful NOT Mind){ "profile":"true"}GET 2BMind) _search?q=title:(Beautiful %{ "profile":"true"}GET 2002 TO 2018%7D _search?q=title:beautiful AND year:[{ "profile":"true"}GET _search?q=title:b*{ "profile":"true"}//模糊匹配&近似度匹配GET 1 _search?q=title:beautifl~{ "profile":"true"}GET "Lord Rings"~2 _search?q=title:{ "profile":"true"}
request body search
将查询语句通过http request body发送给elasticsearch
query dsl
1234567
POST /movies,404_idx/_search?ignore_unavailable=true{ "profile":true, "query":{ "match_all":{} }}
分页
123456
{ "from":10, "size":20}from从0开始,默认范围10个结果获取靠后的翻页成本较高
排序
123
{ "sort":[{"order_date":"desc"}]}
最好在数字型和日期型字段上排序
因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值
_source filtering
如果_source没有存储,那就只返回匹配的文档的元数据
_source支持使用通配符
_source["name*","order_date"]
脚本字段
用例:订单中有不同的汇率,需要结合汇率对订单价格进行排序
12345678
{ "script_fields":{ "new_field":{ "lang":"painless", "source":"doc['order_date'].value+'hello'" } }}
使用查询表达式-Match
1234567891011121314151617181920212223242526272829
GET /comments/_doc/_search{ "query":{ "match":{ "comment":"last christmas" } }}GET /comments/_doc/_search{ "query":{ "match":{ "comment":"last christmas", "operator":"AND" } }}短语查询GET /comments/_doc/_search{ "query":{ "match":{ "comment":"last christmas", "slop":1 } }}
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
#ignore_unavailable=true,可以忽略尝试访问不存在的索引“404_idx”导致的报错#查询movies分页POST /movies,404_idx/_search?ignore_unavailable=true{ "profile": true, "query": { "match_all": {} }}POST /kibana_sample_data_ecommerce/_search{ "from":10, "size":20, "query":{ "match_all": {} }}#对日期排序POST kibana_sample_data_ecommerce/_search{ "sort":[{"order_date":"desc"}], "query":{ "match_all": {} }}#source filteringPOST kibana_sample_data_ecommerce/_search{ "_source":["order_date"], "query":{ "match_all": {} }}#脚本字段GET kibana_sample_data_ecommerce/_search{ "script_fields": { "new_field": { "script": { "lang": "painless", "source": "doc['order_date'].value+'hello'" } } }, "query": { "match_all": {} }}POST movies/_search{ "query": { "match": { "title": "last christmas" } }}POST movies/_search{ "query": { "match": { "title": { "query": "last christmas", "operator": "and" } } }}POST movies/_search{ "query": { "match_phrase": { "title":{ "query": "one love" } } }}POST movies/_search{ "query": { "match_phrase": { "title":{ "query": "one love", "slop": 1 } } }}
query string&simple query string
query string
类似 URI Query
123456789
POST users/_search{ "query":{ "query_string":{ "default_field":"name", "query":"ruan AND yiming" } }}
simple query string
类似query string,但是会忽略错误的语法,同时只支持部分查询语法
不支持AND OR NOT,会当做字符串处理
Term之间默认的关系是OR,可以指定operator
支持部分逻辑
+替代AND
|替代OR
-替代NOT
dynamic mapping
什么是mapping
mapping类似数据库中schema的定义
定义索引中的字段的名称
定义字段的数据类型,例如字符串,数字,布尔。。
字段,倒排索引的相关配置
mapping会把json文档映射成lucene所需的扁平格式
一个mapping属于一个索引的type
每个文档都属于一个type
一个type有一个mapping定义
7.0开始不需要在mapping定义中指定type信息
字段的数据类型
简单类型
text/keyword
date
interger/floating
boolean
ipv4/ipv6
复杂类型
对象/嵌套类型
特殊类型
geo_point&geo_shape/percolator
什么是dynamic mapping
在写入文档时,如果索引不存在会自动创建索引
dynamic mapping的机制,使得我们无需手动定义mappings
elasticsearch会自动根据文档信息推算出字段的类型
但有时会推算的不对
当类型如果设置不对时,会导致一些功能无法正常运行,比如range查询
类型的自动识别
json类型 elasticsearch类型
字符串 匹配日期类型,设置成date
配置数字类型为float或long,该选项默认关闭
设置为text,并且增加Keyword子字段
布尔值 boolean
浮点值 float
整数 long
对象 object
数组 由第一个非空数值的类型所决定
空值 忽略
能否更改mapping的字段类型
两种情况
新增加字段
dynamic设为true时,一旦有新增字段的文档写入,mapping也同时被更新
dynamic设为false,mapping不会更新,新增字段的数据无法被索引,但是信息会出现在_source中
dynamic设置为strict,文档写入失败
对已有字段,一旦已经有数据写入,就不再支持修改字段定义
lucene实现的倒排索引,一旦生成后,就不允许修改
如果希望改变字段类型,比如reindex API,重建索引
原因
如果修改了字段的数据类型,会导致已被索引的数据无法被搜索
但是如果是新增的字段就不会有这样的影响
控制dynamic mappings
true false strict
文档可索引 YES YES NO
字段可索引 YES NO NO
mapping被更新YES NO NO
当dynamic被设置成false时,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃
当设置成strict模式时,数据写入直接出错
example
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
PUT mapping_test/_doc/1{ "firstName":"Chan", "lastName": "Jackie", "loginDate":"2018-07-24T10:29:48.103Z"}GET mapping_test/_mappingDELETE mapping_testPUT mapping_test/_doc/1{ "uid" : "123", "isVip" : false, "isAdmin": "true", "age":19, "heigh":180}GET mapping_test/_mappingPUT dynamic_mapping_test/_doc/1{ "newField":"someValue"}POST dynamic_mapping_test/_search{ "query":{ "match":{ "newField":"someValue" } }}PUT dynamic_mapping_test/_mapping{ "dynamic": false}PUT dynamic_mapping_test/_doc/10{ "anotherField":"someValue"}POST dynamic_mapping_test/_search{ "query":{ "match":{ "anotherField":"someValue" } }}get dynamic_mapping_test/_doc/10PUT dynamic_mapping_test/_mapping{ "dynamic": "strict"}PUT dynamic_mapping_test/_doc/12{ "lastField":"value"}DELETE dynamic_mapping_test
显式mapping设置
如何显式定义一个mapping
123456
PUT movies{ "mappings":{ //define your mappings here }}
自定义mapping的一些建议
可以参考API手写
为了减少输入的工作量,可以依照以下步骤
创建一个临时的index,写入一些样本数据
通过访问mapping api获得该临时文件的动态mapping定义
修改后用,使用该配置创建你的索引
删除临时索引
控制当前字段是否被索引
index 控制当前字段是否被索引。默认为true,如果设置成false,该字段不可被搜索
index options
四种不同级别的index options配置,可以控制倒排索引记录的内容
docs-记录doc id
freqs-记录doc id和term frequencies
positions-记录doc id/term frequencies/term position
offsets-doc id/term frequencies/term posistion/charactor offects
text类型默认记录为positions,其他默认为docs
记录内容越多,占用存储空间越大
null_value
需要对null值实现搜索
只有keyword类型支持设定null_value
copy_to设置
_all在7中已被copy_to所替代
满足一些特定的搜索需求
copy_to将字段的数值拷贝到目标字段,实现类似_all的作用
copy_to的目标字段不出现在_source中
数组类型
elasticsearch中不提供专门的数组类型。但是任何字段都可以包含多个相同类型的数值
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
#设置 index 为 falseDELETE usersPUT users{ "mappings" : { "properties" : { "firstName" : { "type" : "text" }, "lastName" : { "type" : "text" }, "mobile" : { "type" : "text", "index": false } } }}PUT users/_doc/1{ "firstName":"Ruan", "lastName": "Yiming", "mobile": "12345678"}POST /users/_search{ "query": { "match": { "mobile":"12345678" } }}#设定Null_valueDELETE usersPUT users{ "mappings" : { "properties" : { "firstName" : { "type" : "text" }, "lastName" : { "type" : "text" }, "mobile" : { "type" : "keyword", "null_value": "NULL" } } }}PUT users/_doc/1{ "firstName":"Ruan", "lastName": "Yiming", "mobile": null}PUT users/_doc/2{ "firstName":"Ruan2", "lastName": "Yiming2"}GET users/_search{ "query": { "match": { "mobile":"NULL" } }}#设置 Copy toDELETE usersPUT users{ "mappings": { "properties": { "firstName":{ "type": "text", "copy_to": "fullName" }, "lastName":{ "type": "text", "copy_to": "fullName" } } }}PUT users/_doc/1{ "firstName":"Ruan", "lastName": "Yiming"}GET users/_search?q=fullName:(Ruan Yiming)POST users/_search{ "query": { "match": { "fullName":{ "query": "Ruan Yiming", "operator": "and" } } }}#数组类型PUT users/_doc/1{ "name":"onebird", "interests":"reading"}PUT users/_doc/1{ "name":"twobirds", "interests":["reading","music"]}POST users/_search{ "query": { "match_all": {} }}GET users/_mapping
多字段特性及mapping中配置自定义analyzer
多字段类型
厂商名字实现精确匹配
增加一个keyword字段
使用不同的analyzer
不同语言
pinyin字段的搜索
还支持为搜索和索引置顶不同的analyzer
exact values vs full text
exact value: 包括数字、日期、具体一个自渡船 例如"apple store"
elasticsearch中的keyword
全文本,非结构化的文本数据
elasticsearch中的text
exact value不需要被分词
es为每个字段创建一个倒排索引
exact value在索引时,不需要做特殊的分词处理
![2019101215708116787507.png](http://pic.aipp.vip/2019101215708116787507.png)
自定义分词
当es自带分词器无法满足时,可以自定义分词器,通过组合不同的组件实现
charactor filter
tokenizer
token filter
charactor filter
在tokenizer之前对文本进行处理,例如增加删除替换字符。可以配置多个charactor filters。会影响tokenizer的position和offset信息
一些自带的character filters
HTML strip 去除html标签
Mapping 字符串替换
Pattern replace 正则匹配替换
tokenizer
将原始的文本按照一定的规则,切分为词(term或token)
es内置的tokenizers
whitespace/standard/uax_url_email/pattern/keyword/path hierarchy
可以用java开发插件,实现自己的tokenizer
token filters
将tokenizer输出的单词(term)进行增加、删除、修改
自带的token filters
lowrcase/stop/synonym(添加近义词)
设置一个custom analyzer
example
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
PUT logs/_doc/1{"level":"DEBUG"}GET _mappingPOST _analyze{ "tokenizer":"keyword", "char_filter":["html_strip"], "text": "<b>hello world</b>"}POST _analyze{ "tokenizer":"path_hierarchy", "text":"/user/ymruan/a/b/c/d/e"}POST _analyze{ "tokenizer": "standard", "char_filter": [ { "type" : "mapping", "mappings" : [ "- => _"] } ], "text": "123-456, I-test! test-990 650-555-1234"}//char filter 替换表情符号POST _analyze{ "tokenizer": "standard", "char_filter": [ { "type" : "mapping", "mappings" : [ ":) => happy", ":( => sad"] } ], "text": ["I am felling :)", "Feeling :( today"]}// white space and snowballGET _analyze{ "tokenizer": "whitespace", "filter": ["stop","snowball"], "text": ["The gilrs in China are playing this game!"]}// whitespace与stopGET _analyze{ "tokenizer": "whitespace", "filter": ["stop","snowball"], "text": ["The rain in Spain falls mainly on the plain."]}//remove 加入lowercase后,The被当成 stopword删除GET _analyze{ "tokenizer": "whitespace", "filter": ["lowercase","stop","snowball"], "text": ["The gilrs in China are playing this game!"]}//正则表达式GET _analyze{ "tokenizer": "standard", "char_filter": [ { "type" : "pattern_replace", "pattern" : "http://(.*)", "replacement" : "$1" } ], "text" : "http://www.elastic.co"}
index template和dynamic template
什么是index template
index template 帮助你设定mappings和settings并按照一定的规则,自动匹配到新创建的索引之上
模板仅在一个索引被新创建时才会产生作用,修改模板不会影响已创建的索引
你可以设置多个索引模板,这些设置会被merge在一起
你可以指定order的数值,控制merging的过程
index template工作方式
当一个索引被新创建时
应用es默认的settings和mappings
应用order数值低的index template中的设定
应用order高的index template中的设定,之前的设定会被覆盖
应用创建索引时,用户指定setting和mapping并覆盖之前模板中的设定
什么是dynamic template(用来影响dynamic mapping动态判断)
根据es识别的数据类型,结合字段名称,来动态设定字段类型
所有字符串类型设置成keyword,或者关闭keyword字段
is开头的字段都设置成boolean
long_开头的都设置成long类型
dynamic template是定义在某个索引的mapping中
template有一个名称
匹配规则是一个数组
为匹配到字段设置mapping
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
#数字字符串被映射成text,日期字符串被映射成日期PUT ttemplate/_doc/1{ "someNumber":"1", "someDate":"2019/01/01"}GET ttemplate/_mapping#Create a default templatePUT _template/template_default{ "index_patterns": ["*"], "order" : 0, "version": 1, "settings": { "number_of_shards": 1, "number_of_replicas":1 }}PUT /_template/template_test{ "index_patterns" : ["test*"], "order" : 1, "settings" : { "number_of_shards": 1, "number_of_replicas" : 2 }, "mappings" : { "date_detection": false, "numeric_detection": true }}#查看template信息GET /_template/template_defaultGET /_template/temp*#写入新的数据,index以test开头PUT testtemplate/_doc/1{ "someNumber":"1", "someDate":"2019/01/01"}GET testtemplate/_mappingget testtemplate/_settingsPUT testmy{ "settings":{ "number_of_replicas":5 }}put testmy/_doc/1{ "key":"value"}get testmy/_settingsDELETE testmyDELETE /_template/template_defaultDELETE /_template/template_test#Dynaminc Mapping 根据类型和字段名DELETE my_indexPUT my_index/_doc/1{ "firstName":"Ruan", "isVIP":"true"}GET my_index/_mappingDELETE my_indexPUT my_index{ "mappings": { "dynamic_templates": [ { "strings_as_boolean": { "match_mapping_type": "string", "match":"is*", "mapping": { "type": "boolean" } } }, { "strings_as_keywords": { "match_mapping_type": "string", "mapping": { "type": "keyword" } } } ] }}DELETE my_index#结合路径PUT my_index{ "mappings": { "dynamic_templates": [ { "full_name": { "path_match": "name.*", "path_unmatch": "*.middle", "mapping": { "type": "text", "copy_to": "full_name" } } } ] }}PUT my_index/_doc/1{ "name": { "first": "John", "middle": "Winston", "last": "Lennon" }}GET my_index/_search?q=full_name:John
深入搜索
基于词项和基于全文的搜索
基于词项的查询
term
term是表达语义的最小单位。搜索和利用统计语言模型进行自然语言处理都需要处理term
特点
* term level query: term query/range query/exists query/prefix query/wildcard query
* 在ES中,term查询,不对对输入做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,并且使用相关性算分公式为每个包含该词项的文档进行相关度算分-例如"apple store",如果需要完全匹配,可以使用"field.keyword"查询
* 可以通过constant score 将查询转换成一个filtering,避免算分,并利用缓存,提高性能
复合查询-constant score转为filter
将query转为filter,忽略TF-IDF计算,避免相关性算分的开销
filter可以有效利用缓存
![](http://pic.aipp.vip/20191017232434.png)
基于全文的查询
基于全文本的查询
match query/match phrase query/query string/query
特点
* 索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表
* 查询时候,会先对输入的查询进行分词,然后每个词项逐个进行底层的查询,最终将结果进行合并。并为每个文档生成一个算分-例如查询"Matrix reloaded",会查到包括Matrix或reload的所有结果
![](http://pic.aipp.vip/20191018022201.png)
Match Query Result
12345678910
POST movies/_search{ "query":{ "match":{ "title":{ "query":"Matrix reloaded" } } }}
Operator
1234567891011
POST movies/_search{ "query":{ "match":{ "title":{ "query":"Matrix reloaded", "operator":"AND" } } }}
Minimum_should_match
Match Phrase Query
精确匹配查询的短语,需要全部单词和顺序要完全一样,标点符号除外
slop 参数告诉match_phrase查询词条相隔多远扔能匹配到
1234567891011
POST movies/_search{ "query":{ "match_phrase":{ "title":{ "query":"Matrix reloaded", "slop":1 } } }}
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
DELETE productsPUT products{ "settings": { "number_of_shards": 1 }}POST /products/_bulk{ "index": { "_id": 1 }}{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }{ "index": { "_id": 2 }}{ "productID" : "KDKE-B-9947-#kL5","desc":"iPad" }{ "index": { "_id": 3 }}{ "productID" : "JODL-X-1937-#pV7","desc":"MBP" }GET /productsPOST /products/_search{ "query": { "term": { "desc": { //"value": "iPhone" "value":"iphone" } } }}POST /products/_search{ "query": { "term": { "desc.keyword": { //"value": "iPhone" //"value":"iphone" } } }}POST /products/_search{ "query": { "term": { "productID": { "value": "XHDK-A-1293-#fJ3" } } }}POST /products/_search{ //"explain": true, "query": { "term": { "productID.keyword": { "value": "XHDK-A-1293-#fJ3" } } }}POST /products/_search{ "explain": true, "query": { "constant_score": { "filter": { "term": { "productID.keyword": "XHDK-A-1293-#fJ3" } } } }}#设置 position_increment_gapDELETE groupsPUT groups{ "mappings": { "properties": { "names":{ "type": "text", "position_increment_gap": 0 } } }}GET groups/_mappingPOST groups/_doc{ "names": [ "John Water", "Water Smith"]}POST groups/_search{ "query": { "match_phrase": { "names": { "query": "Water Water", "slop": 100 } } }}POST groups/_search{ "query": { "match_phrase": { "names": "Water Smith" } }}
结构化搜索
结构化数据
结构化搜索是指对结构化数据的搜索
日期、布尔类型和数字都是结构化的
文本也可以是结构化的
如彩色笔可以有离散的颜色集合: 红、绿、蓝
一个博客可能被标记了标签,如分布式和搜索
电商网站上商品都有UPCs(通用产品吗)或其他唯一标识,他们都需要遵循严格规定的、结构化的格式
ES中的结构化搜索(使用term)
布尔、时间、日期和数字这类结构化数据: 有精确的格式,我们可以对这些格式进行逻辑操作。包括比较数字或时间范围,或判定两个值的大小
结构化的文本可以做精确匹配或部分匹配
term查询/prefix前缀查询
结构化结果只有"是"或"否"两个值
根据场景需要,可以决定结构化搜索是否打分
term对于查询是包含的关系,如果要查相等则需使用field.keyword
结构化精确匹配使用term,全文检索用match
range查询
123456789101112131415161718192021222324252627282930313233343536373839404142
GET products/_search{ "query":{ "constant_score":{ "filter":{ "range":{ "price":{ "gte":20, "lte":30 } } } } }}GET products/_search{ "query":{ "constant_score":{ "filter":{ "range":{ "date":{ "gte":"now-1d" } } } } }}//查询date字段非空的数据GET products/_search{ "query":{ "constant_score":{ "filter":{ "exists":"date" } } }}
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
#结构化搜索,精确匹配DELETE productsPOST /products/_bulk{ "index": { "_id": 1 }}{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }{ "index": { "_id": 2 }}{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }{ "index": { "_id": 3 }}{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }{ "index": { "_id": 4 }}{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }GET products/_mapping#对布尔值 match 查询,有算分POST products/_search{ "profile": "true", "explain": true, "query": { "term": { "avaliable": true } }}#对布尔值,通过constant score 转成 filtering,没有算分POST products/_search{ "profile": "true", "explain": true, "query": { "constant_score": { "filter": { "term": { "avaliable": true } } } }}#数字类型 TermPOST products/_search{ "profile": "true", "explain": true, "query": { "term": { "price": 30 } }}#数字类型 termsPOST products/_search{ "query": { "constant_score": { "filter": { "terms": { "price": [ "20", "30" ] } } } }}#数字 Range 查询GET products/_search{ "query" : { "constant_score" : { "filter" : { "range" : { "price" : { "gte" : 20, "lte" : 30 } } } } }}# 日期 rangePOST products/_search{ "query" : { "constant_score" : { "filter" : { "range" : { "date" : { "gte" : "now-1y" } } } } }}#exists查询POST products/_search{ "query": { "constant_score": { "filter": { "exists": { "field": "date" } } } }}#处理多值字段POST /movies/_bulk{ "index": { "_id": 1 }}{ "title" : "Father of the Bridge Part II","year":1995, "genre":"Comedy"}{ "index": { "_id": 2 }}{ "title" : "Dave","year":1993,"genre":["Comedy","Romance"] }#处理多值字段,term 查询是包含,而不是等于POST movies/_search{ "query": { "constant_score": { "filter": { "term": { "genre.keyword": "Comedy" } } } }}#字符类型 termsPOST products/_search{ "query": { "constant_score": { "filter": { "terms": { "productID.keyword": [ "QQPX-R-3956-#aD8", "JODL-X-1937-#pV7" ] } } } }}POST products/_search{ "profile": "true", "explain": true, "query": { "match": { "price": 30 } }}POST products/_search{ "profile": "true", "explain": true, "query": { "term": { "date": "2019-01-01" } }}POST products/_search{ "profile": "true", "explain": true, "query": { "match": { "date": "2019-01-01" } }}POST products/_search{ "profile": "true", "explain": true, "query": { "constant_score": { "filter": { "term": { "productID.keyword": "XHDK-A-1293-#fJ3" } } } }}POST products/_search{ "profile": "true", "explain": true, "query": { "term": { "productID.keyword": "XHDK-A-1293-#fJ3" } }}#对布尔数值POST products/_search{ "query": { "constant_score": { "filter": { "term": { "avaliable": "false" } } } }}POST products/_search{ "query": { "term": { "avaliable": { "value": "false" } } }}POST products/_search{ "profile": "true", "explain": true, "query": { "term": { "price": { "value": "20" } } }}POST products/_search{ "profile": "true", "explain": true, "query": { "match": { "price": "20" } } }}POST products/_search{ "query": { "constant_score": { "filter": { "bool": { "must_not": { "exists": { "field": "date" } } } } } }}
搜索的相关性算分
相关性和相关性算分
相关性-Relevance
搜索的相关性算分,描述了一个文档和查询语句匹配的程度。ES会对每个匹配查询条件的结果进行算分_score
打分的本质是排序,需要把最符合用户需求的文档排在前面。ES5前默认相关性算分采用TF-IDF,现在采用BM25
词频(TF)
Term Frequency: 检索词在一篇文档中出现的频率
检索词出现的次数除以文档的总字数
度量一条查询和结果文档相关性的简单方法: 简单将搜索中每一个词的TF进行相加
TF(区块链)+TF(的)+TF(应用)
Stop Word(停用词)
"的"在文档中出现了很多次,但是对贡献相关度几乎没什么用处,不应该考虑他们的TF
逆文档频率IDF
DF: 检索词在所有文档中出现的频率
"区块链"在相对比较少的文档中出现
"应用"在相对比较多的文档中出现
"stop word"在大量文档中出现
inverse document frequency:简单说 = log(全部文档数/检索词出现过的文档总数)
TF-IDF本质就是将TF求和变成加权求和
TF(区块链)*IDF(区块链) + TF(的)*IDF(的)+TF(应用)*IDF(应用)
![](http://pic.aipp.vip/20191023164440.png)
TF-IDF的概念
TF-IDF被公认为是信息检索领域最重要的发明
现代搜索引擎,对TF-IDF进行了大量细微的优化
Lucene中的TF-IDF评分公式
![](http://pic.aipp.vip/20191023165257.png)
BM25
从ES5开始,默认算法改为BM25
和经典的TF-IDF相比,当TF无限增加时,BM25算分会趋于一个数值
![](http://pic.aipp.vip/20191023165430.png)
定制similarity
相似度算分定制
通过explain API查看TF-IDF
在ES中任意查询可以增加explain参数,可以帮助了解查询是如何打分的
boosting relevance
boosting是控制相关度的一种手段
索引,字段或查询子条件
参数boost的含义
当boost>1时,打分的相关度相对性提升
当0<boost<1时,打分的权重相对性降低
当boost<0时,贡献负分
Query & filtering与多字符串多字段查询
Query Context & Filter Context
高级搜索的功能: 支持多项文本输入,针对多个字段进行搜索
搜索引擎一般也提供基于时间,价格等条件的过滤
在ES中,有Query和Filter两种不同的Context
* Query Context: 相关性算分
* Filter Context: 不需要算分(Yes or No),可以利用Cache,获得更好的性能
条件组合
假设要搜索一部电影,包含以下条件
评论中包含了Guitar,用户打分高于3分,同时上映时间要在1993到2000年之间
这个搜索其实包含三段逻辑,针对不同的字段
评论字段中要包含Guitar/用户评分大于3/上映时间需要在给定的范围
同时包含这三个逻辑,并且有比较好的性能?
复合查询: bool Query
bool查询
一个bool查询,是一个或多个查询子句的组合
总共包括4种子句。其中2种会影响算分,2种不影响算分
相关性并不只是全文本检索的专利。也适用于yes|no的子句,匹配的子句越多,相关性评分越高。如果多条查询子句被合并为一条符合查询语句,比如bool查询,则每个查询子句计算得出的评分会被合并到总的相关性评分中
must 必须匹配,贡献算分
should 选择性匹配,贡献算分
must_not filter_context查询语句,必须不能匹配,不贡献算分
filter filter-context必须匹配,不贡献算分
bool查询语法
1234567891011121314151617181920212223
POST /products/_search{ "query":{ "bool":{ "must":{ "term":{"price":"30"} }, "filter":{ "term":{"avaliable":"true"} }, "must_not":{ "range":{ "price":{"lte":10} } }, "should":[ {"term"<{"productId.keyword":"JODL-X-1937"}}, {"term"<{"productId.keyword":"JODL-X-19X8"}} ], "minimum_should_match":1 } }}
子查询可以任意顺序出现
可以嵌套多个查询
如果你的bool查询中,没有must条件,should中必须至少满足一条查询
如何解决结构化查询-”包含而不是相等“的问题
123456789101112
POST movies/_search{ "query":{ "constant_score":{ "filter":{ "term":{ "genre.keyword":"Comedy" } } } }}
Filter Context-不影响算分
Query Context-影响算分
bool嵌套
查询语句的结构,会对相关度算分产生影响
同一层级下的竞争字段,具有相同的权重
通过嵌套bool查询,可以改变对算分的影响
控制字段的boosting
Boosting是控制相��度的一种手段
索引,字段或查询子条件
参数boost的含义
当boost>1时,打分的相关度相对性提高
当0<boost<1时,打分的权重相对性降低
当boost<0时,贡献负分
Not Quite Not
要求苹果公司的产品信息优先
查询分类
terms查询用于结构化数据查询,全文用match查询,而bool属于一种符合查询,可以结合terms查询和match查询
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
POST _bulk{ "index": { "_id": 1 }}{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }{ "index": { "_id": 2 }}{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }{ "index": { "_id": 3 }}{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }{ "index": { "_id": 4 }}{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }POST _search{ "query": { "bool" : { "must" : { "term" : { "price" : "30" } }, "filter": { "term" : { "avaliable" : "true" } }, "must_not" : { "range" : { "price" : { "lte" : 10 } } }, "should" : [ { "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } }, { "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } } ], "minimum_should_match" :1 } }}POST _bulk{ "index": { "_id": 1 }}{ "title" : "Father of the Bridge Part II","year":1995, "genre":"Comedy","genre_count":1 }{ "index": { "_id": 2 }}{ "title" : "Dave","year":1993,"genre":["Comedy","Romance"],"genre_count":2 }POST _search{ "query": { "bool": { "must": [ {"term": {"genre.keyword": {"value": "Comedy"}}}, {"term": {"genre_count": {"value": 1}}} ] } }}POST _search{ "query": { "bool": { "filter": [ {"term": {"genre.keyword": {"value": "Comedy"}}}, {"term": {"genre_count": {"value": 1}}} ] } }}POST _search{ "query": { "bool" : { "filter": { "term" : { "avaliable" : "true" } }, "must_not" : { "range" : { "price" : { "lte" : 10 } } } } }}POST _bulk{ "index": { "_id": 1 }}{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }{ "index": { "_id": 2 }}{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }{ "index": { "_id": 3 }}{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }{ "index": { "_id": 4 }}{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }POST _search{ "query": { "bool": { "should": [ { "term": { "productID.keyword": { "value": "JODL-X-1937-#pV7"}} }, {"term": {"avaliable": {"value": true}} } ] } }}POST _search{ "query": { "bool": { "must": { "term": { "price": "30" } }, "should": [ { "bool": { "must_not": { "term": { "avaliable": "false" } } } } ], "minimum_should_match": 1 } }}POST _search{ "query": { "bool" : { "must" : { "term" : { "price" : "30" } }, "filter": { "term" : { "avaliable" : "true" } }, "must_not" : { "range" : { "price" : { "lte" : 10 } } }, "should" : [ { "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } }, { "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } } ], "minimum_should_match" :2 } }}POST _search{ "query": { "bool": { "should": [ { "term": { "text": "brown" }}, { "term": { "text": "red" }}, { "term": { "text": "quick" }}, { "term": { "text": "dog" }} ] } }}POST _search{ "query": { "bool": { "should": [ { "term": { "text": "quick" }}, { "term": { "text": "dog" }}, { "bool":{ "should":[ { "term": { "text": "brown" }}, { "term": { "text": "brown" }}, ] } } ] } }}DELETE blogsPOST _bulk{ "index": { "_id": 1 }}{"title":"Apple iPad", "content":"Apple iPad,Apple iPad" }{ "index": { "_id": 2 }}{"title":"Apple iPad,Apple iPad", "content":"Apple iPad" }POST blogs/_search{ "query": { "bool": { "should": [ {"match": { "title": { "query": "apple,ipad", "boost": 1.1 } }}, {"match": { "content": { "query": "apple,ipad", "boost": } }} ] } }}DELETE newsPOST _bulk{ "index": { "_id": 1 }}{ "content":"Apple Mac" }{ "index": { "_id": 2 }}{ "content":"Apple iPad" }{ "index": { "_id": 3 }}{ "content":"Apple employee like Apple Pie and Apple Juice" }POST news/_search{ "query": { "bool": { "must": { "match":{"content":"apple"} } } }}POST news/_search{ "query": { "bool": { "must": { "match":{"content":"apple"} }, "must_not": { "match":{"content":"pie"} } } }}POST news/_search{ "query": { "boosting": { "positive": { "match": { "content": "apple" } }, "negative": { "match": { "content": "pie" } }, "negative_boost": 0.5 } }}
单字符串多字段查询:Dis Max Query
单字符串查询的实例
分布式特性及分布式搜索的机制
集群分布式模型与脑裂问题
分布式特性
es分布式架构带来的好处
存储的水平扩容,支持PB级数据
提高系统的可用性,部分节点停止服务,整个集群的服务不受影响
es的分布式架构
不同的集群通过不同的名字来区分,默认"elasticsearch"
通过配置文件修改,或在命令行中-E cluster.name=cname进行设定
节点
节点是一个es的实例,其本质就是一个java进程,一台机器可以运行多个es进程
每个节点都有名字,通过配置文件配置,或者启动时候-E node.name=nname指定
每一个节点启动后,会分配一个UID,保存在data目录下
集群状态
集群状态信息(cluster state),维护了一个集群中,必要的信息
所有的节点信息
所有的索引和其相关的mapping与setting信息
分片的路由信息
在每个节点上都保存了集群的状态信息
但是,只有master节点才能修改集群的状态信息,并负责同步给其它节点
因为,任意节点都能修改信息会导致cluster state信息不一致
节点类型
coordinating node(协调节点)
处理请求的节点,叫coordinating node
路由请求到正确的节点,例如创建索引的请求,需要路由到master节点
所有节点默认都是coordinating node
通过将其他类型设置成false,使其成为dedicated coordinating node
data node
可以保存数据的节点,叫做data node
节点启动后,默认就是数据节点。可以设置ndoe.data:false禁止
data node的职责
保存分片数据。在数据扩展上起至关重要的作用(由master node决定如何把分片分发到数据节点上)
通过增加数据节点
可以解决数据水平扩展和解决数据单点问题
master node
master node的职责
处理创建,删除索引等请求/决定分片被分配到哪个结点/负责索引的创建与删除
维护更新cluster state
master node的最佳实践
master 节点非常重要,在部署上需要考虑解决单点的问题
为一个集群设置多个master节点/每个节点只承担master的单一角色
master eligible nodes
选主流程
互相ping对方,node id低的会成为被选举的节点
其它节点会加入集群,但是不承担master节点的角色。一旦发现被选中的主节点丢失,就会选举出新的master节点
脑裂问题
split-brain,分布式系统的经典网络问题,当出现网络问题,一个节点和其他结点无法连接
node2 和 node 3会重新选举master
node 1 自己还是作为master,组成一个集群,同时更新cluster state
导致2个master,维护不同的cluster state。当网络恢复时,无法选择正确恢复
![20191015157111504988545.png](http://pic.aipp.vip/20191015157111504988545.png)
如何避免脑裂问题
限定一个选举条件,设定quorum(仲裁),只有master eligible节点数大于quorum时,才能进行选举
quorum=(master节点总数/2)+1
当3个master eligible时,设置discovery.zen.minimum_master_nodes为2,即可避免脑裂
从7.0开始,无需设置这个配置
移除minimum_master_nodes参数,让elasticsearch自己选择可以行程仲裁的节点
典型的主节点选举现在只需要很短的时间就可以完成。集群的伸缩变得更安全、更容易,并且可能造成丢失数据的系统配置项更少了
节点更清楚地记录他们的状态,有助于诊断为什么他们不能加入集群或为什么无法选举出主节点
配置节点类型
一个节点默认情况下是一个master eligible,data and ingest node
节点类型 配置参数 默认值
master eligible node.master true
data node.data true
ingest node.ingest true
coordinating only 无 设置上面三个参数全部为false
machine learning node.ml true(需要enable x-pack)
分片与集群的故障转移
replica shard-提高数据可用性
数据可用性
通过引入副本分片(replica shard)提高数据的可用性。一旦主分片丢失,副本分片可以promote成为主分片。副本分片数可以动态调整每个节点上都独有完备的数据。如果不设置副本分片,一旦出现节点硬件故障,就有可能造成数据丢失
提升系统的读取性能
副本分片由主分片(primary shard)同步。通过增加replica个数,一定程度可以提高读取吞吐量
分片数的设定
如何规划一个索引的主分片和副本分片数
主分片数过小: 例如创建了一个primary shard的index
如果该索引增长很快,集群无法通过增加节点实现对这个索引的数据扩展
主分片数设置过大: 导致单个shard容量很小,引发一个节点上有过多分片,影响性能
副本分片数设置过多: 会降低集群整体的写入性能
分片与集群
单节点集群
副本无法分片,集群状态黄色,副本分片必须与主分片分配在不同的节点上
增加一个节点
集群状态转为绿色
集群具备故障转移能力
尝试着将replica设置成2和3,查看集群的状况
![2019101515711162565698.png](http://pic.aipp.vip/2019101515711162565698.png)
再增加一个节点
集群具备故障转移能力
master节点会决定分片分配到哪个结点
通过计算节点,提高集群的计算能力
集群健康状态
green : 健康,所有主分片和副本分片都可用
yellow: 亚健康,所有主分片可用,部分副本分片不可用
red: 不健康状态,部分主分片不可用
故障转移
3个节点共同组成。包含了1个索引,索引设置了3个primary shard和1个 replica
节点1是master节点,节点意外出现故障。集群重新选举master节点
node3 上的R0提升成P0,集群变黄
R0和R1分配,集群变绿
![20191015157111647617237.png](http://pic.aipp.vip/20191015157111647617237.png)
example
1234567
PUT tmdb{ "settings":{ "number_of_shards":3, "number_of_replicas":1 }}
文档分布式存储
文档存储在分片上
文档会存储在具体的某个主分片和副本分片上: 例如文档1,会存储在P0和R0分片上
文档到分片的映射算法
确保文档能均匀分布在分片上,充分利用硬件资源,避免部分机器空闲,部分机器繁忙
潜在的算法
随机/Round Robin。当查询文档1,分片数很多,需要多次查询才可能查到文档1
维护文档到分片的映射关系,当文档数据量大的时候,维护成本高
实时计算,通过文档1,自动算出,需要去哪个分片上获取文档
文档到分片的路由算法
shard = hash(_routing)%number_of_primary_shards
hash算法确保文档均匀分散到分片中
默认的_routing值是文档id
可以自行制定routing数值,例如用相同国家的商品,都分配到指定的shard
设置index settings后,primary数不能随意修改的根本原因
更新文档
![20191015157111727718955.png](http://pic.aipp.vip/20191015157111727718955.png)
> 注意区分更新文档和更新索引
删除一个文档
![20191015157111748437920.png](http://pic.aipp.vip/20191015157111748437920.png)
分片及其生命周期
分片的内部原理
分片(shard)是ES中最小的工作单元,即一个lucene的index
一些问题
为什么ES的搜索是近实时的(1秒后被搜到)
ES如何保证在断电时数据也不会丢失
为什么删除文档,并不会立即释放空间
倒排索引的不可变性
倒排索引采用immutable design,一旦生成,不可更改
不可变性带来的好处
无需考虑并发写文件的问题,避免了锁机制带来的性能问题
一旦读入内核的文件系统缓存,便留在那里。只要文件系统由足够的空间,大部分请求就会直接请求内存,不会命中磁盘,提升了很大的性能
缓存容易生成和维护数据可以被压缩
不可变更性,带来了的挑战: 如果需要让一个新的文档可以被搜索,需要重建整个索引
lucene index
在lucene中,单个倒排索引文件被称为segment。segment是自包含的,不可变更的。多个segments汇总在一起,称为lucene的index,其对应的就是ES中的shard
当有新文档写入时,会生成新segment,查询时会同时查询所有segments,并对结果汇总。lucene中有一个文件,用来记录所有segments信息,叫做commit point。
删除的文档信息,保存在".del"文件中
segment采用不可变设计。在更新或新建文档时,先标记删除,然后创建新文档。flush的时候从标记的.del中删掉
segment会定期merge,合并成一个,同时会删除已删除的文档
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zcw0warzj30jw0fadib.jpg)
什么是refresh
* 将index buffer写入segment的过程叫refresh。refresh不执行fsync操作
refresh频率: 默认1秒发生一次,可通过index.frefresh_interval配置。refresh后,数据就可以被搜索到了。这也是为什么es被称为近实时搜索
* 如果系统由大量的数据写入,那就会产生很多的segment
* index buffer被占满时,会触发refresh,默认值为jvm的10%
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zd1mnnc0j314i0pujwg.jpg)
什么是transaction log
* segment写入磁盘的过程相对耗时,借助文件系统缓存,refresh时,先将segment写入缓存以开放查询
* 为保证数据不丢失。所以在index文档时,同时写transaction log,高版本开始,transaction log默认落盘。每个分片有一个transaction log
* 在es refresh时,index buffer被清空,transaction log不会清空
* es断电时重启时系统会从transactionlog中把数据recover
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zddq7r1kj30va0u0don.jpg)
什么是flush
es flush & lucene commit
flush调用时会做以下几件事情
调用refresh,index buffer清空并refresh
调用fsync,将缓存中的segments写入磁盘
清空(删除)transaction log
flush操作默认30分钟调用一次
transaction log满(默认512MB)时,也会触发flush操作
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zdh5wzamj30z80u0jup.jpg)
merge
segments很多,需要被定期合并,这就需要merge操作
减少segments,将多个segment合并为一个/对.del中的文件做真正删除
ES和lucene会自动进行merge操作
如果需要人为强制merge,可以使用`POST my_index/_forcemerge`
剖析分布式查询及相关性算分
分布式搜索的运行机制
elasticsearch的搜索,会分两阶段进行
第一阶段-query
第二阶段-fetch
query-then-fetch
query阶段
用户发出搜索请求到ES节点。节点收到请求后,会以coordinating节点的身份,在6个主副分片中随机选择3个分片,发送查询请求
被选中的分片执行查询,进行排序。然后,每个分片都会返回From+Size个排序后的文档id和排序值给coordinating节点
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zejwdccoj30u00u7wm1.jpg)
fetch阶段
coordinating node会将query阶段,从每个分片获取的排序后的文档id列表重新进行排序。选择From到From+Size个文档的Id
以multi get请求的方式,到相应的分片获取详细的文档数据
query then fetch
性能问题
每个分片上需要查的文档个数 = from+size
最终协调节点需要处理: number_of_shard*(from+size)
深度分页
相关性算分
每个分片都是基于自己的分片上的数据进行相关性计算。这会导致打分偏离的情况,特别是数据量很小时,相关性算分在分片之间的相互独立。当文档总数很少的情况下,如果主分片大于1,,主分片数越多,相关性算分会越不准
解决算分不准的方法
数据量不大时,可以将主分片设置为1
当数据量足够大时,只要保证文档均匀分散在各个节点上,结果一般就不会出现偏差
使用DFS Query Then Fetch
搜索的URL中指定参数"_search?search_type=dfs_query_then_fetch"
到每个分频啊把个分片的词频和文件频率进行搜集,然后完整的进行一次相关性算分,耗费更多的CPU和内存,执行性能低下,一般不建议使用
example
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
DELETE messagePUT message{ "settings": { "number_of_shards": 20 }}GET messagePOST message/_doc?routing=1{ "content":"good"}POST message/_doc?routing=2{ "content":"good morning"}POST message/_doc?routing=3{ "content":"good morning everyone"}POST message/_search{ "explain": true, "query": { "match_all": {} }}POST message/_search{ "explain": true, "query": { "term": { "content": { "value": "good" } } }}POST message/_search?search_type=dfs_query_then_fetch{ "query": { "term": { "content": { "value": "good" } } }}
排序及Doc Values & Fielddata
排序
elasticsearch默认采用相关性算分对结果进行降序排序
可以通过设定sorting参数,自行设定排序
如果不指定_score,算分为Null
12345678910111213141516
POST /index/_search{ "size":5, "query":{ "match_all":{ } }, "sort":[ { "order_date":{ "order":"desc" } } ]}
排序的过程
排序是针对字段原始内容进行的。倒排索引无法发挥作用
需要用到正排索引,通过文档id和字段快速得到字段原始内容
es有两种实现方法
* fielddata
* doc values(列式存储,对text类型无效)
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zyr3t63aj31yv0u0dw9.jpg)
关闭doc values
默认启用,可以通过mapping设置关闭
增加索引速度/减少磁盘空间
如果重新打开,需要重建索引
什么时候需要关闭
明确不需要做排序及聚合分析
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
#单字段排序POST /kibana_sample_data_ecommerce/_search{ "size": 5, "query": { "match_all": { } }, "sort": [ {"order_date": {"order": "desc"}} ]}#多字段排序POST /kibana_sample_data_ecommerce/_search{ "size": 5, "query": { "match_all": { } }, "sort": [ {"order_date": {"order": "desc"}}, {"_doc":{"order": "asc"}}, {"_score":{ "order": "desc"}} ]}GET kibana_sample_data_ecommerce/_mapping#对 text 字段进行排序。默认会报错,需打开fielddataPOST /kibana_sample_data_ecommerce/_search{ "size": 5, "query": { "match_all": { } }, "sort": [ {"customer_full_name": {"order": "desc"}} ]}#打开 text的 fielddataPUT kibana_sample_data_ecommerce/_mapping{ "properties": { "customer_full_name" : { "type" : "text", "fielddata": true, "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } } }}#关闭 keyword的 doc valuesPUT test_keywordPUT test_keyword/_mapping{ "properties": { "user_name":{ "type": "keyword", "doc_values":false } }}DELETE test_keywordPUT test_textPUT test_text/_mapping{ "properties": { "intro":{ "type": "text", "doc_values":true } }}DELETE test_textDELETE temp_usersPUT temp_usersPUT temp_users/_mapping{ "properties": { "name":{"type": "text","fielddata": true}, "desc":{"type": "text","fielddata": true} }}Post temp_users/_doc{"name":"Jack","desc":"Jack is a good boy!","age":10}#打开fielddata 后,查看 docvalue_fields数据POST temp_users/_search{ "docvalue_fields": [ "name","desc" ]}#查看整型字段的docvaluesPOST temp_users/_search{ "docvalue_fields": [ "age" ]}
分页与遍历: from,size,search after & scroll API
From/Size
默认情况下,查询按照相关度算分排序,返回前10条记录
容易理解的分页方案
From开始位置 Size期望获取文档的总数
分布式系统中深度分页的问题
ES天生就是分布式的。查询信息,但是数据分别保存在多个分片,多台机器上,ES天生就需要满足排序的需求(按照相关性算分)
当一个查询: From=990,Size=10
* 会在每个分片上都获取1000个文档。然后通过Coordinating Node聚合所有结果。最后再通过排序选取前1000个文档
* 页数越深,占用内存越多。为了避免深度分页带来的内存开销。ES有一个设定,默认限定到10000个文档
index.max_result_window
超过后报错
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zyyh78a9j30j60bign8.jpg)
Search After避免深度分页的问题
避免深度分页的性能问题,可以实时获取下一页文档信息
不支持指定页数
只能往下翻
第一步搜索需要制定sort,并且保证值是唯一的(可以通过加入_id保证唯一性)
然后使用上一次,最后一个文档的sort值进行查询
123456789
POST users/_serach{ ... "search_after":[13,"aA0vYGsBrR8X3IP7_wrd"], "sort":[ {"age":"desc"}, {"_id":"asc"} ]}
Search After是如何解决深度分页的问题
假定size是10
当查询990-1000
通过唯一排序值定位,将每次要处理的文档数都控制在10
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zz6qpx5sj30kg0dimyq.jpg)
Scroll API
创建一个快照,有新的数据写入以后,无法被查询到
每次查询后,输入上一次的scroll Id
不同搜索类型和使用场景
Regular
需要实时获取顶部部分文档。例如查询最新的订单
Scroll
需要全部文档,例如导出全部数据
Pagination
From和Size
如果需要深度分页,则选用Search After
example
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
POST tmdb/_search{ "from": 10000, "size": 1, "query": { "match_all": { } }}#Scroll APIDELETE usersPOST users/_doc{"name":"user1","age":10}POST users/_doc{"name":"user2","age":11}POST users/_doc{"name":"user2","age":12}POST users/_doc{"name":"user2","age":13}POST users/_countPOST users/_search{ "size": 1, "query": { "match_all": {} }, "sort": [ {"age": "desc"} , {"_id": "asc"} ]}POST users/_search{ "size": 1, "query": { "match_all": {} }, "search_after": [ 10, "ZQ0vYGsBrR8X3IP75QqX"], "sort": [ {"age": "desc"} , {"_id": "asc"} ]}#Scroll APIDELETE usersPOST users/_doc{"name":"user1","age":10}POST users/_doc{"name":"user2","age":20}POST users/_doc{"name":"user3","age":30}POST users/_doc{"name":"user4","age":40}#5m表示5分钟POST /users/_search?scroll=5m{ "size": 1, "query": { "match_all" : { } }}POST users/_doc{"name":"user5","age":50}POST /_search/scroll{ "scroll" : "1m", "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAWAWbWdoQXR2d3ZUd2kzSThwVTh4bVE0QQ=="}
处理并发读写操作
并发控制的必要性
两个Web程序同时更新某个文档,如果缺乏有效的并发,会导致更改的数据丢失
悲观并发控制
假定有变更冲突的可能。会对资源加锁,防止冲突。例如数据库行锁
乐观并发控制
假定冲突不会发生,不会阻塞正在尝试的操作。如果数据在读写中被修改,更新将会失败。应用程序决定如何解决冲突,例如重新更新,使用新的数据,或将错误报告给客户
ES采用的是乐观并发控制
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zzrwx2fkj30g40f20ug.jpg)
ES的乐观并发控制
* ES中的文档是不可变更的。如果你更新一个文档,会将文档标记为删除,同时增加一个全新的文档。同时文档的version字段加1
* 内部版本控制
if_seq_no + if_primary_term
* 使用外部版本(使用其他数据库作为主要数据存储)
version+version_type = external
![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zztuec0gj30dk0e477h.jpg)
每个文档都有_seq_no、_primary_term
1234
PUT test/_ /3KD1tm0BnzsfJa1YdAJp?if_seq_no=1&if_primary_term=2{ "count":"2"}
example
12345678910111213141516171819202122
DELETE productsPUT productsPUT products/_ /1{ "title":"iphone", "count":100}GET products/_ /1PUT products/_ /1?if_seq_no=1&if_primary_term=1{ "title":"iphone", "count":100}PUT products/_ /1?version=30000&version_type=external{ "title":"iphone", "count":100}
深入聚合分析
聚合分析简介
什么是聚合(aggregation)
es除搜索外,还提供了针对es数据进行统计分析的功能
实时性高
通过聚合,我们回得到一个数据的概览,是分析和总结全套的数据,而不是寻找单个文档
尖沙咀和香港岛的客房数量
不同的价格区间,可预订的经济性酒店和五星级酒店的数量
高性能,只需要一条语句,就可以从elasticsearch得到分析结果
无需在客户端自己去实现分析逻辑
可视化报表-聚合分析
公司程序员的工作岗位分布
公司采用的编程框架分布
公司员工薪水拼布
客户的地理位置分布
订单的增长情况等
集合的分类
bucket aggregation 一些满足特定条件的文档的集合
metric aggregation 一些数学计算,可以对文档字段进行统计分析
pipeline aggregation 对其他的聚合结果进行二次聚合
matrix aggregation 支持对多个字段的操作并提供一个结果矩阵
bucket & metric
bucket 一组满足条件的文档 group
metric 一些系列的统计方法
![20191012157081367936349.png](http://pic.aipp.vip/20191012157081367936349.png)
bucket
elasticsearch提供了很多类型的bucket,帮助你用多种方式划分文档
term&range(时间/年龄区间/地理位置)
metric
metric会基于数据集计算结果,除了支持在字段上进行计算,同样也支持在脚本(painless script)产生的结果之上进行计算
大多数metric是数学计算,仅输出一个值
min/max/sum/avg/cardinality
部分metric支持数据多个值
stats/percentiles/percentile_ranks
bucket示例
![20191012157081389824477.png](http://pic.aipp.vip/20191012157081389824477.png)
加入metrics
![20191012157081397711966.png](http://pic.aipp.vip/20191012157081397711966.png)
嵌套
![20191012157081406761072.png](http://pic.aipp.vip/20191012157081406761072.png)
生产环境中的集群运维
生产环境常用配置和上线清单
Development vs Production Mode
从ES 5开始支持这两种运行模式
ES是通过http.host和transport.bind_host来判断模式
Bootstrap Checks
集群运行在生产模式时,启动必须通过所有Bootstrap检测,否则会启动失败
Boostrap Checks可分为两类 JVM & Linux Checks.
JVM设定
从ES6开始,只支持64位JVM
配置config/jvm.options
避免修改默认配置
将内存Xms和Xmx设置成一样,避免heap resize时引发停顿
Xms设置不要超过物理内存的50%(因为需要把剩下的50%交给lucene进行全文检索);单个节点上,最大内存建议不要超过32G内存
生产环境,JVM必须使用Server模式
关闭JVM Swapping
集群参数设定
静态设置和动态设置
* 静态配置文件尽量简洁: 按照文档设置所有相关系统参数.elasticsearch.yml配置文件中尽量只写必备参数
其他设置项可以通过API动态设置。动态设定分transient和persistent两种,都会覆盖elasticsearch.yaml中的设置
* transient在集群中期后会丢失
* persistent在集群重启后不会丢失
配置优先级
![](http://pic.aipp.vip/20191016234116.png)
系统设置
参考Setup Elasticsearch> Important System Configuration
最佳实践: 网络
单个集群不要跨数据中心进行部署(不要使用WAN)
节点之间的跳数越少越好
如果有多块网卡,最好将transport和http绑定到不同的网卡,并设置不同的防火墙rules
按需为coordinating node或ingest node配置负载均衡
最佳实践: 内存设定计算实例
内存大小要根据Node需要存储的数据来进行估算 (内存和磁盘配比)
搜索类的比例建议: 1:16
日之类: 1:48-1:96之间
总数据量1T,设置一个副本=2T总数据量
如果搜索类的项目,每个节点31*16=496G,加上预留空间,所以每个节点最多400G数据,至少需要5个数据节点
如果是日志类项目,每个节点31*50 = 1550GB,2个数据节点即可
最佳实践: 存储
推荐使用SSD,使用本地存储,避免远程存储
可以本地指定多个path.data,以支持多块磁盘
ES本身提供了很好的HA机制;无需使用RAID1/5/10
最佳实践: 服务器硬件
建议使用中等配置的机器,不建议使用过于强劲的硬件配置
不建议在一台服务器上运行多个节点
集群配置: Throttles限流
为Relocation和Recovery设置限流,避免过多任务对集群产生性能影响
Recovery
cluster.routing.allocation.node_concurrent_recoveries:2
relocation
cluster.routing.allocation.cluster_concurrent_rebalance:2
集群设置: 关闭Dynamic indexes
可以考虑关闭动态索引创建的功能
123456
PUT _cluster/settings{ "persistent":{ "action.auto_create_index":false }}
或通过模板设置白名单
123456
PUT _cluster/settings{ "persisteng":{ "action.auto_create_index":"logstash-*,.kibana*" }}
为Elasticsearch和Kibana配置安全功能
打开authentication & authorization
实现索引和字段级的安全控制
节点间通信加密
enable https
audit log
监控elasticsearch集群
监控API
ES提供了了多个监控相关的API
* Node Stats: _nodes/stats
* Cluster Stats: _cluster/stats
* Index Stats: index_name/_stats
查看Task相关的api
Pending Cluster Tasks API: `GET _cluster/pending_tasks`
Task Management API: `GET _tasks(可以用来cancel一个task)`
监控Thread Pools
GET _nodes/thread_pool
GET _nodes/stats/thread_pool
GET _cat/thread_pool?v
GET _nodes/hot_threads
The index & Query Slow Log
支持将分片上,Search和Fetch阶段的慢查询写入文件
支持为Query和Fetch分别定义阈值
索引级的动态设置,可以按需设置,或者通过index template统一设定
slow log文件通过log4j2.properties配置
![](http://pic.aipp.vip/20191017000543.png)
如何创建监控dashboard
* 开发es plugin,通过读取相关监控API,将数据发送到ES,或者TSDB
* 使用Metricbeats搜集相关指标
* 使用kibana或grafana创建dashboard
* 可以开发elasticsearch exporter,通过prometheus监控elasticsearch集群
example
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
GET _nodes/statsGET _cluster/statsGET kibana_sample_data_ecommerce/_statsGET _cluster/pending_tasksGET _tasksGET _nodes/thread_poolGET _nodes/stats/thread_poolGET _cat/thread_pool?vGET _nodes/hot_threadsGET _nodes/stats/thread_poolPUT my_index/_settings{ "index.indexing.slowlog":{ "threshold.index":{ "warn":"10s", "info": "4s", "debug":"2s", "trace":"0s" }, "level":"trace", "source":1000 }}DELETE my_index//"0" logs all queriesPUT my_index/{ "settings": { "index.search.slowlog.threshold": { "query.warn": "10s", "query.info": "3s", "query.debug": "2s", "query.trace": "0s", "fetch.warn": "1s", "fetch.info": "600ms", "fetch.debug": "400ms", "fetch.trace": "0s" } }}GET my_index
集群运维面临的挑战
用户集群数量多,业务场景差异大
使用与配置不当,优化不够
如何让用户更加高效的正确的使用ES
如何让用户更全面的了解自己的集群的使用状况
发现问题之后,需要防患于未然
Elastc有提供support diagnostics tool-https://github.com/elastic/support-diagnostics
为什么要诊断集群的潜在问题
防患与未然
master节点/数据节点宕机-负载过高,导致节点失联
副本丢失,导致数据可靠性 受损
集群压力过大,数据写入失败
提升集群性能
数据节点负载不均衡(避免单点瓶颈) 优化分片,segment
规范操作方式(利用别名/避免dynamic mapping引发过多字段,对索引的合理性进行管控)
阿里云 -EYOU
解决集群yellow/red问题
集群健康度
分片健康
红: 至少有一个主分片没有分配
黄: 至少有一个副本分片没有分配
绿: 主副分片全部正常分配
索引健康: 最差的分片状态
集群健康: 最差的索引状态
Health相关API
123456
GET _cluster/health 集群状态GET _cluster/health?level=indices 所有索引的健康状态(查看有问题的索引)GET _cluster/health/my_index 单个索引的健康状态GET _cluster/health?level=shards 分片级的索引GET _cluster/allocation/explain 返回第一个未分配shard的原因GET _cat/allocation?v 磁盘占用情况
案例1
症状: 集群变红
分析: 通过allocatin explain API发现创建索引失败,因为无法找到标记相应box type的节点
解决: 删除索引,集群变绿。重新创建索引,并制定正确的routing box type,索引创建成功,集群保持绿色状态
症状2
症状: 集群变黄
分析: 通过allocation explain API发现无法在相同节点上创建副本
解决: 将索引的副本数设置为0,或者通过增加节点解决
分片没有被分配的一些原因
INDEX_CREATE: 创建索引导致。在索引的全部分频啊分配完成之前,会有短暂red,不一定代表有问题
CLUSTER_RECOVER: 集群重启阶段,会有这个问题
INDEX_REOPEN: open一个之前close的索引
DANGLING_INDEX_IMPORTED: 一个节点离开集群期间,有索引被删除。这个节点重新返回时,会导致dangling问题
常见问题与解决思路
集群变红,需要检查是否有节点离线。如果有,通常通过重启离线节点可以解决问题
由于配置导致的问题,需要修复相关的配置(比如错误的box_type,错误的副本数)
如果是测试的索引,可以直接删除
因为磁盘空间限制,分片规则(shard filtering)引发的,需要调整规则或增加节点
对于节点返回集群,导致的dangling变红,可直接删除danglinig索引
集群red&yellow问题总结
red&yellow是集群运维中常见的问题
除了集群故障,一些创建,增加副本等操作,都会导致集群短暂的red和yellow,所以监控和报警时需要设置一定的延时
通过检查节点数,使用ES提供的相关API,找到真正原因
可以指定move或reallocate分片
提供集群写性能
提高写性能的方法
写性能优化的目标: 增大吞吐量,越高越好
客户端: 多线程,批量写
可以通过性能测试,确定最佳文档数量
多线程,需要观察是否有HTTP429返回,实现retry以及线程数量的自动调节
服务器端: 单个性能问题往往是多个因素造成的。
使用更好的硬件
线程切换/堆栈状况
服务器端优化写入性能的一些手段
降低IO操作
使用ES自动生成的文档ID、一些相关的ES配置,如refresh interval
降低CPU和存储开销
减少不必要分词/避免不需要的doc_values/文档的在端你尽量保证相同的顺序,可以提高文档的压缩率
尽可能做到写入和分片的均衡负载,实现水平扩展
shard filtering/write load balancer
调整bulk线程池和队列
关闭无关的功能
如果只需要聚合不需要搜索,index设置成false
不需要算分,Norms设置成false
不要对字符串使用默认的dynamic mapping。字段过多,会对性能产生比较大的影响
index_options控制在创建倒排索引时,哪些内容会被添加到倒排索引中。优化这些设置,一定程度可以节约CPU
关闭_source,减少IO操作(适合指标性数据)
针对性能的取舍
如果要追求极致的写入速度,可以牺牲数据可靠性及搜索实时性以换取性能
牺牲可靠性: 将副本分片设置为0,写入完毕再调整回去
牺牲搜索实时性: 增加refresh interval的时间
牺牲可靠性: 修改translog配置
数据写入的过程
refresh
将文档先保存在index buffer中,以refresh_interval为间隔时间,定期清空buffer,生成segment,借助文件系统缓存的特性,先将segment放在文件系统缓存中,并开发查询,以提升搜索的实时性
translog
segment没有写入磁盘即便发生宕机,数据也能恢复,默认配置是每次请求都会落盘
flush
删除旧的trans文件
生成segment并写入磁盘/更新commit point并写入磁盘。ES自动完成,可优化点不多
refresh interval
降低refresh频率
增加refresh_interval数值。默认1s,如果设置成-1,会进制自动refersh
避免过于频繁的refresh,而生成过多的segment文件
但是会降低搜索的实时性
增大静态你配置参数indices.memory.index_buffer_size
默认10%,会导致自动触发refresh
translog
降低写磁盘的频率,但是会降低容灾能力
inde.translog.durability: 默认是request,每个请求都落盘。设置成async,异步写入
index.translog.sync_interval设置成60s,每分钟执行一次
index.translog.flush_threshod_size: 默认512MB,可以适当调大,当translog超过该值触发flush
分片设定
副本在写入时设定为0,完成后再增加
合理设置主分片数,确保均匀分配在所有数据节点上
index.routing.allocation.total_shards_per_node:限定每个索引在每个节点上可分配的主分片数
5个节点的集群。索引有5个主分片,1个副本,应该如何设置?
(5+5)/5=2
生产环境中要适当调大这个数字,避免有些节点下线时,分片无法正常迁移
bulk,线程池和队列大小
客户端
单个bulk请求体的数据量不要太大,官方建议大约5~15MB
写入端尽量将数据轮训打到不同节点
服务端
索引创建属于计算密集型任务,应该使用固定大小的线程池来配置。来不及处理的放入队列,线程数应该配置成CPU核心数+1,避免过多的上下文切换
队列大小可以适当增加,不要过大,否则占用的内存会成为GC的负担
![](http://pic.aipp.vip/20191017005941.png)
提升集群读性能
尽量Denormalize数据
ES不等于关系型数据库
尽可能Denormalize数据,从而获取最佳性能
使用nested类型的数据,查询速度慢很多
使用Parent/Child关系,查询速度会慢超级多
数据建模
尽量使用filter context,利用缓存机制,减少不必要的算分
结合profile,explain API分析慢查询问题,持续优化数据模型
严禁使用*开头通配符Terms查询
避免查询时脚本
可以在index文档时,使用ingest pipeline,计算并写入某字段
![](http://pic.aipp.vip/20191017010428.png)
常见查询性能问题-使用query context
![](http://pic.aipp.vip/20191017010529.png)
聚合文档消耗内存
聚合查询会消耗内存,特别是针对很大的数据集进行聚合运算
当需要使用不同的query scope,可以使用filter bucket
![](http://pic.aipp.vip/20191017010628.png)
通配符开始的正则表达
通配符开头的正则,性能会非常糟糕,需避免使用
优化分片
避免over sharding
一个查询需要范文每一个分频啊,分片过多,会导致不必要的查询开销
结合应用场景,控制单个分片的尺寸
search: 20GB
logging: 40GB
force-merge read-only索引
基于时间序列的索引,将只读的索引进行force merge,减少segment数量
集群压力测试
ES本身提供了REST API,所以可以通过很多传统的性能测试工具
jMeter
Gatling
专门为ES设计的工具
ES Pref & Elasticsearch-stress-test
Elastic Rally
ES Rally
github.com/elastic/rally
安装运行
pip3 install esrally
esrally configure
运行
esrally -distribbution-version=7.1.0
运行1000条测试数据: esrally -distribbution-version=7.1.0 --test-mode
段合并优化及注意事项
merge优化
降低分段产生的数量/频率
可将refresh intervel调整到分钟级/indices.memory.index_buffer_size(默认10%)
降低最大分段大小,避免较大的分段继续参与merge,节省系统资源(最终会有多个分段)
index.merge.policy.segments_per_tier,默认为10,越小需要越多的合并操作
index.merge.policy.max_merged_segment,默认5GB,超过此大小后,就不在参与后续的合并操作
force merge
当index不再有写入操作时,建议对齐进行force merge
最终分成几个segments比较好
越少越好,最好可以force merge成一个,但是force merge会占用大量的网络IO CPU
如果不能再业务高峰期之前做完,就需要考虑增大最终的分段数
Shard的大小/index.merge.polocy.max_merged_segment的大小
![](http://pic.aipp.vip/20191017012047.png)
缓存及使用breaker限制内存使用
ES缓存主要分三大类
Node query cache(filter context)
Shard query cache(cache query结果)
fielddata cache
![](http://pic.aipp.vip/20191017012329.png)
Node query cache
每个节点有一个node query缓存
该节点所有shard共享,之缓存filter context相关内容
cache采用LRU算法
静态配置,需要设置在data node上
node level:indices.queries.cache.size:"10%"
index level: index.queries.cache.enabled:true
Shard Request Cache
缓存每个分片上的查询结果
只会换粗你设置了size=0的查询对应的结果。不会缓存hits,但是会缓存aggregations和suggestions
cache key
LRU算法,将整个json查询串作为key,与json对象的顺序相关
静态配置
数据节点indices.requests.cache.size:"1%"
![](http://pic.aipp.vip/20191017012648.png)
fielddata cache
除text类型,默认都采用doc_values,节约了内存
aggregation的global ordinals也保存在fielddata cache中
text类型字段要打开fielddata才能对其进行聚合和排序
text经过分词,排序和聚合效果不佳,建议不要轻易使用
配置
可以控制indices。fielddata.cache。size避免产生GC(默认无限制)
管理内存的重要性
可用内存一半分配给JVM,一半留给操作系统,缓存索引文件
内存引发问题
长时间GC,影响节点,导致集群响应缓慢
OOM,导致丢节点
一些常见的内存问题
segments个数过多,导致full gc
现象: 几区年整体响应缓慢,也没有特别多的数据读写,但是发现节点再持续进行full gc
分析: 查看es的内存使用,发现segments.memory占用很大空间
解决: 通过forc merge 把segments合并为1个
建议: 对于不再写入和更新的索引,可以将其设置为只读。同时,进行force_merge操作。如果问题依旧存在,则需要考虑扩容。
field data cache过大,导致full gc
现象: 集群整体响应缓慢,也没有特别多的数据读写,但是发现节点再持续进行full gc
分析: 查看ES内存使用,发现fielddata.memory.size占用很大空间,同时数据不存在写入和更新,也执行过segments merge
解决: 将indices.fielddata.cache.size设小,重启节点,堆内存恢复正常
建议: field data cache的构建比较重,ES不会主动释放,所以这个值应该设置的保守一些。如果业务上确实需要,可以通过增加节点,扩容解决
circuit breaker
包含多种断路器,避免不合理操作引发的OOM,每个断路器可以指定内存使用的限制
![](http://pic.aipp.vip/20191017013519.png)
circuit breaker统计信息
GET /_nodes/stats/breaker?
tripped大于0,说明有过熔断
![](http://pic.aipp.vip/20191017013617.png)
https://time.geekbang.org/course/detail/197-112076
集群性能排查
查看线程池工作情况,了解实际事件消费情况
http://127.0.0.1:9200/_cat/thread_pool?v
查看集群健康
http://10.75.30.125:9200/_cat/health?v
查看集群节点状况(内存/cpu占用情况,cpu负载,各节点角色)
http://127.0.0.1:9200/_cat/nodes?v
查看集群节点磁盘使用情况
http://127.0.0.1:9200/_cat/allocation?v
查看索引使用情况,并按照磁盘使用大小倒叙排序
http://127.0.0.1:9200/_cat/indices?v&s=store.size:desc
查看集群文档数量
http://127.0.0.1:9200/_cat/count?v
查看集群每个数据节点fielddata使用的内存量
http://127.0.0.1:9200/_cat/fielddata?v
查看等待中的任务
http://127.0.0.1:9200/_cat/pending_tasks?v
查看扩展信息
http://127.0.0.1:9200/_cat/plugins?v
查看恢复情况(节点启动、节点失败、副本配置级别改动等都会进行恢复动作)
http://127.0.0.1:9200/_cat/recovery?v
查看节点上包含哪些分片
http://127.0.0.1:9200/_cat/shards
查看分片中的segment信息
http://127.0.0.1:9200/_cat/segments?v
查看模板信息
http://127.0.0.1:9200/_cat/templates?v
> _cat API每个都可以使用help参数查看帮助,例如http://127.0.0.1:9200/_cat/thread_pool?help
常用查询
过滤器
组合过滤器
bool过滤器是复合过滤器,可以接受多个其他过滤器作为参数,并将这些过滤器结合成各种布尔逻辑组合
一个bool过滤器由三部分构成
must
should
must_not
ps:
filter/must/should/must_not后接的都是数组
must/should/must_not中是各种查找组合
查找
精确查找(对于文本注意使用keyword)
term 精确值查找
terms 批量精确值查找
全文查找
match
match_phrase 短语查询 有顺序
match_all 表示查询所有
范围查找
range
|
|
只有master节点能够创建索引?
开发工具中command+/ 跳转到官方文档
reindex指的是mapping发生变化,或分词器发生变化,对倒排索引进行重建
1)客户端发起数据写入请求,对你写的这条数据根据_routing规则选择发给哪个Shard。
确认Index Request中是否设置了使用哪个Filed的值作为路由参数,
如果没有设置,则使用Mapping中的配置,
如果mapping中也没有配置,则使用_id作为路由参数,然后通过_routing的Hash值选择出Shard,最后从集群的Meta中找出出该Shard的Primary节点。
2)写入请求到达Shard后,先把数据写入到内存(buffer)中,同时会写入一条日志到translog日志文件中去。
当写入请求到shard后,首先是写Lucene,其实就是创建索引。
索引创建好后并不是马上生成segment,这个时候索引数据还在缓存中,这里的缓存是lucene的缓存,并非Elasticsearch缓存,lucene缓存中的数据是不可被查询的。
3)执行refresh操作:从内存buffer中将数据写入os cache(操作系统的内存),产生一个segment file文件,buffer清空。
写入os cache的同时,建立倒排索引,这时数据就可以供客户端进行访问了。
默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。
buffer内存占满的时候也会执行refresh操作,buffer默认值是JVM内存的10%。
通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。
若要优化索引速度, 而不注重实时性, 可以降低刷新频率。
4)translog会每隔5秒或者在一个变更请求完成之后,将translog从缓存刷入磁盘。
translog是存储在os cache中,每个分片有一个,如果节点宕机会有5秒数据丢失,但是性能比较好,最多丢5秒的数据。。
可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
可以通过配置增加transLog刷磁盘的频率来增加数据可靠性,最小可配置100ms,但不建议这么做,因为这会对性能有非常大的影响。
5)每30分钟或者当tanslog的大小达到512M时候,就会执行commit操作(flush操作),将os cache中所有的数据全以segment file的形式,持久到磁盘上去。
第一步,就是将buffer中现有数据refresh到os cache中去。
清空buffer 然后强行将os cache中所有的数据全都一个一个的通过segmentfile的形式,持久到磁盘上去。
将commit point这个文件更新到磁盘中,每个Shard都有一个提交点(commit point), 其中保存了当前Shard成功写入磁盘的所有segment。
把translog文件删掉清空,再开一个空的translog文件。
flush参数设置:
index.translog.flush_threshold_period:
index.translog.flush_threshold_size:
#控制每收到多少条数据后flush一次
index.translog.flush_threshold_ops:
6)Segment的merge操作:
随着时间,磁盘上的segment越来越多,需要定期进行合并。
Es和Lucene 会自动进行merge操作,合并segment和删除已经删除的文档。
我们可以手动进行merge:POST index/_forcemerge。一般不需要,这是一个比较消耗资源的操作。
分片对于ES而言相当关键,她是ES真正存储数据的地方,她是ES实现天然水平扩展的基础,她是保证ES高可用高性能的基石。
分片的本质是啥呢?一个ES分片本质上就是一个Lucene Index,那Lucene Index的本质又是什么呢?Lucene Index 的本质是一个倒排索引的存储结构,那倒排索引是啥呢?倒排索引是索引的一种,是相对于正排索引来命名的,索引的存在就是加快查询的速度,倒排索引通过分词和文档ID的关系来加快文档的查询速度。那索引中包括文档的所有内容嘛?这个一直是我的疑惑,如果包含,那这个索引就太重了,如果不包含,那拿到文档ID后还需要再找一次文档的内容,希望老师能回答一下!
最后Lucene Index 对应的倒排索引不是一直固定唯一的,她是有许多的小的倒排索引合并后才形成的一个唯一的倒排索引。小的倒排索引在Lucene中叫做Segment,单个的Segment倒排索引文件是自包含的,不可变的。当有新文档写入时,并且执行了Refresh,就会生产一个新的Segment倒排索引文件。在Lucene中有一个文件,用来记录所有Segment倒排索引文件的信息,这个文件叫做Commmit Point。
在查询时会查询所有Segment倒排索引文件,并对结果汇总。
当删除文件信息时会保存在.del文件之中,查询后进行过滤,所以,被删除的文件并没有立刻被删除,只是记录到一个文件之中了,这是为什么删除ES文档时为什么磁盘空间不降反升的原因。
Segment倒排索引文件会定期合并,最终合并成为一个Segment倒排索引文件,同时也会真正的清除已删除的文件。此时才会真正的释放出来被删除的文档所占用的磁盘空间。
至此分片和倒排索引都联系起来了,不过还是那个疑惑没解开,倒排索引中是否包括所有的文档内容?
我猜测不包含,通过倒排索引只是拿到了查询的分词和文档ID的关系,还需要根据文档ID获取文档的内容。否则这个倒排索引多重呀!当然,文档ID是文档的唯一标识,拿到文档ID其实等于拿到了文档的内容。
作者回复: 👍
分布式检索
搜索被执行成一个两阶段过程,我们称之为query then fetch
查询阶段
在初始 查询阶段 时, 查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的 优先队列。
当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点。 这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端
查询阶段包含以下三个步骤:
客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
https://www.elastic.co/guide/cn/elasticsearch/guide/cn/_query_phase.html
取回阶段
分布式阶段由以下步骤构成:
协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。
一旦所有的文档都被取回了,协调节点返回结果给客户端。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/distributed-search.html#distributed-search
分布式原理
一个lucene index对应ES中的一个shard
refresh操作
将index buffer内容刷新至系统file cache中
什么时间执行refresh操作
默认每隔1秒执行一次
什么是translog
translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。
这是一项WAL技术,类似mysql中的redo log(Write-Ahaed Logging)
translog根据配置有两种持久化策略,默认为同步操作
1.同步操作,每次请求后fsync到磁盘,在fsync成功后返回客户端200
2.异步操作,可以是每N秒(默认5秒)被刷新到磁盘
flush操作
index buffer文档被写入新的段,并清空index buffer
文件系统缓存通过fsync刷新至磁盘,并生成新的commit point
translog清空
什么时候执行flush操作
translog达到最大值
主动调用flush
30分钟自动flush
translog过大(默认512MB)
https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html#translog
安全
-Expack.security.enabled=true
-Expack.security.transport.ssl.enabled
bin/elasticsearch-setup-passwords interactive -u "https://10.75.30.125:9201"
/usr/share/elasticsearch/elastic-stack-ca.p12
searchwb