全文搜索引擎 ElasticSearch

ElasticSearch是一个基于Lucene的分布式多用户全文搜索引擎,使用Json索引,提供RESTful API,有着极高的实时搜索性能。html

基础知识

ES主要功能git

  • 全文搜索引擎(倒排索引:根据词找到 位置&频次)
  • 数据分析系统(尤为是时间序列数据的聚合分析)
  • 分布式实时存储系统

优势github

  • 高效的分词搜索
  • 强大的聚合运算

ES特性算法

  • 集群由主节点(负责管理集群全部变动)和普通节点组成
  • 索引被切割为分片调配到集群节点上(分片上并存着原始文档),主分片数在索引建立时即已肯定
  • 副本分片用于实现故障切换,负载均衡(分片的备份套数随时可变)

索引结构sql

  • 分片、副本等配置
  • Type Mapping配置,包含:
    • 元数据配置
    • 字段域的类型、索引及搜索方式

健康状态docker

  • 绿色:全部主副分片均正常
  • 黄色:副本分片存在不正常
  • 红色:主分片有存在不正常

Mysql概念对比

Mysql : ES 关系对比数据库

  • 数据库 : 索引 index
  • 表 : 类型 type
  • 行 : 文档 document
  • 列 : 字段 field
  • Schema : 映射 mapping
  • SQL : DSL

Index VS Type

在具体存储空间选型时须要考虑一下几点:json

  • 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插件进行词库热更新
    • 常规WebServerclient请求文件的内容发生变动时 会自动更新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'
  • 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"] }
  • 参考 https://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/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:analyzedString会被ES处理成多值字段
  • 多字段排序 [{字段1: {order:desc|asc}}, ...]

注意:

  • 尽可能在index:not_analyzed字段上进行排序
  • 然而在index:analyzed字段上排序极耗内存

查询高亮

请求结构 curl -XGET http://localhost:9200/索引/类型/_search -d'{query:查询表达式, highlight:高亮表达式}'

高亮表达式:

{
    pre_tags : [<tag1>],
    post_tags : [</tag1>],
    fields : {
        _all: {
            pre_tags : [<tag1>],
            post_tags : [</tag1>],
            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 : [<tag1>],
                        post_tags : [</tag1>],
                        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语法查询父子对象字段

扩容设计

  • 待定
相关文章
相关标签/搜索