Learning Progress: Completed.😀
Elasticseach
1 简介
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎, 国内简称ES,Elasticsearch是用java开发的,底层基于Lucene, Lucene是一种全文检索的搜索库,直接使用Lucene还是比较麻烦的,Elasticsearch在Lucene的基础上开发了一个强大的搜索引擎。
-
ELK代表了Elasticsearch + Logstash + Kibana 三套软件,他们的作用如下:
Elasticsearch:前面简介提到过,解决海量数据搜索问题。
Logstash:解决数据同步问题,因为我们数据一般存储在Mysql之类的数据库中,需要将数据导入到ES中,Logstash就支持数据同步、数据过滤、转换功能。
Kibana:Elasticsearch数据可视化支持,例如:通过各种图表展示ES的查询结果,也可以在Kibana通过ES查询语句分析数据,起到类似ES Web后台的作用。
2 存储结构
MYSQL 是关系数据库,Elasticsearch是NOSQL类型的数据库,虽然他们都是数据库,但是他们定位不一样,也不是同一类型的数据库,拿来做对比,是因为一方面对MYSQL比较熟悉,另外从使用角度ES的存储结构跟MYSQL比较相似。
在Elasticsearch中索引(index)类似mysql的表,代表文档(Document)数据的集合,文档指的是ES中存储的一条数据。
Elasticsearch是面向文档的数据库,文档是最基本的存储单元,文档类似mysql表中的一行数据。简单的说在ES中,文档指的就是一条JSON数据。Elasticsearch中文档使用json格式存储,因此存储上比Mysql要灵活的多,Elasticsearch支持任意格式的json数据。
文档中的任何json字段都可以作为查询条件。文档的json格式没有严格限制,可以随意增加、减少字段,甚至每一个文档的格式都不一样也可以。
-
文档由多个json字段(Field)组成, 这里的字段类似mysql中表的字段。当然Elasticsearch中字段也有类型的,下面是常用的字段类型:
数值类型(包括: long、integer、short、byte、double、float)
text:支持全文搜索
keyword:不支持全文搜索,例如:email、电话这些数据,作为一个整体进行匹配就可以,不需要分词处理。
date:日期类型
boolean
Elasticsearch的mapping(映射)类似mysql中的表结构定义,每个索引都有一个映射规则,我们可以通过定义索引的映射规则,提前定义好文档的json结构和字段类型,如果没有定义索引的映射规则,Elasticsearch会在写入数据的时候,根据我们写入的数据字段推测出对应的字段类型,相当于自动定义索引的映射规则。
Elasticsearch存储结构 | MYSQL存储结构 |
---|---|
index(索引) | 表 |
document(文档) | 行,一行数据 |
Field(字段) | 表字段 |
mapping (映射) | 表结构定义 |
3 文档 CRUD
-
文档元数据,指的是插入JSON文档的时候,Elasticsearch为这条数据,自动生成的系统字段。元数据的字段名都是以下划线开头的。常见的元数据如下:
_index:代表当前JSON文档所属的索引名字
_type:代表当前JSON文档所属的类型,虽然新版ES废弃了type的用法,但是元数据还是可以看到。
_id:文档唯一Id, 如果我们没有为文档指定id,系统会自动生成
_source:代表我们插入进去的JSON数据
_version:文档的版本号,每修改一次文档数据,字段就会加1, 这个字段新版的ES已经不使用了
_seq_no:文档的版本号, 替代老的_version字段
_primary_term:文档所在主分区,这个可以跟_seq_no字段搭配实现乐观锁。
在Elasticsearch插入一个JSON文档,又叫索引文档, 注意这里的索引跟前面提到的文档所属的索引名,不是一回事,很晕吧,其实主要翻译问题,我们将数据插入到ES的过程,其实就是创建索引的过程,所以插入文档,也叫做索引文档,这里索引是动词, 而文档属于哪个索引(index),这里的索引代表一个分类,有数据库的概念,是个名词。搞不清楚也没关系,知道索引文档的意思,其实就是往ES插入数据就行。
4 文档类型定义
精确值通常指的就是数值类型、时间、布尔值、字符串的keyword类型,这些不可分割的数据类型,精确值搜索效率比较高,精确值匹配类似MYSQL中根据字段搜索,例如:拿一个手机号去搜索数据,对于每一个文档的手机号字段,要么相等,要么不等,不会做别的计算。
全文类型,指的就是text类型,会涉及分词处理,存储到ES中的数据不是原始数据,是一个个关键词。例如:我们有一个title字段,数据类型是text,我们插入”上海复旦大学”这个字符串,经过分词处理,可能变成:“上海”、“复旦大学”、“大学” 这些关键词,然后根据这些关键词建倒排索引。
-
查看索引映射规则:
GET /order/_mapping
查询
5 基本语法结构
GET /{索引名}/_search
{
"from" : 0, // 返回搜索结果的开始位置
"size" : 10, // 分页大小,一次返回多少数据
"_source" :[ ...需要返回的字段数组... ],
"query" : { ...query子句... },
"aggs" : { ..aggs子句.. },
"sort" : { ..sort子句.. }
}
query子句主要用来编写类似SQL的Where语句,支持布尔查询(and/or)、IN、全文搜索、模糊匹配、范围查询(大于小于)。
aggs子句,主要用来编写统计分析语句,类似SQL的group by语句。
sort子句,用来设置排序条件,类似SQL的order by语句。
ES查询的分页主要通过from和size参数设置,类似MYSQL 的limit和offset语句。
_source用于设置查询结果返回什么字段,类似Select语句后面指定字段。
6 query 查询
6.1 匹配单个字段
GET /{索引名}/_search
{
"query": {
"match": {
"{FIELD}": "{TEXT}"
}
}
}
如果title字段的数据类型是text类型,搜索关键词会进行分词处理。
6.2 精确匹配单个字段
如果我们想要类似SQL语句中的等值匹配,不需要进行分词处理,例如:订单号、手机号、时间字段,不需要分值处理,只要精确匹配。通过term实现精确匹配语法:
GET /{索引名}/_search
{
"query": {
"term": {
"{FIELD}": "{VALUE}"
}
}
}
6.3 通过terms实现SQL的in语句
如果我们要实现SQL中的in语句,一个字段包含给定数组中的任意一个值就匹配。terms语法:
GET /order_v2/_search
{
"query": {
"terms": {
"{FIELD}": [
"{VALUE1}",
"{VALUE2}"
]
}
}
}
6.4 范围查找
通过range实现范围查询,类似SQL语句中的>, >=, <, <=表达式。range语法:
GET /{索引名}/_search
{
"query": {
"range": {
"{FIELD}": {
"gte": 10,
"lte": 20
}
}
}
}
范围参数:
gt:大于(>)
gte:大于且等于(>=)
lt:小于(<)
lte:小于且等于(<=)
6.5 bool组合查询
GET /{索引名}/_search
{
"query": {
"bool": { // bool查询
"must": [], // must条件,类似SQL中的and, 代表必须匹配条件
"must_not": [], // must_not条件,跟must相反,必须不匹配条件
"should": [] // should条件,类似SQL中or, 代表匹配其中一个条件
}
}
}
可以任意选择must、must_not和should条件的参数都是一个数组,意味着他们都支持设置多个条件。
7 全文搜索
ES通过分词处理、相关度计算解决不同文章相关度对比问题。ES内置了一些相关度算法,例如:TF/IDF算法,大体上思想就是,如果一个关键词在一篇文章出现的频率高,并且在其他文章中出现的少,那说明这个关键词与这篇文章的相关度很高。
分词就是为了提取搜索关键词,理解搜索的意图,我们平时在百度搜索内容的时候,输入的内容可能很长,但不是每个字都对搜索有帮助,所以通过分词算法,我们输入的搜索关键词,会进一步分解成多个关键词。
在ES中测试分词效果:
GET /_analyze
{
"text": "需要分词的内容",
"analyzer": "分词器"
}
目前中文分词器比较常用的有:smartcn和ik两种。smartcn是目前ES官方推荐的中文分词插件,不过目前不支持自定义词库;ik支持自定义扩展词库。
8 排序
ES的默认排序是根据相关性分数排序,如果我们想根据查询结果中的指定字段排序,需要使用sort Processors处理。
GET /{索引名}/_search
{
"query": {
...查询条件....
},
"sort": [
{
"{Field1}": { // 排序字段1
"order": "desc" // 排序方向,asc或者desc, 升序和降序
}
},
{
"{Field2}": { // 排序字段2
"order": "desc" // 排序方向,asc或者desc, 升序和降序
}
}
....多个排序字段.....
]
}
聚合分析
9 统计分析概念
ES聚合查询类似SQL的GROUP BY,一般统计分析主要分为两个步骤:
分组
组内聚合
9.1 核心概念
满足特定条件的文档的集合,叫做桶。桶的就是一组数据的集合,对数据分组后,得到一组组的数据,就是一个个的桶。
ES中桶聚合,指的就是先对数据进行分组,ES支持多种分组条件,例如:支持类似SQL的GROUP BY根据字段分组,当然ES比SQL更强大,支持更多的分组条件,以满足各种统计需求。
桶等同于组,分桶和分组是一个意思,ES使用桶代表一组相同特征的数据。
9.2 ES聚合查询语法
{
"aggregations" : {
"<aggregation_name>" : {
"<aggregation_type>" : {
<aggregation_body>
}
[,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套聚合查询,支持多层嵌套
}
[,"<aggregation_name_2>" : { ... } ]* // 多个聚合查询,每个聚合查询取不同的名字
}
}
说明:
aggregations:代表聚合查询语句,可以简写为aggs
<aggregation_name>:代表一个聚合计算的名字,可以随意命名,因为ES支持一次进行多次统计分析查询,后面需要通过这个名字在查询结果中找到我们想要的计算结果。
<aggregation_type>:聚合类型,代表我们想要怎么统计数据,主要有两大类聚合类型,桶聚合和指标聚合,这两类聚合又包括多种聚合类型,例如:指标聚合:sum、avg, 桶聚合:terms、Date histogram等等。
<aggregation_body>:聚合类型的参数,选择不同的聚合类型,有不同的参数。
aggregation_name_2:代表其他聚合计算的名字,意思就是可以一次进行多种类型的统计。
例子:
GET /order/_search
{
"size" : 0, // 设置size=0的意思就是,仅返回聚合查询结果,不返回普通query查询结果。
"aggs" : { // 聚合查询语句的简写
"popular_colors" : { // 给聚合查询取个名字,叫popular_colors
"terms" : { // 聚合类型为,terms,terms是桶聚合的一种,类似SQL的group by的作用,根据字段分组,相同字段值的文档分为一组。
"field" : "color" // terms聚合类型的参数,这里需要设置分组的字段为color,根据color分组
}
}
}
}
等价SQL如下:
SELECT COUNT(color)
FROM order
GROUP BY color
10 指标聚合
10.1 统计函数
ES指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用。
常用的统计函数如下:
-
Value Count:类似sql的count函数,统计总数
GET /sales/_search?size=0 { "aggs": { "types_count": { // 聚合查询的名字,随便取个名字 "value_count": { // 聚合类型为:value_count "field": "type" // 计算type这个字段值的总数 } } } }
-
Cardinality:类似SQL的count(DISTINCT 字段),统计不重复的数据总数
POST /sales/_search?size=0 { "aggs" : { "type_count" : { // 聚合查询的名字,随便取一个 "cardinality" : { // 聚合查询类型为:cardinality "field" : "type" // 根据type这个字段统计文档总数 } } } }
-
Avg:求平均值
POST /exams/_search?size=0 { "aggs": { "avg_grade": { // 聚合查询名字,随便取一个名字 "avg": { // 聚合查询类型为: avg "field": "grade" // 统计grade字段值的平均值 } } } }
Sum:求和(同Avg)
Max:求最大值(同Avg)
Min:求最小值(同Avg)
10.2 综合例子
实际应用中经常先通过query查询,搜索索引中的数据,然后对query查询的结果进行统计分析。
GET /sales/_search
{
"size": 0, // size = 0,代表不想返回query查询结果,只要统计结果
"query": { // 设置query查询条件,后面的aggs统计,仅对query查询结果进行统计
"constant_score": {
"filter": {
"match": {
"type": "hat"
}
}
}
},
"aggs": { // 统计query查询结果, 默认情况如果不写query语句,则代表统计所有数据
"hat_prices": { // 聚合查询名字,计算price总和
"sum": {
"field": "price"
}
},
"min_price": { // 聚合查询名字,计算price最小值
"min": {
"field": "price"
}
},
"max_price": { // 聚合查询名字,计算price最大值
"max": {
"field": "price"
}
}
}
}
11 分组统计
Elasticsearch桶聚合,目的就是数据分组,先将数据按指定的条件分成多个组,然后对每一个组进行统计。 组的概念跟桶是等同的,在ES中统一使用桶(bucket)这个术语。
ES桶聚合的作用跟SQL的group by的作用是一样的,区别是ES支持更加强大的数据分组能力,SQL只能根据字段的唯一值进行分组,分组的数量跟字段的唯一值的数量相等,例如: group by 店铺id, 去掉重复的店铺ID后,有多少个店铺就有多少个分组。
11.1 聚合类型
ES常用的桶聚合如下:
-
Terms聚合:类似SQL的group by,根据字段唯一值分组
GET /order/_search?size=0 { "aggs": { "shop": { // 聚合查询的名字,随便取个名字 "terms": { // 聚合类型为: terms "field": "shop_id" // 根据shop_id字段值,分桶 } } } }
-
Histogram聚合:根据数值间隔分组,例如: 价格按100间隔分组,0、100、200、300等等
POST /sales/_search?size=0 { "aggs" : { "prices" : { // 聚合查询名字,随便取一个 "histogram" : { // 聚合类型为:histogram "field" : "price", // 根据price字段分桶 "interval" : 50 // 分桶的间隔为50,意思就是price字段值按50间隔分组 } } } }
-
Date histogram聚合:根据时间间隔分组,例如:按月、按天、按小时分组
POST /sales/_search?size=0 { "aggs" : { "sales_over_time" : { // 聚合查询名字,随便取一个 "date_histogram" : { // 聚合类型为: date_histogram "field" : "date", // 根据date字段分组 "calendar_interval" : "month", // 分组间隔:month代表每月、支持minute(每分钟)、hour(每小时)、day(每天)、week(每周)、year(每年) "format" : "yyyy-MM-dd" // 设置返回结果中桶key的时间格式 } } } }
-
Range聚合:按数值范围分组,例如: 0-150一组,150-200一组,200-500一组。
GET /_search { "aggs" : { "price_ranges" : { // 聚合查询名字,随便取一个 "range" : { // 聚合类型为: range "field" : "price", // 根据price字段分桶 "ranges" : [ // 范围配置 { "to" : 100.0 }, // 意思就是 price <= 100的文档归类到一个桶 { "from" : 100.0, "to" : 200.0 }, // price>100 and price<200的文档归类到一个桶 { "from" : 200.0 } // price>200的文档归类到一个桶 ] } } } }
11.2 综合例子
GET /cars/_search
{
"size": 0, // size=0代表不需要返回query查询结果,仅仅返回aggs统计结果
"query" : { // 设置查询语句,先赛选文档
"match" : {
"make" : "ford"
}
},
"aggs" : { // 然后对query搜索的结果,进行统计
"colors" : { // 聚合查询名字
"terms" : { // 聚合类型为:terms 先分桶
"field" : "color"
},
"aggs": { // 通过嵌套聚合查询,设置桶内指标聚合条件
"avg_price": { // 聚合查询名字
"avg": { // 聚合类型为: avg指标聚合
"field": "price" // 根据price字段计算平均值
}
},
"sum_price": { // 聚合查询名字
"sum": { // 聚合类型为: sum指标聚合
"field": "price" // 根据price字段求和
}
}
}
}
}
}
12 多桶排序
类似terms、histogram、date_histogram这类桶聚合都会动态生成多个桶,如果生成的桶特别多,我们如何确定这些桶的排序顺序,如何限制返回桶的数量。
默认情况,ES会根据doc_count文档总数,降序排序。
ES桶聚合支持两种方式排序:
-
内置排序
_count:按文档数排序。对 terms 、 histogram 、 date_histogram 有效
_term:按词项的字符串值的字母顺序排序。只在 terms 内使用
_key:按每个桶的键值数值排序, 仅对 histogram 和 date_histogram 有效
例子:
GET /cars/_search { "size" : 0, "aggs" : { "colors" : { // 聚合查询名字,随便取一个 "terms" : { // 聚合类型为: terms "field" : "color", "order": { // 设置排序参数 "_count" : "asc" // 根据_count排序,asc升序,desc降序 } } } } }
-
按度量指标排序
通常情况下,我们根据桶聚合分桶后,都会对桶内进行多个维度的指标聚合,所以我们也可以根据桶内指标聚合的结果进行排序。
GET /cars/_search { "size" : 0, "aggs" : { "colors" : { // 聚合查询名字 "terms" : { // 聚合类型: terms,先分桶 "field" : "color", // 分桶字段为color "order": { // 设置排序参数 "avg_price" : "asc" // 根据avg_price指标聚合结果,升序排序。 } }, "aggs": { // 嵌套聚合查询,设置桶内聚合指标 "avg_price": { // 聚合查询名字,前面排序引用的就是这个名字 "avg": {"field": "price"} // 计算price字段平均值 } } } } }
如果分桶的数量太多,可以通过给桶聚合增加一个size参数限制返回桶的数量:
GET /_search
{
"aggs" : {
"products" : { // 聚合查询名字
"terms" : { // 聚合类型为: terms
"field" : "product", // 根据product字段分桶
"size" : 5 // 限制最多返回5个桶
}
}
}
}
SQL
13 SQL 简介
我们可以直接通过REST API执行SQL语句,语法格式如下:
POST /_sql?format=txt
{
"query": "这里书写SQL语句"
}
14 SQL 语法
ES 支持的 SQL 命令:
-
查询ES索引的字段和类型
POST /_sql?format=txt { "query": "SHOW COLUMNS FROM library" }
-
将ES中所有的索引都列出来
POST /_sql?format=txt { "query": "SHOW TABLES" }
-
展示ES支持的SQL函数有哪些
POST /_sql?format=txt { "query": "SHOW FUNCTIONS" }
15 SQL 全文搜索
ES SQL语法虽然支持like语句,但是like并不是使用全文搜索算法,ES SQL语句中主要通过MATCH函数实现全文搜索。
POST /_sql?format=txt
{
"query": "SELECT author, name FROM library WHERE MATCH(author, 'frank')"
}
通过SCORE()函数实现相关度排序:
POST /_sql?format=txt
{
"query": "SELECT author, name FROM library WHERE MATCH(author, 'frank') ORDER BY SCORE()"
}