1.es
ElasticSearch是⼀一个基于Lucene的开源分布式搜索和分析引擎服务器器。它的特点有:分布式,零配
置,⾃自动发现,索引⾃自动分⽚片,索引副本机制,restful⻛风格接⼝口,多数据源,⾃自动搜索负载等。ElasticSearch这个词由Elastic
和search
组成,前者代表可动态扩容,后者代表其功能,当集群环境下es节点数需要扩容时基本能做到只要花钱买两台机器,加入集群,就可以直接使用,非常的方便.我们在什么样的场景下需要es?
- 当我们的sql存在过多like的时候可以考虑用es来代替mysql
- **数据过大,需要分布式存储时.
- 搜索,如日志,商品等等
- 对数据要求不那么严谨的批处理和流式计算.
- free-schema比较适合中台的应用
2.概念
1.1 lucene
一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎
1.1.1 索引数据结构
对一段文章或者记录进行切割分词,记录单个词条到文档的映射关系,如,【doc1:我是中国人,doc2:我爱中国。】如果用es提供的标准分词器按照单个中文拆开,则记录如下
term | 频率 | 记录 |
---|---|---|
我 | 2 | doc1,doc2 |
是 | 1 | doc1 |
中 | 2 | doc1,doc2 |
国 | 2 | doc1,doc2 |
人 | 1 | doc1 |
爱 | 1 | doc2 |
这样就可以通过对词条的搜索来找到包含记录的文档。
1.1.2 搜索
term | doc1 | doc2 | doc3 | doc4 |
---|---|---|---|---|
人工 | 1 | 1 | 1 | 0 |
智能 | 1 | 1 | 1 | 0 |
成为 | 1 | 0 | 0 | 0 |
互联网 | 1 | 0 | 1 | 0 |
大会 | 1 | 0 | 0 | 0 |
焦点 | 1 | 0 | 0 | 0 |
谷歌 | 0 | 1 | 0 | 1 |
推出 | 0 | 1 | 0 | 0 |
开源 | 0 | 1 | 0 | 1 |
工具 | 0 | 1 | 0 | 1 |
搜索谷歌,开源但是不包含大会的文档:
谷歌:0 1 0 1
开源:0 1 0 1
大会:0 1 1 1(不包含大会,则取反)
对这个三个数做与运算-->0101即文档2和文档四包含此次搜索要求。
1.1.3 segment
段是lucene存储的最小单元,每一次写入都会将当前线程索引的数据写入到一个新的segment中,防止多线写入对于同一个句柄文件产生的竞争,但是在搜索时为了防止遍历所有的段,又需要将segement合并到一个大的文件当中去,删除操作和更新都是记录删除的标识,在重新写入一个新的段,后面再合并的时候在去做真正的删除操作。
1.2 es
1.2.1 基本概念
mysql | es |
---|---|
库 | index |
表 | type |
行 | document |
字段 | field |
schema | mapping |
1.2.2 分片-副本
分片和副本是做数据高可用的时候必不可少的一个概念,无论是hdfs还是kafka或者是其他的分布式中间件都会有这样的概念,只是叫法不一样,数据过于大,一台机器放不下,需要将数据切分,也就是分片,可能也有别的中间件叫法不一样,比如kafak叫分区, 副本的作用有两个一个是备份,另外一个是它本身也能提供主分片的功能。这两个功能如果和mysql进行类比,可以类型分片是搞分库,而副本则是主从,在es中,一个索引默认有5个主分片,1个副本,同一个分片的主分片和副本不能落在同一个node上,防止因为机器宕机,而丢失该分片的所有数据,在机器允许的情况下,提高副本分片的数量也能提高系统的qps,分片的丢失也是es常见的报红报黄的问题。
1.2.2 节点
类型 | 描述 |
---|---|
Master | 主节点 |
Data Node | 存储数据节点 |
Coordinating Node | 客户端节点 |
Ingest Node | 索引文当前可以做些预处理的工作 |
1.2.4 zen
zen是es7.0前默认的选举算法,类似于bully算法,基于二阶段提交,假设在完美的情况下有五个机器,3台作为master节点,首先节点启动,es会根据自己的ip和端口号计算出一个id,并与另外两台简历长连接,当检测到长连接的数量已经达到可以选举,并且集群中没有主节点的情况下,开始正式选举,假设a,b,c生成的三个id分别为1,2,3,此时a得知b和c分别为2,3,知道集群中最小id为节点本身,而b,c也知道集群中最小节点为a,以最小id作为评判的标准,此时a,作为候选节点,则等待b,c加入自己,而b,c则主动发送信息,告知要加入a,如果加入成功,并且加入的数量是大于法定人数,则选举成功,当然这个是理想情况,实际情况非常复杂,要考虑消息超时,选举轮次等等因素。
1.2.5 分词器
1.2.5.1 内置分词器
GET _analyze
{
"analyzer": "standard",
"text": "不转不是中国人"
}
es内部内置了些分词器,我们也可以根据自己的需求来置顶一些分词器,如果是对于业务开发,常见的分词器就是ik,ik分两种分词,一种是standard分词,就是按照中文一个一个割开,另外一个是smart分词,就是可以根据一些常见的语义来进行分词,在ik目录下有一个分词字典,把常用的词写进去,就能达到智能分词的目的,ik也内置了很多词条。
1.2.5.2 自定义分词器
分词器分词主要是经过三个步骤
1.character filter,这个是处理串的第一个过程,可以对整个串进行处理。
2.tokenizer:按照规则进行分词,如按照空格分开
3.token filter:这个是最后的一个步骤,可以对每个term进行处理。
GET _analyze
{
"tokenizer" : "whitespace",
"filter" : ["lowercase"],
"char_filter" : ["html_strip"],
"text" : "this is A <b>test</b>"
}
这个过程就是先对text文本内容先去掉html标签,在按空格切开,最后把所有的串变成小写,也可以在filter指定去掉停用词,去掉this is等用法。
1.2.6 写入
1.客户端向协调协调节点发送请求,协调节点将写请求转发直master节点.
2.master节点根据id确定主分片的位置,如果没有id此时会生成id,会并将数据请求转发至主分片所在的节点
3.主分片进行写操作,先是进行双写操作,写translog后在写buffer,后面会有一个sync线程定期将内存的数据刷到磁盘上(refresh_interval),而刷到磁盘时只是将数据刷到系统的oscache中,后面就是操作系统的事情了,所以线上在部署es时,假如32g内存的机器,至少要留一半的大小给系统缓存,不仅能加速写的过程,也会加速读的过程.
4.当主节点写入成功后,需要将数据转发至副本节点,进行写入,副本写入成功数受quorum
( int( (primary + number_of_replicas) / 2 ) + 1 )的影响,这个也是一个法定人数的概念,写入超过一半认为此次写入成功.
5.每次写入会生成一个segment,然后后面会有一个purge线程,定期合并segment.
1.2.7 搜索
1.将搜索的语句用索引时相同的分词器进行分词.
2.协调节点将请求转发至每个主分片或者副本分片
3.每个分片进行搜索的操作和打分,并将执行结果的id和排序值返回给协调节点,协调节点在进行排序(query_then_fetch),协调节点在根据负载均衡策略,去分片上回取数据.
4.每个分片进行搜索的操作和打分,并将执行结果的文档返回给协调节点,协调节点在进行排序(query_and_fetch)
query_and_fetch和query_then_fetch
3.语法
3.1 查询语法
3.1.1 term
term查询应该是最好理解的,就是搜索语句刚好是一个term,可以直接在倒排序索引中搜索.如:
PUT test/uk/1
{
"word":"我是中国人"
}
PUT test/uk/2
{
"word":"我来自江苏"
}
GET test/uk/_search
{
"query": {
"term": {
"word": {
"value": "我"
}
}
}
}
##返回
"hits": {
"total": 2,
"max_score": 0.2876821,
"hits": [
{
"_index": "test",
"_type": "uk",
"_id": "2",
"_score": 0.2876821,
"_source": {
"word": "我来自江苏"
}
},
{
"_index": "test",
"_type": "uk",
"_id": "1",
"_score": 0.2876821,
"_source": {
"word": "我是中国人"
}
}
]
3.1.2 match
match相对term多做了个动作,就是会对搜索语句进行分词,属于全文搜索.如
GET test/uk/_search
{
"query": {
"match": {
"word": "我是"
}
}
}
###返回
"hits": [
{
"_index": "test",
"_type": "uk",
"_id": "1",
"_score": 0.5753642,
"_source": {
"word": "我是中国人"
}
},
{
"_index": "test",
"_type": "uk",
"_id": "2",
"_score": 0.2876821,
"_source": {
"word": "我来自江苏"
}
}
]
这个时候,只要分完词后只要包含这几个分词结果的文档就都出来了,是or的关系,也可以指定要满足与的关系,如
GET test/uk/_search
{
"query": {
"match": {
"word": {
"query": "我是",
"operator": "and"
}
}
}
}
###返回
"hits": [
{
"_index": "test",
"_type": "uk",
"_id": "1",
"_score": 0.5753642,
"_source": {
"word": "我是中国人"
}
}
]
另外es在生成文档的时候帮我们生成了一个keyword关键字,我们可以用keyword关键字做精确查询,如:
GET test/uk/_search
{
"query": {
"match": {
"word.keyword": "我是中国人"
}
}
}
###返回
"hits": [
{
"_index": "test",
"_type": "uk",
"_id": "1",
"_score": 0.2876821,
"_source": {
"word": "我是中国人"
}
}
]
3.1.3 match_phrase
match_phrase 是另外一种玩法,它可以控制分词后词条之间间隔的距离,如:
GET test/uk/_search
{
"query": {
"match_phrase": {
"word": {
"query": "我国",
"slop":1
}
}
}
}
因为此时我
和国
之间的间隔为2,当指定为1时,无法查出.
3.1.4 bool
bool语法可以实现多级的or和and和not的关系,比如我现在查询这样一段话:这个语句中必须要包含我
和中国
,但是不能包含江
这个term,语法如下:
GET test/uk/_search
{
"query": {
"bool": {
"must": [
{"term": {
"word": {
"value": "我"
}
}},{
"match": {
"word": "中国"
}
}
],
"must_not": [
{"term": {
"word": {
"value": "江"
}
}}
]
}
}
}
3.1.5 filter
filter语法和query的语法差不多,把最外层的query换成filter即可,如:
GET test/uk/_search
{
"post_filter": {
"match": {
"word.keyword": "我是中国人"
}
}
}
##返回
"hits": [
{
"_index": "test",
"_type": "uk",
"_id": "1",
"_score": 1,
"_source": {
"word": "我是中国人"
}
}
但是可以和上面那一条对比下,这条的score得分为1,而上面是一个小于1的数,所以filter是不会走打分机制的,正因为不需要打分,es也能将这部分数据直接缓存,当我们不需要使用打分的时候,建议多用filter.
3.1.6 paninless
painless是es内置的脚本语言,可以用painless进行批量处理,多次聚合等操作,语法和java类似,如,将只要包含我
这个语句的替换成我特么黑化
POST test/uk/_update_by_query
{
"query":{
"match":{
"word":"我"
}
},
"script": {
"lang": "painless",
"inline": "ctx._source.word = params.word;",
"params":{
"word":"我特么黑化"
}
}
}
3.2 聚合语法
聚合是es提供的另外一个功能,可以根据term进行做分组,而且可以多次下钻,然后做min,max等操作,聚合总共分为指标聚合,桶聚合, 矩阵聚合 , 管道聚合 ,前面两种用的比较多,指标聚合的意思就是计算分组和的min,max值,而桶聚合的意思是将分组后的数据进行分析,可以算各个分组里面的一些指标和数据.聚合使用的不是倒排序索引,而是正排序索引,如果需要对text字段进行聚合需要打开fielddata功能,不然无法使用.
例子来与极客时间(Elasticsearch核心技术与实战)
DELETE /employees
PUT /employees/
{
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"gender" : {
"type" : "keyword"
},
"job" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 50
}
}
},
"name" : {
"type" : "keyword"
},
"salary" : {
"type" : "integer"
}
}
}
}
PUT /employees/_bulk
{ "index" : { "_id" : "1" } }
{ "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 }
{ "index" : { "_id" : "2" } }
{ "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000}
{ "index" : { "_id" : "3" } }
{ "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 }
{ "index" : { "_id" : "4" } }
{ "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000}
{ "index" : { "_id" : "5" } }
{ "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 }
{ "index" : { "_id" : "6" } }
{ "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000}
{ "index" : { "_id" : "7" } }
{ "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 }
{ "index" : { "_id" : "8" } }
{ "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000}
{ "index" : { "_id" : "9" } }
{ "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 }
{ "index" : { "_id" : "10" } }
{ "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000}
{ "index" : { "_id" : "11" } }
{ "name" : "Jenny","age":36,"job":"Java Programmer","gender":"female","salary":38000 }
{ "index" : { "_id" : "12" } }
{ "name" : "Mcdonald","age":31,"job":"Java Programmer","gender":"male","salary": 32000}
{ "index" : { "_id" : "13" } }
{ "name" : "Jonthna","age":30,"job":"Java Programmer","gender":"female","salary":30000 }
{ "index" : { "_id" : "14" } }
{ "name" : "Marshall","age":32,"job":"Javascript Programmer","gender":"male","salary": 25000}
{ "index" : { "_id" : "15" } }
{ "name" : "King","age":33,"job":"Java Programmer","gender":"male","salary":28000 }
{ "index" : { "_id" : "16" } }
{ "name" : "Mccarthy","age":21,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : { "_id" : "17" } }
{ "name" : "Goodwin","age":25,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : { "_id" : "18" } }
{ "name" : "Catherine","age":29,"job":"Javascript Programmer","gender":"female","salary": 20000}
{ "index" : { "_id" : "19" } }
{ "name" : "Boone","age":30,"job":"DBA","gender":"male","salary": 30000}
{ "index" : { "_id" : "20" } }
{ "name" : "Kathy","age":29,"job":"DBA","gender":"female","salary": 20000}
# Metric 聚合,找到最低的工资
POST employees/_search
{
"size": 0,
"aggs": {
"min_salary": {
"min": {
"field":"salary"
}
}
}
}
# Metric 聚合,找到最高的工资
POST employees/_search
{
"size": 0,
"aggs": {
"max_salary": {
"max": {
"field":"salary"
}
}
}
}
# 一个聚合,输出多值
POST employees/_search
{
"size": 0,
"aggs": {
"stats_salary": {
"stats": {
"field":"salary"
}
}
}
}
#Salary Ranges 分桶,可以自己定义 key
POST employees/_search
{
"size": 0,
"aggs": {
"salary_range": {
"range": {
"field":"salary",
"ranges":[
{
"to":10000
},
{
"from":10000,
"to":20000
},
{
"key":">20000",
"from":20000
}
]
}
}
}
}
3.2.1 聚合不准确
1.cardinality等一些操作采用近似的算法,提升性能
2./假设我们有一个索引,被分了三个shard,分布在三个不同的机器上,文档中某一个字段为英文字母A
,B
,C
等等,现在对这个字段进行聚合,找出文档中数量排名前五的聚合结果:
分片1 | 分片2 | 分片三 |
---|---|---|
A(25) | A(30) | A(45) |
B(18) | B(25) | C(44) |
C(6) | F(17) | Z(36) |
D(3) | Z(16) | G(30) |
D(2) | G(15) | E(29) |
F(2) | H(14) | H(28) |
G(2) | I(10) | Q(2) |
H(2) | Q(6) | D(1) |
I(1) | J(8) | |
J(1) | C(4) |
然后在每个分片中取前五个,在协调节点中再次聚合:
分片1 | 分片2 | 分片三 |
---|---|---|
A(25) | A(30) | A(45) |
B(18) | B(25) | C(44) |
C(6) | F(17) | Z(36) |
D(3) | Z(16) | B(30) |
D(2) | G(15) | E(29) |
聚合结果
排名 | term |
---|---|
1 | A(100) |
2 | Z(52) |
3 | C(50) |
4 | G(45) |
5 | B(43) |
实际情况中,C有6+4+44=54个,这是因为在数据节点返回的时候,并未将排名第六以后的数据返回给协调节点,我们也可以通过调大shard_size
的参数返回更多的数据,从而提高数据的准确性,但是过大的size会增加集群的负担,降低集群性能,所以如果对数据的准确性要求没那么高的情况下,可以使用es的聚合功能,如果数据准确性很高,请考虑hadoop或者flink这类工具.
4.状态检查
# 集群健康
GET /_cat/health?v
# 查询文档总数
GET /_cat/count?v
#查询某个索引的
GET /_cat/count/movies?v
# 查看索引
GET /_cat/indices
#查看某一个索引
GET /_cat/indices/movies?v
#查看yellow状态的索引
GET /_cat/indices?v&health=yellow
#根据文本数进行倒排序
GET /_cat/indices?v&s=docs.count:desc
es在使用过程中,经常会遇到状态变黄变红的情况,变黄的原因是副本分片的丢失,此时不影响写入和查询,变红是因为主分片丢失,会影响写入,常见的状态原因如下:
# 查看所有分片
GET _cat/shards
#根据索引正则
GET _cat/shards/m*?v
#unassigned.reason可以查看未分配的原因
GET _cat/shards?h=index,shard,prirep,state,unassigned.reason
state状态描述:
INDEX_CREATED Unassigned as a result of an API creation of an index.
CLUSTER_RECOVERED Unassigned as a result of a full cluster recovery.
INDEX_REOPENED Unassigned as a result of opening a closed index.
DANGLING_INDEX_IMPORTED Unassigned as a result of importing a dangling index引入危险索引()
NEW_INDEX_RESTORED Unassigned as a result of restoring into a new index.
EXISTING_INDEX_RESTORED Unassigned as a result of restoring into a closed index.
REPLICA_ADDED Unassigned as a result of explicit addition of a replica.
ALLOCATION_FAILED Unassigned as a result of a failed allocation of the shard.
NODE_LEFT Unassigned as a result of the node hosting it leaving the cluster.
REROUTE_CANCELLED Unassigned as a result of explicit cancel reroute command.
REINITIALIZED When a shard moves from started back to initializing, for example, with shadow replicas.
REALLOCATED_REPLICA A better replica location is identified and causes the existing replica allocation to be cancelled.
#查看unassigned.reason具体解读
GET /_cluster/allocation/explain
{
"index": "myindex",
"shard": 0,
"primary": true
}
变红或者变黄在短暂时间内是正常现象,但是持续时间很长的时候就需要注意了,我之前遇到es变红的比较多的原因主要有两个.一个是磁盘被打满,数据写不进去,另外一个是节点宕机,所以这两种情况需要找运维同学协助排查问题.
5.使用技巧
1.esjar包冲突比较严重,也不仅仅是es的问题,底层netty包经常会和别的jar包进行冲突,而且jar包要和线上es版本一致,es客户端版本兼容问题很头疼,主要原因是Lucene升级不兼容,Lucene有一个类专门用来记录版本号,密密麻麻,这种情况可以在idea里面安装个maven analyzer,可以方便排查冲突.
2.语法,es语法比较复杂,熟记我们常用的就行了,再不会的去翻翻文档,这里面有个小技巧,就是可以利用kibana,可以搜索完看下kibana发的请求,然后去构造这个json.自己写完构造请求,可以输出下toString方法,看是不是一样.
3.可视化客户端,es有很多可视化客户端,如果没有监控啥需求,仅仅是看看数据,可以在chrome安装个head插件即可.
6.推荐环节
1.官方文档
2.极客时间- Elasticsearch核心技术与实战
3.Elasticsearch顶尖高手系列-核心知识篇 + 高手进阶篇,bilibili有(顺便安利一波,bilibili真的是码农的好网站)
4.elasticsearch源码解析与优化实战
5.Lucene实战
6.es中文网站,可以订阅