Elasticsearch 入门
Elasticsearch 是一个分布式的、可扩展的、近实时的开源搜索分析引擎。它支持 PB 级的容量,在存储时通过对文档的分析和建立索引,使之可以被全文搜索、分析。它的底层基于开源库 Luceue 。Elasticsearch 对 Luceue 进行了封装并提供了 REST API 的操作接口,开箱即用。
基本概念
概念对比
学习新内容时,最容易的理解方式是用它和已经熟悉的概念进行比较。下面是 Elasticsearch 与 Mysql 对应概念的比较。
概念 | Mysql | Elasticsearch |
---|---|---|
Index 索引 | 数据库 | 一个逻辑上存储文档的地方,可以是集群 |
Type 类型 | 数据表 | 一个索引可以在逻辑上分为不同的类型。与 Mysql 不同之处在于一个索引中的不同类型的字段是相同的;而数据库中不同表的字段基本上是不同的。 |
Document 文档 | 数据行 | 一个文档对象所有的键和值,是 JSON 格式,比 Mysql 复杂,可以包含对象、数组 |
Mapping 映射 | 数据类型 | 除了描述文档中每个值的数据类型,还包括字段的 index(怎样索引字符串) , analyzer(指定搜索和索引时使用的分析器) |
shard 主分片 | 无 | 一个分片是一个 Lucene 索引。一个 ES 索引的分片数是在创建索引的时候设置的,因为有了分片,ES 水平扩展时变得容易 |
replica 副本分片 | 类似从库 | ES 的副本分片可以在创建时设置,在创建后修改 |
一个 Elasticsearch 服务的实例为一个节点,一个节点可以包含多个分片,至少需要包含一个分片。
Elasticsearch 的存储容量大小为所有的主分片容量决定。所以在创建索引的时候需要考虑分片预分配。默认为 5 个主分片,也就是最后水平可扩展最大的容量为 5 个 ES 服务实例。
对比例子
下面是使用 Mysql 创建一个雇员数据库和使用 Elasticsearch 创建一个雇员索引的比较。
雇员属性有:first_name、last_name、age、about (简介)
使用 Mysql 创建
- 创建数据库 megacorp 。
- 创建表 employee ,定义表的字段。设置字段的长度、类型,根据需要设置表的索引。
- 插入数据。
使用 Elasticsearch 创建
- 创建索引,创建时确定索引的分片数、副本分片数。如果不指定, ES 默认 5 分片、1 副本分片。
- 设置索引的 Mapping 。 比如 about 用 text 类型存储,age 用 long 类型。about 需要使用 ik-analyzer 中文分词器,这样就可以通过简介的内容搜索对应的雇员;而 age 设置为 not_analyzed (建立索引的时候,直接对 age 字段值建立索引,不会对内容进行分析,基本类型默认 date、long 默认为 not_analyzed)。
- 索引雇员文档到 Elasticsearch 中。
默认情况下,文档中每一个属性都会被索引,没有索引的属性是不能被搜索的。
Elasticsearch 集群搭建
使用 Docker-compose 可以让搭建 Elasticsearch 变得更简单。
集群文件目录如下:
Docker-compose 目录结构.png
docker-compose.yml 文档内容
version: '2.2'
services:
elasticsearch:
image: elasticsearch:6.5.1
container_name: es
environment:
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./es1/data:/usr/share/elasticsearch/data
- ./es1/es.yml:/usr/share/elasticsearch/config/elasticsearch.yml
ports:
- 9200:9200
- 9300:9300
networks:
- esnet
elasticsearch2:
image: elasticsearch:6.5.1
container_name: es2
environment:
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./es2/data:/usr/share/elasticsearch/data
- ./es2/es.yml:/usr/share/elasticsearch/config/elasticsearch.yml
networks:
- esnet
elasticsearch3:
image: elasticsearch:6.5.1
container_name: es3
environment:
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./es3/data:/usr/share/elasticsearch/data
- ./es3/es.yml:/usr/share/elasticsearch/config/elasticsearch.yml
networks:
- esnet
networks:
esnet:
ES_JAVA_OPTS 设置 ES 使用的最大与最小 heap 空间。
主节点 es.yml
cluster.name: es-cluster
bootstrap.memory_lock: true
node.name: node-master
node.master: true
node.data: true
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
network.publish_host: es
transport.tcp.compress: true
discovery.zen.ping.unicast.hosts: ["es:9300", "es1:9300", "es3:9300"]
cluster.name 为 ES 集群名称,集群所有的节点必须是相同的名称。
bootstrap.memory_lock 用于锁定 ES 进程使用的内存地址,防止被 swap out
node.name 为节点的名称
node.master 是否为主节点
node.data 节点是否存储数据
http.port 为外部访问 ES 服务的端口
transport.tcp.port 为 ES 集群通信的端口
transport.tcp.compress 集群通信时进行压缩
discovery.zen.ping.unicast.hosts 单播发现。 ES 将在这里面的地址去通信发现集群节点
工作节点 es.yml
cluster.name: es-cluster
bootstrap.memory_lock: true
node.name: node-master-3
node.master: false
node.data: true
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
network.publish_host: es3
discovery.zen.ping.unicast.hosts: ["es:9300", "es2:9300", "es3:9300"]
工作节点与主节点主要是 node.master 的配置不同
docker-compose up
这样一个主节点、2个工作节点的 Elasticsearch 集群就搭建起来了。
结构化搜索实践
一个 Elasticsearch 的查询包含:
- 查询内容所在的索引、类型。 多个索引、多个类型之间使用“,”分割。
- 路由 routing 。如果数据建立索引的时候有指定路由,查询的时候可以指定路由,这样 ES 就不用到每个分片去查询一次了。
- 查询语句 query 。
- 排序。根据指定字段、排序方式进行排序。
- 分页。可以通过 from、limit 查询指定的数据。
一个简单的 DSL SQL
curl -X GET "localhost:9200/bank/account/_search" -d '
{
"query" : {
"match": {
"title" : "search"
}
},
"from": 10,
"size": 10,
"sort": [
{"date": {"order":"asc"}}
]
}'
bank 为查询的索引; account 为查询的类型;query 中为查询语句。
Elasticsearch 中的查询分为简单查询、组合查询。
常用的简单查询又分为了查询(query context)、过滤(filter context)。查询时不仅要找到匹配对应内容的文档,还会计算文档的分数(score), 过滤只需找到对应的文档,而不计算文档的分数。官方推荐能用过滤的地方就不要用查询。
简单查询
- match_all 和 match_none
查询所有的内容
curl -X GET "localhost:9200/bank/_search" -d '
{
"query" : {
"match_all": {}
}
}'
- 全文索引
a. match 指定的分析器先对查询内容进行分析,然后对分析的内容在倒排索引中进行查询; match_phrase 也会先将搜索内容分析为词项,然后对词项进行搜索,但是匹配的数据需要包含所有的词项。
curl -X GET "localhost:9200/bank/account/_search" -d '
{
"query" : {
"match": {"title":"search"}
}
}'
b. multi_match 通过对多个字段进行 match 搜索
curl -X GET "localhost:9200/bank/_search" -d '
{
"query" : {
"multi_match": {
"query":"this is test",
"fields": ["title", "subtitle"]
}
}
}'
c. query_string 通过查询语句进行查询
new york city 和 big apple 是两个独立的查询内容
curl -X GET "localhost:9200/bank/_search" -d '
{
"query" : {
"query_string": {
"query":"(new york city) OR (big apple)",
"default_field": ["subtitle"]
}
}
}'
- Term 查询
全文查询在查询前会将查询内容分析为词项,然后进行查询; term 查询不对查询内容进行分析,是对值进行精确的查询。Term 查询不会计算查询结果的分数(score),属于过滤查询(filter context)。
a. term 和 terms
对指定的一个和多个内容进行精确查询
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"terms" : { "user" : ["kimchy", "elasticsearch"]}
}
}
'
b. range 查询
文档属性值在一个范围的查询。可以是字符串、数字、日期。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"range" : {
"age" : {
"gte" : 10,
"lte" : 20
}
}
}
}'
c. exists 查询
查找指定属性至少又一个非空值的文档。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"exists" : { "field" : "user" }
}
}'
d. wildcard 查询
根据通配符进行查询(? 匹配1个任意字符;* 匹配多个任意字符)。查询会耗性能。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"wildcard" : { "user" : "ki*y" }
}
}'
复合查询
将多个简单查询通过组合起来就是一个复合查询了。常用的组合方式有下面几种。
- constant_score。这个查询里面的查询将不再计算分数,统一返回指定的分数。
curl -X GET "localhost:9200/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"constant_score" : {
"filter" : {
"term" : { "user" : "kimchy"}
},
"boost" : 2
}
}
}'
查询结果所有文档的 _score 都为 2 。
- bool
逻辑组合,包括 must、should、must_not、filter。must 和 should 的查询会计算分数,其他的不会。
must 必须包含; should 单独查询的时候,至少需要满足一个条件,与 must、filter 组合的时候可以不需要满足。