在日常开发中,我们使用最多的数据应该就是关系型数据库如 MySQL
, Oracle
等,其特性为表结构格式统一易于上手,但有一个缺点就是面对海量数据读写性能相对较差。
非关系型数据库中较为出名的就是 Redis
, Postgre
, Mongo
等,以 Redis
为例,其数据存储于内存之中,读写速度相对关系型有一个质的飞跃,但同时也带来了内存的损耗。
ElasticSearch
是一款分布式存储数据库,在海量数据查询以及模糊查询时有着不错的性能,解决了 MySQL
等数据库性能瓶颈与 Redis
针对大数据量耗费资源的缺点。
一、概念介绍
1. 索引
在 Elasticsearch
中并没有库表的概念,取而代之的为索引 (Indices)
,可以理解为传统数据库的 Database
。
2. 类型
在 Elasticsearch 6.x
中 Types
概念类似于数据库中的一张表,但随着版本更新,在最新的 7.x
版本中 Types
被逐渐废除,因此一个索引 Indices
下只允许存在一个 Types
。
3. 文档
在 Elasticsearch
中每条记录是以文档 (Documents)
的形式存在,相当于 MySQL
等数据表中的一条记录,每条文档都会自动为其分配一个唯一标识。
4. 字段
Fields
即为每条文档中的属性的类型,常用类型包含如下几类:
类型 | 描述 |
---|---|
text | 当一个字段需要用于全文搜索(会被分词)则应该使用该类型。 |
keyword | 当一个字段需要按照精确值进行过滤、排序、聚合等操作时则应该使用该类型。 |
date | 时间类型,精度不包含时分秒。 |
boolean | 布尔类型,即 true 与 false。 |
range | 区间类型,取值范围如下:integer_range, long_range, float_range, double_range, date_range, ip_range |
二、数据索引
1. 索引创建
通过 PUT
请求创建新索引,请求格式如下,其中 settings
参数非必填。
settings
中 number_of_shards
用于执行数据的分片大小,类似于关系型的中的分区操作,以提高存储查询效率;number_of_replicas
用于指定副本大小,即集群状态多端副本保证数据的安全性以防止数据丢失或损坏。
PUT http://<ip>:<port>/<index_name>
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
}
}
2. 字段映射
同理在创建索引时可通过 mapping
参数指定字段映射。
若不指定将会根据文档数据字段类型自动匹配,映射内容中的 type
参数上一点中的字段表格。
PUT http://<ip>:<port>/<index_name>
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mapping": {
"_doc": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
}
3. 索引别名
Elasticsearch
索引别名是一个指向一个或多个索引的稳定引用名称。通过使用别名,可以将查询和索引维护从实际索引名称中分离出来,这为在切换索引版本、重建索引等操作时提供了更大的灵活性。
使用索引别名的优点如下:
- 日志切割:对于日志类型的索引,可以使用别名轻松地进行日志切割。
- 切换版本:可以通过更改别名指向的实际索引,而不必更改应用程序中的查询代码。
- 滚动升级:在进行索引版本升级时,可以创建新版本的索引,然后通过别名将查询指向新索引,实现滚动升级。
4. 索引删除
通过 DELETE 请求删除索引,删除索引会同步删除其对应下的数据,请求格式如下:
DELETE http://<ip>:<port>/<index_name>
三、数据增改
1. 数据插入
数据插入的请求模板示例如下,其中 <type>
的可选值为:_doc
,且 <id>
若未指定则回自动分配。
POST http://<ip>:<port>/<index>/<type>/<id>
{
"field1": "value1",
"field2": "value2",
"field3": "value3"
}
2. 数据更新
数据更新的请求模板示例如下:
PUT http://<ip>:<port>/<index>/<type>/<id>
{
"field1": "value1_1",
"field2": "value2_1",
"field3": "value3_1"
}
3. 数据删除
数据删除的请求模板示例如下:
DELETE http://<ip>:<port>/<index>/<type>/<id>
四、批量操作
1. 基本格式
action | 参数 |
---|---|
create | 创建新的一条记录。 |
update | 更新已有一条记录。 |
index | 若记录存在,则进行覆盖。 |
delete | 删除一条数据记录。 |
需要注意的是每一行数据要以换行符结束,同时内容必须空出最后一行空白行。
{"action": {"metadata"}}
{content}
完成 JSON
文件的创建后可通过下图 PostMan
中入口设置。
2. 批量插入
(1) 索引不存在
批量插入数据,索引不存在时新建。
POST http://<ip>:<port>/_bulk
// 文件内容
{"create":{"_index": "<index_name>","_type": "_doc","_id": "1"}}
{"id": "1", "name":"alex"}
{"create":{"_index": "<index_name>","_type": "_doc","_id": "2"}}
{"id": "2", "name":"beth"}
(2) 索引存在
批量插入数据,索引必须已存在。
POST http://<ip>:<port>/<index>/_doc/_bulk
// 文件内容
{"create":{"_id": "1"}}
{"id": "1", "name":"alex"}
{"create":{"_id": "2"}}
{"id": "2", "name":"beth"}
(2) 增量插入
批量插入数据,若数据已存在则覆盖。
POST http://<ip>:<port>/<index>/_doc/_bulk
// 文件内容
{"index":{"_id": "1"}}
{"id": "1", "name":"alex"}
{"index":{"_id": "2"}}
{"id": "2", "name":"beth"}
3. 批量更新
批量更新的请求模板示例如下:
POST http://<ip>:<port>/<index>/_doc/_bulk
// 文件内容
{"update":{"_id": "1"}}
{"doc": {"id": "1"}}
{"update":{"_id": "2"}}
{"doc": {"id":"2"}}
4. 批量删除
批量删除的请求模板示例如下:
POST http://<ip>:<port>/<index>/_doc/_bulk
// 文件内容
{"delete":{"_id": "1"}}
{"delete":{"_id": "2"}}
五、数据查询
1. 结构查询
通过 _mapping
查询当前索引下的数据字段结构。
GET http://<ip>:<port>/<index>/_mapping
2. 查询得分
在 Elasticsearch
中,每个文档的匹配程度可以通过一个称为 “分数”(score
)的值来表示。分数反映了文档与查询条件的匹配程度,越匹配的文档得分越高。
当执行查询时,Elasticsearch
计算每个文档的得分,然后按照得分排序返回结果。这个得分是由查询条件中的每个子句(例如,匹配关键词的 must
子句)的相关性计算得出的。
对于 bool
查询中的 must
子句,其中的每个条件都会影响文档的得分。如果一个文档满足多个 must
子句的条件,那么它的得分会相应增加。这样,得分可以用来衡量文档与查询条件的匹配程度。
而对于 filter
子句,其中的条件也用于筛选文档,但它们不会影响文档的得分。filter
子句主要用于对文档进行精确匹配和过滤,而不涉及相关性计算。由于不计算得分,filter
子句的查询更轻量,对于一些不需要计算相关性的场景,这可以提高性能。
因此,当使用 filter
子句时,查询的主要目的是过滤文档,而不是影响查询结果的排序和相关性。这对于一些特定的过滤条件(如范围查询、精确匹配等)非常有用,因为它们可以更高效地执行,而无需计算和排序相关性得分。
3. 全量查询
通过 Get
查询目标数据,效果等价于 select * from <table>
。
默认查询仅返回十条数据,若查看多数据需分页进行,同时通过 pretty=true
可格式化返回的结果数据。
GET http://<ip>:<port>/<index_name>/_search
GET http://<ip>:<port>/<index_name>/_search?pretty=true
4. 分页查询
分页查询同理,在请求体内通过 from
与 size
指定分页参数,作用类似 MySQL
中的 limit
和 offset
。
// paging query data
GET http://<ip>:<port>/<index_name>/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 10
}
六、条件查询
1. 精准查询
精准查询即与常见关系型中 where
条件中的 =
作用等价。
下述示例的作用效果即等价于 select * from <table> where <column_name> = <column_name>
。
GET http://<ip>:<port>/<index_name>/_search
{
"query": {
"bool": {
"must": [
{
"match_phrase": {
"<column_name>": {
"query": "<column_value>"
}
}
}
]
}
}
}
2. 区间查询
区间查询即与常见关系型中 where
条件中的 in
作用等价。
下述示例的作用效果即等价于 select * from <table> where <column_name> in (1, 2, 3)
。
GET http://<ip>:<port>/<index_name>/_search
{
"query": {
"bool": {
"must": [
{
"terms": {
"<column_name>": [1, 2, 3]
}
}
]
}
}
}
3. 范围查询
范围查询即与常见关系型中 where
条件中的 >, >=, <, <=
作用等价。
其中不同的符号对应参考下表:
范围符 | ES 关键字 |
---|---|
> | gt |
>= | from |
< | lt |
<= | to |
下述示例的作用效果即等价于 select * from <table> where <column_name> >= 1
。
GET http://<ip>:<port>/<index_name>/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"<column_name>": {
"from": "1"
}
}
}
]
}
}
}
七、聚合查询
1. 统计查询
select count(*) from <table_name>
其中 count_num_rows
为自定义名称,value_count
等价于 count(*)
,aggregations
可简写为 aggs
。
GET http://<ip>:<port>/<index>/_search
{
"size": 0,
"aggregations": {
"count_num_rows": {
"value_count": {
"field": "_index"
}
}
}
}
2. 分组查询
select
DATE_FORMAT('<time_field>', '%Y-%m-%d %H:00:00') as time,
COUNT(*) AS num
from
<table_name>
group by
time
having
num > 0;
注意字段 <time_field>
的类型 type
必须为 date
,其中 interval
的可选值如下:
参数 | 描述 |
---|---|
year | 年 |
quarter | 季度 |
month | 月 |
week | 周 |
day | 日 |
hour | 小时 |
minute | 分钟 |
second | 秒 |
{
"size": 0,
"aggs": {
"group_date": {
"date_histogram": {
"field": "<time_field>",
"interval": "hour",
"time_zone": "+08:00",
"format": "yyyy-MM-dd HH:mm:ss"
},
"aggs": {
"num_filter": {
"bucket_selector": {
"buckets_path": {
"count": "_count"
},
"script": "params.count > 0"
}
}
}
}
}
}