ElasticSearch(简称ES)

ElasticSearch(以下简称ES)

ES是基于Apache Lucene实现的开源全文搜索引擎,提供了REST API的操作接口,开箱即用。


安装 & 启动(9200)

直接安装

  1. 安装Java 8环境
  2. 通过yum install -y wget安装wget
  3. 安装ES
1
2
3
4
5
6
7
8
9
10
11
cd /usr/DragonBaby308
mkdir es5.5.1
cd es5.5.1
# 下载5.5.1版本,没有选择最新的7.0是因为7.0要求JDK11
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.zip
# 解压
yum install -y unzip zip
unzip elasticsearch-5.5.1.zip
# 启动
cd elasticsearch-5.5.1/
./bin/elasticsearch

Debug

报错:

1
2
3
4
5
6
Java HotSpot(TM) Server VM warning: INFO: os::commit_memory(0x6ac00000, 2080374784, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 2080374784 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /usr/DragonBaby308/es5.5.1/elasticsearch-5.5.1/hs_err_pid17540.log

原因:
这是因为服务器上剩余的物理内存太小,无法给JVM分配足够的内存导致的(ES默认堆大小是2G,我的弟弟服务器总内存才1G - -|||)。

解决方法:
export ES_JAVA_OPTS="-Xmx128m -Xms128m",将堆大小设置为128m,尝个甜头就完事了。

JVM中(或是其他用到JVM的软件中),但凡将最大堆(-Xmx)和最小堆(-Xms)设置为相同数值,都是为了防止自动扩容

设置完毕后,再次./bin/elasticsearch启动ES。如果一切正常,默认会在9200端口启动。
新开一个命令行,通过curl http://localhost:9200/即可访问ES,会返回一个包含结点、集群、版本等信息的JSON对象。

es

安装中文分词插件ik

1
2
3
4
# 安装中文分词插件ik
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.5.1/elasticsearch-analysis-ik-5.5.1.zip
# 重启ES
./bin/elasticsearch

通过Docker-Compose安装ELK

ELK = ElasticSearch + Logstash + Kibana

ElasticSearch基于JSON的分布式全文搜索引擎
Logstash动态数据收集管道。一般用于监听应用的日志目录,将日志转为JSON格式后传送给ES
Kibana为用户提供ES数据可视化UI

  1. docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3'
services:
elasticsearch:
image: elasticsearch:7.3.1
environment:
discovery.type: single-node
ports:
- "9200:9200"
- "9300:9300"
logstash:
image: logstash:7.3.1
command: logstash -f /etc/logstash/conf.d/logstash.conf
volumes:
# 挂载logstash配置文件
- ./config:/etc/logstash/conf.d
- /user/elk/logs/:/opt/build/
ports:
- "5000:5000"
kibana:
image: kibana:7.3.1
environment:
-ELASTICSEARCH_URL=http://elasticsearch:9200
ports:
- "5601:5601"
  1. docker-compose up

基础概念

1.节点(Node) & 集群(Cluster)

ES从本质上来说就是分布式的数据库,允许多台服务器协同工作,多台服务器可以运行多个ES实例。
单个ES实例称为一个节点(Node),一组节点称为集群(Cluster)。

2.索引(Index):/_cat/indices?v

ES会索引所有字段,经过处理后写入一个反向索引(Inverted Index),查找数据时,直接查找该索引。

反向索引(Inverted Index):
正向索引是从文档(document)建立到词语(term)的索引(index),而反向索引(Inverted Index)则是从词语建立到文档的索引
比如:使用不同的数字索引不同不同的句子
0: “I love you”
1: “I love you too”
2: “I dislike you”
这就是一个正向索引。但如果我们改一下,使用单词作为索引,将句子的位置作为被索引的元素:
“I”: {0,1,2}
“love”: {0,1}
“you”: {0,1,2}
“dislike”: {2}
这就建立起了一个反向索引。
如果我们要搜索“I dislike you”,就相当于对3个反向索引求交集——{0,1,2} ∩ {2} ∩ {0,1,2},找到的就是文档2。
一般搜索引擎的原理就是反向索引,ES的原理也是。

所以ES数据管理的顶层节点就是索引(Index),每一个索引就相当于一个数据库,名字必须小写

查看当前节点下所有Indexcurl -X GET http://localhost:9200/_cat/indices?v

3.文档(Document)

Index中的每一条记录称为文档(Doucument),每一条Document都采用JSON格式表示,如:

1
2
3
4
5
{
"user":"张三",
"title":"工程师",
"desc":"数据库管理"
}

4.分组(Type):/mapping?pretty=true

Document可以进行分组(Type),不同的Type应该具有相同的结构。

ES 6.x只允许每个Index包含一个Type,ES 7.X彻底移除了Type。

查看所有Index下的Type:curl 'http://localhost:9200/mapping?pretty=true'


API

1.新建Index:PUT /index -d ‘json’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
curl -X PUT 'localhost:9200/accounts' -d '
{
"mappings": {
"person": {
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}'

以上代码中,创建了一个Index:accounts,其下有一个Type:person,person有3个字段:user、title、desc

2.删除Index:DELETE /index

curl -X DELETE 'localhost:9200/accounts'

3.插入Document:PUT/ POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# PUT /index/type/id -d 'document'
curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}'

# POST 可以不指定id
curl -X POST 'localhost:9200/accounts/person' -d '
{
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}'

4.查找Document:/Index/Type/id?pretty=true

curl 'localhost:9200/accounts/person/1?pretty=true'

5.删除Document:DELETE /Index/Type/id

curl -X DELETE 'localhost:9200/accounts/person/1'

6.更新Document:PUT /Index/Type/id -d ‘json’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}'

{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_version":2,
"result":"updated",
"_shards":{"total":2,"successful":1,"failed":0},
"created":false
}

curl 'localhost:9200/accounts/person/_search'

8.全文搜索:/Index/Type/_search -d ‘json’

1
2
3
4
curl 'localhost:9200/accounts/person/_search'  -d '
{
"query" : { "match" : { "desc" : "软件" }}
}'

底层原理

0.角色

  1. Shard: 分片,单个分片内可以有多个节点,一主多从

  2. Node: 节点,即单个机器

    primary node主节点
    replica node副本节点,同步复制主节点
    coordinating node协调节点,客户端请求发送到的节点,用于处理所有节点返回的数据,并将数据返回给客户端

  3. Document: 文档,即数据,每个文档都有自己的doc id

1.写数据

  • 写请求只能发送到primary node,然后由primary node同步到所有replica node
  1. 客户端将写请求发送到某个node,这个node就是coordinating node
  2. coordinating nodedocument进行路由,将数据写入对应shardprimary node
  3. primary node将数据同步到所有对应的replica node
  4. primary nodereplica node写入成功,coordinating node才会返回成功给客户端

(1)插入

  1. 数据写入内存buffer
  2. 每隔1s,将内存bufferrefresh到OS Cache
  3. 每隔5s,将OS; Cache中的数据持久化到translog日志
  4. translog日志大到一定程度,或是默认每隔30min,会触发commit操作,将缓冲区数据flushsegment file磁盘文件
  5. 数据写入segment file就建立了反向索引

(2)删除

  1. “删除”和“插入”大同小异,不同的是commit操作时会生成一个.del文件,里面将某些document标识为deleted状态,那么搜索的时候根据.del文件就知道document是否已经被删除

    此时是逻辑删除

  2. buffer每次refresh产生一个segment file,默认1s一个,segment file越来越多,会定期执行merge操作,将多个segment file合并为一个,此时会将所有标识为deleteddocument物理删除,然后将新的segment file写入磁盘,删除原有的segment file

    有点类似于Redis惰性删除 + 定期删除策略。

(3)更新

  1. “更新”和“删除”大同小异,同样是在commit时生成一个.del文件,记录所有标识为deleteddocument
  2. 不同点在于生成.del文件后,会“插入”新数据

2.读数据

  • 读请求可以发送到primary node/replica node,通过round-robin随机轮询做负载均衡
  1. 客户端请求发送到任意node,该节点成为coordinating node
  2. coordinating node根据doc id进行hash路由,将请求发送到对应的shard,然后根据round-robin随机轮询算法,将请求分发到primary node/replica node做负载均衡
  3. 接收请求的nodedocument返回给coordinating node
  4. coordinating nodedocument返回给客户端

3.全文搜索

  1. 客户端将搜索请求发送给协调者节点coordinating node
  2. coordinating node将搜索请求发送给所有的primary nodereplica node

    replica是同步primary的,所以两者数据是一致的,

读请求发送给primary/replica都可以

  1. 收到搜索请求的shard搜索自己的数据,返回一系列的doc idcoordinating node将所有收到的doc id进行合并、排序、分页……,产出最终结果
  2. coordinating node根据doc id去各个shard拉取实际的document数据,返回给客户端

常见问题

1.ES基于JVM进行存储,那么是不是堆内存越大越好?

  1. 基于JVM内存的应用,往往会受到GC的节制,所以它们一般不会选择使用JVM的内存进行存储
  2. ES使用的是OS Cache持久化存储数据,而非JVM堆内存
  3. JVM堆内存分配得越大,OS Cache能使用的内存就越小,所以并不是堆内存越大越好

2.ES全文搜索的原理是什么?

反向索引。

反向索引(Inverted Index):
正向索引是从文档(document)建立到词语(term)的索引(index),而反向索引(Inverted Index)则是从词语建立到文档的索引
比如:使用不同的数字索引不同不同的句子
0: “I love you”
1: “I love you too”
2: “I dislike you”
这就是一个正向索引。但如果我们改一下,使用单词作为索引,将句子的位置作为被索引的元素:
“I”: {0,1,2}
“love”: {0,1}
“you”: {0,1,2}
“dislike”: {2}
这就建立起了一个反向索引。
如果我们要搜索“I dislike you”,就相当于对3个反向索引求交集——{0,1,2} ∩ {2} ∩ {0,1,2},找到的就是文档2。
一般搜索引擎的原理就是反向索引,ES的原理也是。

3.ES写入的数据立马就能搜索到吗?

  • 不行,数据写入内存buffer后,1s后才会同步到OS Cache,这时才能被查询到
  • 也就是说,ES数据的写入和查询之间会有1s的延迟

4.ES如果宕机,会丢失多久的数据?

  • ES每隔5sOS Cache中的数据持久化到translog日志,所以如果宕机,也最多只丢失5s的数据
-------------本文结束感谢您的阅读-------------

本文标题:ElasticSearch(简称ES)

文章作者:DragonBaby308

发布时间:2019年07月21日 - 10:12

最后更新:2020年01月25日 - 12:49

原始链接:http://www.dragonbaby308.com/es/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%