es-3. 使用

146 阅读10分钟

[toc]

集群运维

  1. 禁止集群主或副分片分配
curl -XPUT http://${CLUSTER}.jdcloud.com:80/_cluster/settings?pretty -d "${ARGU}"
ARGU:{"transient":{"cluster.routing.allocation.enable": ${VALUE}}}

VALUE 可选如下:NULL new_primaries none
默认值为:NULL,值“null” 为取消此设置,恢复到默认设置 new_primaries:集群仅禁止副本分片的分配 none:集群禁止所有分片的分配 2. 查看状态为未分配的分片

curl -XGET http://${CLUSTER}.jdcloud.com:80/_cat/shards?h=index,shard,prirep,state,unassigned.reason -s| grep UNASSIGNED
  1. 执行集群同步刷新
curl -XPOST "http://${CLUSTER}.jdcloud.com:80/_flush/synced?pretty"
  1. 调整集群数据节点磁盘使用上限值
curl -XPUT http://${CLUSTER}.jdcloud.com:80/_cluster/settings?pretty -d "${ARGU}"
ARGU:{"transient":{"cluster.routing.allocation.disk.watermark.high": ${VALUE}}}
  1. 调整集群数据节点磁盘使用上限值
curl -XPUT http://${CLUSTER}.jdcloud.com:80/_cluster/settings?pretty -d "${ARGU}"
ARGU:{"transient":{"cluster.routing.allocation.disk.watermark.low": "${VALUE}"}}
  1. 调整集群集群重平衡并发数
curl -XPUT http://${CLUSTER}.jdcloud.com:80/_cluster/settings?pretty -d "${ARGU}"
ARFU: {"transient":{"cluster.routing.allocation.node_concurrent_recoveries": "${VALUE}"}}
  1. 将IP上的分片迁移到其他节点
curl -XPUT http://${CLUSTER}.jdcloud.com:80/_cluster/settings?pretty -d "${ARGU}"
ARGU: {"transient":{"cluster.routing.allocation.exclude._ip": "${IP}"}}
  1. 索引模板相关操作
   获取索引
   curl http://${CLUSTER}.jdcloud.com:80/_template/${TEMPLATE_NEME}?pretty
   删除索引
   curl -XDELETE http://${CLUSTER}.jdcloud.com:80/_template/${TEMPLATE_NEME}?pretty
   增加索引
   curl -XPUT http://${CLUSTER}.jdcloud.com:80/_template/${TEMPLATE_NEME}?pretty -d "$JSON_FILE"

快速上手

1. 执行健康检查

curl "localhost:9200/_cat/health?v"

执行成功之后可以看到

epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1541906922 11:28:42 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%

+ status: green(正常),yellow(集群整体可用,但某些复制节点有问题),red(集群不可用)   2. 查询所有节点

curl "localhost:9200/_cat/nodes?v"

执行成功之后可以看到

host ip heap.percent ram.percent load node.role master name
127.0.0.1 127.0.0.1 4 81 -1.00 d * Nightshade

  3. 查询所有index

curl "localhost:9200/_cat/indices?v"

执行成功之后可以看到

health status index pri rep docs.count docs.deleted store.size pri.store.size

意味着我们目前还没有建立任何索引   4. 建立index 建立名称为customer的index

curl -XPUT 'localhost:9200/customer?pretty'
curl 'localhost:9200/_cat/indices?v'

执行之后可以看到

health status index pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer 5 1 0 0 650b 650b

我们可以得知customer索引现在有5个数据分片和1次复制,包含了0个文档。另外,health状态为yellow,这是因为我们现在只有一个node,而复制操作至少需要两个node,所以replica是连接不上的,所以状态为yellow   5. 创建mapping 查询已有es的mapping

/your_index/_mapping?pretty
curl -XPOST http://localhost:9200/recipes/ftype/_mapping -d '{"ftype":{"properties":{"name":{"type":"string","index":"not_analyzed"},"age":{"type":"integer"},"score":{"type":"double"},"create_time":{"type":"date","format":"yy-MM-dd HH:mm:ss"}}}}'

每一个type都有一个对应mapping文件,在插入第一个文档的时候,es会自动搜索插入文档的字段并为这个type建立一个mapping,随着插入文档字段的增多,mapping中的字段也随着增多。 这里,我们手动创建一个mapping,对每一个字段我们都严格的定义类型,我们要在customer索引创建如下的mapping

{
"properties": {
    "name": {
        "type": "string",
        "index": "not_analyzed" /*关闭分词*/
    },
    "age": {
        "type": "integer"
    },
    "score": {
        "type": "double"
    },
    "create_time": {
        "type": "date",
        "format": "yy-MM-dd HH:mm:ss" /*日期格式转换*/
    }
  }
}

  6. 插入document 在插入数据前,必须指定数据的type。现在我们在customer索引中插入一个类型为external的数据,并且数据的ID为1 插入的JSON数据 {"name":"fanyank"}

curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '{"name" : "fanyank"}'

执行之后可以看到

{
    "_index" : "customer",
    "_type" : "external",
    "_id" : "2",
    "_version" : 1,
    "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
    },
    "created" : true
}

  插入数据时注意,elasticsearch不会检查索引是否存在,如果发现索引不存在,那么elasticsearch会自动创建索引并插入,所以执行插入时一定要检查索引名称是否拼写正确。   我们在插入数据时也可以不指定ID,这样elasticsearch会为我们生成一个唯一的hashcode,注意此时使用的http请求是POST

curl -XPOST 'localhost:9200/customer/external?pretty' -d '{"name" : "fanyank"}'

执行之后返回如下

{
"_index" : "customer",
"_type" : "external",
"_id" : "AWcBDGmpUlH0QZg74qVp",
"_version" : 1,
"_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
},
"created" : true
}

  7. 更新数据 更新数据和插入数据一样,如果发现id已经存在,那么elasticsearch就会执行更新操作 elasticsearch执行更新操作的本质是删除已有数据然后新插入一条数据   1. 方法一

curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '{"name":"Big Bang"}'

  2. 方法二

curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '{"doc":{"name":"Nathan James"}}'

  3. 方法三 在external中添加age字段

curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '{"doc":{"name":"Nathan James","age":20}}'

  4. 方法四 使用script

curl -XPOST 'localhost:9200/customer/external/1/_update?pretty' -d '
{
"script" : "ctx._source.age += 5"
}'

  注意方法二,方法三请求是POST请求,而且请求体包含doc字段 8. 查询数据 查询我们刚刚插入的id为1的数据

curl 'localhost:9200/customer/external/1?pretty'

执行之后返回如下

{
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 4,
"found" : true,
"_source" : {
    "name" : "fanyank"
}
}

  9. 删除index

curl -XDELETE 'localhost:9200/customer?pretty'
curl 'localhost:9200/_cat/indices?v'

  10. 删除document

curl -XDELETE 'localhost:9200/customer/external/2?pretty'

  11. 批处理 批处理可以在一次请求中对多个document完成插入,更新,删除操作 如下示例在一次请求中插入了一条数据{"name":"Alice"},更新一条数据{"name":"tom","age",16},删除一条数据{"_id":"1"}

curl -XPOST 'localhost:9200/customer/external/_bulk?pretty' -d '
{"index":{"_id":"3"}}
{"name":"Alice"}
{"update":{"_id":"2"}}
{"doc":{"name:"tom","age":"16"}}
{"delete":{"_id":"1"}}
'

  这个不成功,稍后再试一下  

搜索API详解

1. 搜索某个索引的全部数据

curl 'localhost:9200/bank/_search?q=*&pretty'

或者

curl 'localhost:9200/bank/_search?pretty' -d '
{
"query" : {"match_all":{}}
}
'

  2. 限制返回的结果数量为100,默认限制的是10个

curl 'localhost:9200/nebulae/_search?pretty' -d '
{
"query" : {"match_all":{}},
"size": 100
}
'

  3. 分页 返回下标为10往后的10个数据

curl 'localhost:9200/bank/_search?pretty' -d '
{
"query" : {"match_all":{}},
"from": 10,
"size": 10
}
'

  4. 排序 按照余额倒序排列

curl 'localhost:9200/bank/_search?pretty' -d '
{
"query" : {"match_all":{}},
"sort": { "balance": { "order": "desc" } }
}
'

  5. 限制返回的字段 限制返回的字段只有account_number和balance

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"query": { "match_all": {} },
"_source": ["account_number", "balance"]
}'

  6. 条件查询 查询account_id为10010的账户

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"query": { "match": {"account_id":"10010"} }
}'

  7. 与查询 查询address为test,且account_id为10010的账户

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"query": {
    "bool": {
        "must": [
            {"match": {"account_id":"10010"}},
            {"match": {"address": "test"}}
        ]
        }
    }
}'

  8. 或查询 查询account_id为10010或account_id为10011的账户

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
    "query": {
        "bool": {
            "should": [
                {"match": {"account_id":"10010"}},
                {"match": {"account_id": "10011"}}
            ]
        }
    }
}'

  9. 非查询 查询account_id不为10010和10011的账户

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
    "query": {
        "bool": {
            "must_not": [
                {"match": {"account_id":"10010"}},
                {"match": {"account_id": "10011"}}
            ]
        }
    }
}'

  10. 组合查询(与或非) 查询account_id不为10011,且address为test的账户

curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
    "query": {
        "bool": {
            "must_not": [
            {"match": {"account_id": "10011"}}
        ],
        "must": [
            {"match": {"addrss": "test"}}
        ]
        }
    }
}'

 

过滤器查询

过滤器查询,必须先要查出来所有数据然后才能进行过滤 1. 单个字段过滤查询 通常查询一个精确的值时,我们不希望对查询进行评分进行计算,只希望对文档进行快速的包含或者排除计算,所以我们会使用constant_score查询使得es以非评分的模式查询term,理论上以非评分模式进行查询的速度是优于评分模式的。 1. term过滤单个字段 查询所有数据,过滤出姓名为fanyank的用户

{
    "query": {
        "constant_score": {
            "filter": {
                "term": {
                "name": "fanyank"
                }
            }
        }
    }
}

  2. terms过滤出匹配某个字段的所有元素 查询出所有数据,过滤出姓名为fanyank,jerry的用户

{
    "query": {
        "constant_score": {
            "filter": {
                "terms": {
                    "name": [
                        "fanyank",
                        "jerry"
                    ]
                }
            }
        }
    }
}

  2. 多个字段过滤组合查询 我们在某些场景下可能用到组合过滤进行查询,如面对如下的SQL,我们的ES查询参数应该怎么写呢?

select * from customer where (name = 'fanyank' and age = 10)
or (age = 2)

  此时我们会用到多个字段进行过滤查询,在开始之前,有必要了解一下bool查询器,因为bool查询器提供了查询与或非的能力bool查询器结构如下

{
"bool": {
    "must": [],
    "must_not": [],
    "should": []
    }
}

  其中

  1. 多个term与或非查询 接开头的SQL语句,我们可以写出如下查询参数

{
    "query": {
        "filtered": {
            "filter": {
                "bool": {
                    "should": [
                        {"term": {"age": 2}},
                        {
                        "bool": {
                            "must": [
                                {"term": {"name": "fanyank"}},
                                {"term": {"age": 10}}
                            ]}
                        }
                    ]
                }
            }
        }
    }
}

  2. 范围查询

select * from customer where create_time between '2018-11-01' and '2018-11-31'

对应的ES查询如下

{
"query": {
    "filtered": {
        "filter": {
            "range": {
                "create_time": {
                "gte": "2018-11-01",
                "lte": "2018-11-31"
                 }
               }
         }
    }
}

  3. range和terms组合查询

select * from customer where
name in ('fanyank','jerry')
and create_time between '2018-11-01' and '2018-11-03'

  对应的ES查询参数如下:  

{
    "query":{
        "filtered":{
            "filter":{
                "bool":{
                    "must":[
                        {
                            "terms":{
                                "name":[
                                    "fanyank",
                                    "jerry"
                                ]
                            }
                        },
                        {
                            "range":{
                                "create_time":{
                                    "gte":"2018-11-01",
                                    "lte":"2018-11-03"
                                }
                            }
                        }
                    ]
                }
            }
        }
    }
}

 

聚合查询

1. avg 使用avg查询某个字段的平均值 查询学生的平均成绩

{
    "aggs":{
        "avg_grade":{
            "avg":{
                "field":"grade"
            }
        }
    }
}

  查询结果如下  

{
    "aggregations":{
        "avg_grade":{
            "value":75
        }
    }
}

2. cardinality 使用cardinality统计某个字段去重之后的值 查询学生数量,由于每个学生的ID是唯一的,所以去重之后统计结果就是学生数量,但是如果按照姓名去做统计,那么统计出来的学生的数量就不准确了,因为可能存在姓名相同的学生

{
    "size":0,
    "aggs":{
        "student_count":{
            "cardinality":{
                "field":"id"
            }
        }
    }
}

  查询结果如下:  

{
    "aggregations":{
        "student_count":{
            "value":100
        }
    }
}

  3. stats 统计学生成绩(min,max,avg,count,sum)

{
    "size":0,
    "aggs":{
        "student_grade_stats":{
            "extended_stats":{
                "field":"grade"
            }
        }
    }
}

  查询结果如下:

{
    "aggregations":{
        "grade_stats":{
            "count":9,
            "min":72,
            "max":99,
            "avg":86,
            "sum":774,
            "sum_of_squares":67028,
            "variance":51.55555555555556,
            "std_deviation":7.180219742846005,
            "std_deviation_bounds":{
                "upper":100.36043948569201,
                "lower":71.63956051430799
            }
        }
    }
}

  4. terms terms可以让数据按照给定字段的不同值进行分组,组的形式以bucket形式呈现 把学生按照性别进行分组

{
    "size":0,
    "aggs":{
        "genders":{
            "terms":{
                "field":"gender"
            }
        }
    }
}

  执行之后结果如下:  

{
...
    "aggregations" : {
        "genders" : {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets" : [
                {
                "key" : "male",
                "doc_count" : 10
                },
                {
                "key" : "female",
                "doc_count" : 10
                }
             ]
        }
    }
}

  5. value_count 统计学生个数(即使存在两个名词相同的学生也可以统计出来准确的个数)

{
    "size":0,
    "aggs":{
        "student_count":{
            "value_count":{
                "field":"name"
            }
        }
    }
}

  6. 嵌套查询 分别计算男生和女生的数学成绩的平均分

{
    "aggs":{
        "groupby_gender":{
            "terms":{
                "field":"gender"
            },
            "aggs":{
                "avg_math_score":{
                    "avg":{
                        "field":"math"
                    }
                }
            }
        }
    }
}

  查询结果如下  

{
    "aggregations":{
        "groupby_gender":{
            "doc_count_error_upper_bound":0,
            "sum_other_doc_count":0,
            "buckets":[
                {
                    "key":"male",
                    "doc_count":2,
                    "avg_math_score":{
                        "value":94.9
                    }
                },
                {
                    "key":"female",
                    "doc_count":1,
                    "avg_math_score":{
                        "value":9.9
                    }
                }
            ]
        }
    }
}

  7. script 尽管ES为我们提供了多种多样的统计函数,但是面对复杂的业务场景ES也常常感到无能为力,此时我们就要使用自己定义的脚本进行数据统计 首先需要在/config/elasticseach.yml配置文件中配置,使得ES支持script脚本

script.inline: on
script.indexed: on

配置成功后重启ES   查询firstNamelastName拼接起来去重之后的数量  

{
    "size":0,
    "aggs":{
        "fullNameCount":{
            "cardinality":{
                "script":"doc['firstName'].value + ' ' + doc['lastName'].value"
            }
        }
    }
}

  统计学生的成绩,如果成绩不存在,则把学生的年龄作为成绩进行统计(虽然很不合理)  

{
    "size":0,
    "aggs":{
        "group_by_name":{
            "terms":{
                "field":"name"
            },
            "aggs":{
                "score_or_age":{
                    "sum":{
                        "script":"if(doc['score'].value==0){return doc['age'].value}else{return doc['score'].value}"
                    }
                }
            }
        }
    }
}

  简化版本(使用三目运算符)  

{
    "size":0,
    "aggs":{
        "group_by_name":{
            "terms":{
                "field":"name"
            },
            "aggs":{
                "score_or_age":{
                    "sum":{
                        "script":"return (doc['score'].value==0) ? doc['age'].value : doc['score'].value"
                    }
                }
            }
        }
    }
}

 

数据字典

某次搜索结果如下,借此说明各个字段的含义  

{
"took": 63,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1000,
"max_score": 1,
"hits": [
{
"_index": "bank",
"_type": "account",
"_id": "1",
"_score": 1,
"_source": {
"account_number": 1,
"balance": 39225,
"firstname": "Amber",
"lastname": "Duke",
"age": 32,
"gender": "M",
"address": "880 Holmes Lane",
"employer": "Pyrami",
"email": "amberduke@pyrami.com",
"city": "Brogan",
"state": "IL"
}
},
{
    "_index": "bank",
    "_type": "account",
    "_id": "6",
    "_score": 1,
    "_source": {
        "account_number": 6,
        "balance": 5686,
        "firstname": "Hattie",
        "lastname": "Bond",
        "age": 36,
        "gender": "M",
        "address": "671 Bristol Street",
        "employer": "Netagy",
        "email": "hattiebond@netagy.com",
        "city": "Dante",
        "state": "TN"
        }
        }
        ]
    }
}

数据字典: + took: 查询花费的毫秒数 + time_out: 查询是否超时 + shards: 查询所涉及到的数据分片的数量,成功数量和失败数量 + hits: 查询结果 + hits.total: 返回的结果总数 + hits.hits: 返回的结果,以数组形式返回 + _score: 与查询参数匹配的程度 + max_score: 最大匹配程度