千万人的失败,都有是失败在做事不彻底,往往做到离成功只差一步就终止不做了。
什么是elasticsearch
Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。
-
我们先来看一下Elasticsearch的服务架构
最底层Gateway是ES用来存储索引的文件系统,支持多种类型。
Gateway的上层是一个分布式的lucene框架。
Lucene之上是ES的模块,包括:索引模块、搜索模块、映射解析模块等。
-
ES模块之上是 Discovery、Scripting和第三方插件。
Discovery是ES的节点发现模块,不同机器上的ES节点要组成集群需要进行消息通信,集群内部需要选举master节点,这些工作都是由Discovery模块完成。支持多种发现机制,如 Zen 、EC2、gce、Azure。 Scripting用来支持在查询语句中插入javascript、python等脚本语言,scripting模块负责解析这些脚本,使用脚本语句性能稍低。ES也支持多种第三方插件。
上层是ES的传输模块和JMX。传输模块支持多种传输协议,如 Thrift、memecached、http,默认使用http。JMX是java的管理框架,用来管理ES应用。
最上层是ES提供给用户的接口,可以通过RESTful接口和ES集群进行交互。
elasticsearch的集群架构
这篇文章对集群的讲解很到位
参考文章
Cluster:集群,由一个或多个 Elasticsearch 节点组成。
-
Node:节点,组成 Elasticsearch 集群的服务单元,同一个集群内节点的名字不能重复。通常在一个节点上分配一个或者多个分片。
Elasticsearch 的节点的分类如下: 1.主节点(master node):负责创建、删除索引;分配分片、追踪集群的节点状态等工作,对机器的性能要求相对较低。 2.数据节点(data node):负责数据的存储和其他操作,比如对索引数据的增删改查和聚合等操作,对机器的配置和要求都比较高。首先是对硬盘的空间要求比较大,其次是对cpu memory 和机器io的性能消耗很大,数据节点可以通过配置文件新增节点 3.客户端节点(client node):有负责集群状态的节点,有存储和操作数据的节点,总得需要和用户交互吧,对,这些接收用户请求并返回结果的节点,就是 客户端节点,任何一个节点都可以作为客户端节点,负责请求的 分发,汇总。
-
集群状态有三种,green yellow和red
Green:绿色,健康。所有的主分片和副本分片都可正常工作,集群 100% 健康。 Yellow:黄色,预警。所有的主分片都可以正常工作,但至少有一个副本分片是不能正常工作的。此时集群可以正常工作,但是集群的高可用性在某种程度上被弱化。 Red:红色,集群不可正常使用。集群中至少有一个分片的主分片及它的全部副本分片都不可正常工作。这时虽然集群的查询操作还可以进行,但是也只能返回部分数据(其他正常分片的数据可以返回),而分配到这个分片上的写入请求将会报错,最终会导致数据的丢失。
3c和脑裂
1.共识性(consensus),即在分布式系统中,所有的节点必须对给定的数据或者节点状态达成共识,elasticsearch使用zen discovery完成共识
2.并发(concurrency),我们知道,elasticsearch 是一个分布式系统,当一个写请求到达主分片之后,还需要同步给备份分片,那么为什么会有备份分片呢?我们看下面这个图
我们先考虑一下为什么会有分片,首先我们知道,一个索引可以存储超出单个节点硬件限制的大量数据。比如集群里面,每个节点最多存储500g数据,但是我们又10亿文档索引,需要占据1tb的磁盘空间,怎么办?这个时候,我们就可以像把一桶水倒入 多个水杯一样,一个水杯放不下,我们就多使用几个水杯,这里的水杯就是我们的分片,分片不单单解决了数据量大的问题,而且提高了服务的性能,所以说分片有两个作用:1.允许水平分割或者扩展内容容量 2.允许你再分片上进行分布式、并行的操作,提高服务的性能和io
那么好了,既然我们的对数据的操作是基于分片实现的,那么会不会某个分片就丢了,坏了的情况呢?或者某个节点暂停服务了,那么这个节点上面的分片的数据也就查询不到了。怎么办?Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做备份分片,备份分片提高了系统的高可用,另一方面,备份分片也可以用于搜索。
备份分片的数量可以在创建索引的 时候指定,也可以动态指定。但是分片的数量必须在索引创建的时候指定,并且不可修改,所以至于指定多少分片,我们可以根据实际的存储量决定,一般一个分片可以容纳20-40g的容量
我们接着说并发,Elasticsearch怎么来控制并发带来的数据不同步问题呢?当然是上锁,Elasticsearch使用的是乐观锁,乐观锁有两种
- 乐观并发控制(occ):是用来解决 写冲突的无锁并发控制,乐观锁任务事务之间的竞争并不激烈,所以只有在提交事务的时候才会检测数据有没有发生变化,如果发生变化了,就放弃提交并重试。这种锁适用于数据竞争较少的环境
- 多版本并发控制(mvcc):用来解决读写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳。每次都保存,但是还保存了版本号,版本号和事务时间戳关联的,读操作 只读该事务开始前的数据库的快照
3.一致性(consistency):Elasticsearch集群保证写一致性的方式是在写入之前先检查集群的状态,看看又多少分片可以写入,如果达到某个调价,就写入,否则就等待,默认一分钟后重试,而这个条件是可以设置的
- one:只要主分片可以用,就可以写
- all:所有分片,包括主分片和备份分片都可以用,才可以写
- quorum :就是 大部分分片可以用的时候允许写入,对 “大部分” 的计算公式为 int ((primary+number_of_replicas)/2)+1。
- Elasticsearch 集群保证读写一致性的方式是,为了保证搜索请求的返回结果是最新版本的文档,备份可以被设置为 Sync(默认值),写操作在主分片和备份分片同时完成后才会返回写请求的结果。
这样,无论搜索请求至哪个分片都会返回最新的文档。但是如果我们的应用对写要求很高,就可以通过设置 replication=async 来提升写的效率,如果设置 replication=async,则只要主分片的写完成,就会返回写成功。
4.脑裂
在集群中,主节点会定时ping 其他节点的可用状态,其他节点也ping主节点,当集群网络不稳定的时候,可能会发生一个节点ping不通主节点,那么就认为主节点宕机,就会重新选举一个新的主节点,这个时候一个集群就会出现多个master,这就是脑裂,当一个集群中出现多个master的时候,可能会导致数据丢失。
如何理解倒排索引?
参考文章
我们知道,Lucene是基于倒排表存储索引的。elasticsearch也是基于Lucene做的封装,那么什么是倒排索引呢?如果我让你找包含 故乡 这个词的诗句,你可能要犹豫一下,然后想起来 《静夜思》低头思故乡 和 《月夜忆舍弟》 月是故乡明;但是如果如果直接问你 静夜思和月夜忆舍弟,你很快就背诵出来了。我们往往都记得标题,忘了内容。那么怎么样我们搜索内容的时候,才可以快速找到标题呢,这就用到了倒排索引,我们给每一篇文章打上tag ,再根据tag 找到对应的文章
参考文章
- 接下来我们实际操作一下
演示环境
- centos7
- elasticsearch7.2.0
下载elasticsearch
简单启动
- 创建非root用户
useradd elastic
#下载tar包并解压目录
[elastic@localhost ~]$ tar zxvf elasticsearch-7.2.0-linux-x86_64.tar.gz
#进入目录启动
[elastic@localhost bin]$ pwd
/home/elastic/elasticsearch-7.2.0/bin
#启动
[elastic@localhost bin]$ ./elasticsearch
- 这期间可能有以下报错
max number of threads [3795] for user [elastic] is too low, increase to at least [4096]
原因是 elasticsearch 启动的时候要求当前用户最大线程数至少为 4096 个线程,而操作系统限制该用户最大线程数为 3795,只需要修改当前用户的最大线程数即可。可以使用ulimit -a查看当前用户允许的最大线程数
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3795
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 3795
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
可以看到 max user processes 确实为 3795
在 Linux 中,用户允许的最大线程数的配置文件为/etc/security/limits.conf,我们需要在该配置文件中添加下面配置
# elastic是登录用户名,配置完毕后需要退出重新登录
elastic - nproc 65535
- 退出并重新登录查看max user processes 为65535
[elastic@localhost ~]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3795
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 65535
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
- 此时再次启动,启动成功
开启一个节点
多个节点的部署
可以使用多台服务器部署 ,或者同一台服务器多个节点部署也可以,端口号不一样就行了,来看一下节点1的配置
[elastic@localhost bin]$ cat ../config/elasticsearch.yml
# ======================== Elasticsearch Configuration =========================
#集群名称
cluster.name: cluster1
#节点名称 集群中保证唯一
node.name: node1
#主节点属性值,表示是否有成为主节点的资格
node.master: true
# 节点是否存储数据
node.data: true
# 本节点的ip
network.host: 172.16.131.5
# 本节点的端口
http.port: 9200
# 集群之间的通讯端口
transport.port: 9300
# 种子节点
discovery.seed_hosts: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
# 初始化的时候需要配置一些具有主节点属性的节点
cluster.initial_master_nodes: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
- 本次的多节点 部署在同一台服务器
[elastic@localhost ~]$ cp -r elasticsearch-7.2.0 elasticsearch2
[elastic@localhost ~]$ cp -r elasticsearch-7.2.0 elasticsearch3
[elastic@localhost ~]$ ls
elasticsearch2 elasticsearch3 elasticsearch-7.2.0 elasticsearch-7.2.0-linux-x86_64.tar.gz
- 我们配置不同的端口号并启动
节点2
[elastic@localhost config]$ cat elasticsearch.yml
# ======================== Elasticsearch Configuration =========================
cluster.name: cluster1
node.name: node2
node.master: true
node.data: true
node.ingest: true
network.host: 172.16.131.5
http.port: 9201
transport.port: 9301
discovery.seed_hosts: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
cluster.initial_master_nodes: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
- 节点3
cluster.name: cluster1
node.name: node3
node.master: true
node.data: true
node.ingest: true
network.host: 172.16.131.5
http.port: 9202
transport.port: 9302
discovery.seed_hosts: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
cluster.initial_master_nodes: ["172.16.131.5:9300","172.16.131.5:9301","172.16.131.5:9302"]
- 在启动节点3的时候报了错
failed; error='Not enough space' (errno=12)
,因为我虚拟机的内存是1g,可能不够了,这个我们一会再解决,我们先看一下启动状态 - 但是当我启动之后,发现配置没有任何问题,但是两个节点不在 一个集群
-
后来经过请教大佬,是因为我之前单节点启动过一次,在elastic/data 下面会生成一个nodes目录,由于启动时声明了单节点,所以这个nodes 下的目录不会每次启动时检测节点。所以新的节点不会被加入当前服务,那么此时,删除nodes目录并重新启动服务即可
- 另外一点建议是cluster.initial_master_nodes里面最好是配置节点的名字
cluster.name: cluster1
node.name: node2
node.master: true
node.data: true
node.ingest: true
network.host: 172.16.131.5
http.port: 9201
transport.port: 9301
discovery.seed_hosts: ["172.16.131.5:9300","172.16.131.5:9301"]
cluster.initial_master_nodes: ["node1","node2"]
- 此时,我们的单台服务器多节点 部署成功,但是我们来看刚才遗留的问题,在启动节点3的时候报了错
failed; error='Not enough space' (errno=12)
在前面我们配置过 jvm.option 这个文件,但是查看我们的内存,发现只有1个G,所以通过修改Xms和Xmx的值大小可以避免这个问题,比如-Xms108m -Xmx108m
,但是为了避免以后实践过程中内存不足的问题,不建议这样做
[elastic@localhost config]$ cat jvm.options
#启动时分配的内存
-Xms1g
#运行过程中分配的最大内存
-Xmx1g
[elastic@localhost config]$ cat /proc/meminfo | grep Mem
MemTotal: 995672 kB
MemFree: 490128 kB
MemAvailable: 476936 kB
使用docker安装elastic和启动方式(单机)
使用docker安装elastic和启动方式(集群)
安装kibana
- 下载地址点击下载
- 解压后进入config目录配置文件
[elastic@localhost config]$ vi /home/elastic/kibana-7.2.0-linux-x86_64/config/kibana.yml
server.port: 5601
server.host: "172.16.131.5"
elasticsearch.hosts: ["http://172.16.131.5:9200"]
# 启动kibana
nohup ./bin/kibana --allow-root & > /dev/null 2>&1