ElasticSearch是一个基于Lucene的分布式多用户全文搜索引擎,使用Json索引,提供RESTful API,有着极高的实时搜索性能。
基础知识
ES主要功能
- 全文搜索引擎(倒排索引:根据词找到 位置&频次)
- 数据分析系统(尤其是时间序列数据的聚合分析)
- 分布式实时存储系统
优点
- 高效的分词搜索
- 强大的聚合运算
ES特性
- 集群由主节点(负责管理集群所有变更)和普通节点组成
- 索引被切割为分片调配到集群节点上(分片上并存着原始文档),主分片数在索引创建时即已确定
- 副本分片用于实现故障切换,负载均衡(分片的备份套数随时可变)
索引结构
- 分片、副本等配置
- Type Mapping配置,包含:
- 元数据配置
- 字段域的类型、索引及搜索方式
健康状态
- 绿色:所有主副分片均正常
- 黄色:副本分片存在不正常
- 红色:主分片有存在不正常
Mysql概念对比
Mysql : ES 关系对比
- 数据库 : 索引 index
- 表 : 类型 type
- 行 : 文档 document
- 列 : 字段 field
- Schema : 映射 mapping
- SQL : DSL
Index VS Type
在具体存储空间选型时需要考虑一下几点:
- Index由一系列分片组成, 每个分片即一个Lucene索引实例并存在磁盘,内存,文件句柄的开销
- 查询结果时,ES需要合并所有相关Index下的所有分片的结果集
- 同Index下的不同Type有资源争用问题
- 相关性得分在Index范围内计算, 不同类型Type会相互影响判分
存储空间选型建议:
一般一种文档数据即占用一个INDEX空间,除非满足以下条件可考虑TYPE空间- 两种文档有明显父子关系
- 两种文档有类似的mapping结构
- 每个Type下的文档量级不是特别大
基于Docker安装
DockerFile:
FROM elasticsearch:2.3.5# head插件RUN /usr/share/elasticsearch/bin/plugin install mobz/elasticsearch-head# IK分词安装RUN mkdir /usr/share/elasticsearch/plugins/analysis-ik \ && cd /usr/share/elasticsearch/plugins/analysis-ik \ && wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v1.9.5/elasticsearch-analysis-ik-1.9.5.zip \ && unzip elasticsearch-analysis-ik-1.9.5.zip \ && rm -fr elasticsearch-analysis-ik-1.9.5.zip
镜像构建与启动:
- API服务端口:9200
- 集群节点通信端口:9300
docker build -t test/elasticsearch .docker run -d -p 9200:9200 -p 9300:9300 --name=es test/elasticsearch# 检查插件(应该会罗列head、 analysis-ik两个插件)docker exec es bin/elasticsearch-plugin list
访问:
- API接口:
http://localhost:9200
- head插件:
http://192.168.137.4:9200/_plugin/head
中文分词
analyzer
分析器由以下组成:
char_filter
字符过滤器tokenizer
分词器filter
词条过滤器
IK分词:
- 支持自定义本地词库
plugins/analysis-ik/config/custom/mydict.dic
文件中追加自定义词条- 重启ES服务
- 重建相关数据的索引
- 支持热更新远程词库
plugins/analysis-ik/config/IKAnalyzer.cfg.xml
中配置utf-8远程词库remote_ext_dict地址- 词库地址http头部需返回
Last-Modified、ETag
,任一值变动都会触发IK插件进行词库热更新 - 常规
WebServer
在client
请求文件的内容发生变更时 会自动更新Last-Modified、ETag
两个头部信息
- 可用分词器
- ik_smart: 会做最粗粒度的拆分
- ik_max_word: 会将文本做最细粒度的拆分
测试分词器
curl -XGET 'http://localhost:9200/索引/_analyze?analyzer=ik&pretty=true&text=xxx'
索引管理
- 初始化Index
curl -XPUT http://localhost:9200/索引
- 创建Type Mapping
curl -XPUT http://localhost:9200/索引/_mapping/类型 -d' { "properties": { "字段": { "type": "text", #byte、integer、float、string、date、boolean、geo_point、geo_shape、nested(用于嵌套的对象数组)、object(用于对象,对象会被扁平化处理) "index": "analyzed", #analyzed(全文搜索)、not_analyzed(精确值搜索)、no(不可搜索); "analyzer": "ik_max_word", "include_in_all": true, #该字段是否要包含在_all字段中进行搜索 "fields": { "子字段1": { "type": "string" } } } } } #子字段查询标识 字段.子字段1 # 默认索引模式 - string类型默认`analyzed` - 其他简单类型默认`not_analyzed`
- Index文档
- 指定id
curl -XPUT http://localhost:9200/索引/类型/id -d'xxx'
- 自动id
curl -XPOST http://localhost:9200/索引/类型 -d'xxx'
- 指定id
- Index更新
curl -XPUT http://localhost:9200/索引/类型/id -d'xxx'
- Index删除
curl -XDELETE http://localhost:9200/索引
- 检索文档
curl -XGET http://localhost:9200/索引/类型/id
基本查询
curl -XGET http://localhost:9200/索引/类型/_search?查询字符串
查询字符串:
- q匹配参数
q=xxx:yyy
- 如果q参数没有指定字段,则查询 _all 字段(所有字段值拼接成的大字符串)
+
追加匹配条件,-
追加不匹配条件
- sort排序参数
sort=字段:desc|asc
- sort可指定多次
复合查询
DSL请求体形式 curl -XPOST http://localhost:9200/_search -d'DSL请求体'
(注意请求类型是post)
DSL请求体
- 空查询
{}
- DSL查询表达式
{query:查询表达式, sort:排序, from:起始数, size:每页文档数}
查询模式
- filters过滤模式:高效 不评分查询(结果缓存)
- queries搜索模式:低效 评分查询(结果不缓存) 一般query前先filter来缩减查询规模
查询表达式
- 单一查询语句
- 不指定字段
{查询方法: 值}
- 指定了字段
{查询方法: {字段:值}}
- 不指定字段
- bool合并查询语句
{ bool:{ 合并关系: 查询语句 或 [查询语句], ... } }
查询语句合并关系
每个子查询有自己的评分,在bool时合并评分,以下每个合并关系在每个bool的直接下级中只能调用一次- filter 必须匹配(不评分模式的过滤)
- must 必须匹配(评分模式的搜索)
- must_not 必须不匹配
- should 希望匹配(匹配其一则加分,用于修正相关性)
- minimum_should_match 指定should方式的最小匹配次数 bool查询若只有filter则可替换为constant_score查询,从而使评分为常量值
常见查询语句
- match_all 匹配所有文档(默认查询方法)
{match_all: {}}
- match 标准查询(字段配置决定全文或精确查询)
{match: {字段: 值}}
- multi_match 多字段匹配
{multi_match: {query:值, fields:[字段1, ...]}}
- range 区间查询
{range: {字段:{gte:最小值,lt:最大值}}}
- term 精确值查询
{term: {字段:值}}
- terms 多值精确查询(等于其一就行)
{terms: {字段:[值1,...]}}
- exists、missing 字段有值判断
{exists: {field:字段}}
- nested 嵌套查询
{nested: {path:上级字段, query:查询表达式}}
注意
- term 和 terms 是 包含(contains) 操作,而非 等值(equals),即
{ "term" : { "tags" : "search" } }
可以匹配字段{ "tags" : ["search", "open_source"] }
- 参考 finding_multiple_exact_values.html#%E5%8C%85%E5%90%AB_%E8%80%8C%E4%B8%8D%E6%98%AF%E7%9B%B8%E7%AD%89
查询调试
- 校验语法:
curl -XGET http://localhost:9200/索引/类型/_validate/query -d{}
- 解释校验:
curl -XGET http://localhost:9200/索引/类型/_validate/query?explain -d{}
- 解释评分
- 面向常规查询:
curl -XGET http://localhost:9200/_search?explain&format=yaml -d{}
- 面向指定文档:
curl -XGET http://localhost:9200/索引/类型/ID/_explain -d{}
- 面向常规查询:
查询排序
默认相关性得分降序排列 curl -XGET http://localhost:9200/索引/类型/_search -d'{query:查询表达式, sort:排序, from:起始数, size:每页文档数}'
查询结果集排序:
- 单字段排序
- 排序字段单值
{字段: {order:desc|asc}}
- 排序字段多值
{字段: {order:desc|asc, mode:归一模式}}
- 归一模式:
min、max、avg、sum、
- 归一模式:
index:analyzed
的String
会被ES处理成多值字段
- 排序字段单值
- 多字段排序
[{字段1: {order:desc|asc}}, ...]
注意:
- 尽量在
index:not_analyzed
字段上进行排序 - 然而在
index:analyzed
字段上排序极耗内存
查询高亮
请求结构 curl -XGET http://localhost:9200/索引/类型/_search -d'{query:查询表达式, highlight:高亮表达式}'
高亮表达式:
{ pre_tags : [], post_tags : [ ], fields : { _all: { pre_tags : [], post_tags : [ ], fragment_size: 100, #匹配片段长度 number_of_fragments: 5, #匹配片段数 no_match_size: 0, #无匹配情况下文本长度 } field1 : {} }}
聚合查询
请求体
curl -XGET http://localhost:9200/索引/类型/_search -d{ size:0, #设置查询结果集数目为0提高聚合查询速度 query:查询表达式, aggs:聚合表达式,}
聚合表达式
{ 聚合名1:{ 聚合方法:{field:字段} global:{}, #声明在全局桶下聚合(聚合运算基于全部文档) aggs:嵌套聚合表达式(仅桶类聚合下面可以增加嵌套聚合) }, ...}
聚合构成
一个聚合的每个 层级 都可以有多个度量或桶- Buckets桶 - 足特定条件的文档的集合
- Metrics指标 - 桶内的文档进行统计计算
聚合方法
- Buckets桶类(输出桶集,桶内包含统计量)
- terms 唯一词项分桶(俗称facet特性,高效统计指定层面下各类目约束下的统计量)
- histogram 数值序列分桶
- interval: 间隔数值
- date_histogram 时间序列分桶
- interval: year|quarter|month|day ...
- format: yyyy-MM-dd
- Metrics指标类(输出度量值)
- min 最小字段值
- max 最大字段值
- avg 平均字段值
- sum 求和字段值
- stats 字段统计值汇总(包括count、min、max、avg、sum)
- extended_stats 拓展的字段统计值汇总(stats基础上追加 sum_of_squares平方和、variance方差、std_deviation标准差)
分桶聚合通用参数
- min_doc_count 指定为0则强制空桶也被返回
- extended_bounds 扩展分桶的上下限
GEO查询
距离算法distance_type
- arc 最慢但最精确(球体地球角度)
- plane 精度性能居中(平坦地球角度)
- sloppy_arc 最快但最误差(比arc快四五倍,精度99.9%)
圈过滤
{ geo_distance:{ distance:1km, distance_type:距离算法, location位置字段:{ lon:对比经度 lat:对比纬度 } }}
环过滤
{ geo_distance_range:{ gte:1km,内径 lte:1km,外径 distance_type:距离算法, location位置字段:{ lon:对比经度 lat:对比纬度 } }}
排序
{ _geo_distance:{ location位置字段:{ lat:对比纬度, lon:对比经度 }, nested_path:可选的nest路径, order:desc|asc, unit:km, distance_type:距离算法 }}
返回结果中,每个条目的sort字段首个值即为相对距离/m
完整示例
{ index: my_index, body: { query: { bool: { must: [ #评分模式的搜搜匹配 {match: {字段: 值}}, ... {nested: { path:上级字段, query: { bool: { must: [ {match: {上级字段.该级字段: 值}}, ... ] } } }} ], filter: [ #不评分模式的过滤 {term: {字段:值}}, {range: {字段:{gte:最小值,lt:最大值}}}, ... { geo_distance:{ distance:1km, distance_type:距离算法, location位置字段:{ lon:对比经度 lat:对比纬度 } } } ], } }, from: 100, size: 50, sort: [ {排序字段: {order: desc}}, ... {_geo_distance:{ location位置字段{ lon:对比经度 lat:对比纬度 }, order:asc, unit:km, distance_type:距离算法 } } ], highlight: { { fields : { myField: { pre_tags : [], post_tags : [ ], fragment_size: 100, #匹配片段长度 number_of_fragments: 5, #匹配片段数 no_match_size: 0, #无匹配情况下文本长度 } } } }, aggs: { 聚合名: { term: {field: 字段名}, ... aggs: { mySum: {sum: {field:字段名}} } }, ... }, }}
数据建模
关联关系设计
- 应用层关联 mapping:独立 优缺点:类似于关系型数据库的标准设计,查询性能较差 查询:人工组织关联查询
- 非规范化文档(适用单个子对象) mapping:共用(子对象:object类型字段,也是字段默认自动映射类型) 文档结构:冗余文档结构,被扁平化为键值结构 优缺点:查询效率较高,但是文档索引过大 查询:使用点语法查询子对象字段
- 嵌套对象文档(适用多个子对象) mapping:共用(子对象:nested类型字段) 文档结构:冗余文档结构,每一个嵌套对象都会被索引为一个隐藏的独立文档 优缺点:查询效率较高,但是文档索引过大 查询:使用nested语法查询子对象字段
- 父子关系文档 mapping:独立 文档结构:父对象和子对象都是完全独立的文档 优缺点:索引效率较高,但是查询效率较低 查询:使用has_child、has_parent语法查询父子对象字段
扩容设计
- 待定