一、ElasticSearch安装部署
1、下载软件包
- 前往官网下载,下载对应平台的软件包
2、解压软件包
- 解压命令
tar -zxvf ./elasticsearch-7.8.0-linux-x86_64.tar.gz
3、创建启动用户并启动应用
elasticsearch为了安全,不能用root用户运行应用实例,需要为实例创建用户。如果用root用户运行,会出现以下异常。
java.lang.RuntimeException: can not run elasticsearch as root
- 创建用户命令
adduser elasticsearch
- 修改用户密码
passwd elasticsearch
- 将es解压目录的所有者授予此用户
chown -R elasticsearch elasticsearch-7.8.0
- 切换至elasticsearch用户
su elasticsearch
- 进入es安装目录,运行启动脚本
/usr/local/elasticsearch-7.8.0/bin/elasticsearch -d
4、重要的系统配置
#配置 ES 的集群名称,默认值是 ES,建议改成与所存数据相关的名称,ES 会自动发现在同一网段下的集群名称相同的节点。
cluster.name:elasticsearch
#集群中的节点名,在同一个集群中不能重复。节点的名称一旦设置,就不能再改变了。当然,也可以设置成服务器的主机名称,例如 node.name:${HOSTNAME}。
node.nam: "node1"
#指定该节点是否有资格被选举成为 Master 节点,默认是 True,如果被设置为 True,则只是有资格成为 Master 节点,具体能否成为 Master 节点,需要通过选举产生。
noed.master:true
#指定该节点是否存储索引数据,默认为 True。数据的增、删、改、查都是在 Data 节点完成的。
node.data:true
#设置都索引分片个数,默认是 5 片。也可以在创建索引时设置该值,具体设置为多大都值要根据数据量的大小来定。如果数据量不大,则设置成 1 时效率最高。
index.number_of_shards:5
#设置默认的索引副本个数,默认为 1 个。副本数越多,集群的可用性越好,但是写索引时需要同步的数据越多。
index.number_of_replicas:1
#设置配置文件的存储路径,默认是 ES 目录下的 Conf 文件夹。建议使用默认值。
path.conf:/path/to/conf
#设置索引数据多存储路径,默认是 ES 根目录下的 Data 文件夹。切记不要使用默认值,因为若 ES 进行了升级,则有可能数据全部丢失。 可以用半角逗号隔开设置的多个存储路径,在多硬盘的服务器上设置多个存储路径是很有必要的。
path.data:/path/to/data1,/path/to/data2
#设置日志文件的存储路径,默认是 ES 根目录下的 Logs,建议修改到其他地方。
path.logs:/path/to/logs
#设置第三方插件的存放路径,默认是 ES 根目录下的 Plugins 文件夹。
path.plugins:/path/to/plugins
#设置为 True 时可锁住内存。因为当 JVM 开始 Swap 时,ES 的效率会降低,所以要保证它不 Swap。
bootstrap.mlockall:true
#设置本节点绑定的 IP 地址,IP 地址类型是 IPv4 或 IPv6,默认为 0.0.0.0。
network.bind_host:192.168.0.1
#设置其他节点和该节点交互的 IP 地址,如果不设置,则会进行自我判断。
network.publish_host:192.168.0.1
#用于同时设置 bind_host 和 publish_host 这两个参数。
network.host:192.168.0.1
#设置对外服务的 HTTP 端口,默认为 9200。ES 的节点需要配置两个端口号,一个对外提供服务的端口号,一个是集群内部使用的端口号。 http.port 设置的是对外提供服务的端口号。注意,如果在一个服务器上配置多个节点,则切记对端口号进行区分。
http.port:9200
#设置集群内部的节点间交互的 TCP 端口,默认是 9300。注意,如果在一个服务器配置多个节点,则切记对端口号进行区分。
transport.tcp.port:9300
#设置在节点间传输数据时是否压缩,默认为 False,不压缩。
transport.tcp.compress:true
#设置在选举 Master 节点时需要参与的最少的候选主节点数,默认为 1。如果使用默认值,则当网络不稳定时有可能会出现脑裂。 合理的数值为(master_eligible_nodes/2)+1,其中 master_eligible_nodes 表示集群中的候选主节点数。
discovery.zen.minimum_master_nodes:1
#设置在集群中自动发现其他节点时 Ping 连接的超时时间,默认为 3 秒。 在较差的网络环境下需要设置得大一点,防止因误判该节点的存活状态而导致分片的转移。
discovery.zen.ping.timeout:3s
禁止内存交换
为了避免不必要的es jvm堆中数据被交换到磁盘,造成垃圾收集性能较差,可能造成分钟级别的垃圾收集,es内存使用通过jvm堆的大小控制,不需要交换内存。以下有三种解决方式。
- 修改/etc/fstab文件,注释掉包含“swap”关键字的行。
- 修改/etc/sysctl.conf文件中的配置 vm.swappiness = 1。此配置降低Linux内核的交换偏好性,此配置不会禁用内存交换,在某些紧急条件下仍然可能触发内存交换
- 修改es的elasticsearch.yml文件,配置bootstrap.memory_lock: true。此配置会锁定es进程的内存地址空间,避免被交换出内存,需要注意的是当尝试分配更多的内存空间(超过锁定的内存地址空间)es进程会退出;通常启动es的用户不被允许锁定内存空间,需要采用root用户修改/etc/security/limits.conf文件,配置memlock为unlimited。
防止ES脑裂问题
正常的es集群只有一个节点扮演master的角色,而脑裂问题的出现就是因为从节点在选择主节点上出现分歧导致一个集群出现多个主节点从而使集群分裂,导致原本一个集群分裂成多个独立的集群,使得集群处于异常状态。
产生脑裂的原因可能有如下几点
- 网络分区,导致不同机房的节点自成一派形成新的集群
- 主节点es虚拟机堆出现过长时间的垃圾收集,导致从节点认为主节点故障,产生新的选举
- 节点负载过大,导致响应阻塞,从节点认为主节点故障,产生新的选举
预防脑裂方案
1、角色分离
在es集群中配置2到3个主节点并且让它们只负责管理不负责存储,从节点只负责存储。另外从节点禁用自动发现机制并为其指定主节点,在elasticsearch.yml文件中。
- 主节点配置
#表示节点是否具有成为主节点的资格,默认为true
node.master: true
#表示节点是否存储数据
node.data: false
- 从节点配置
#表示节点是否具有成为主节点的资格,默认为true
node.master: false
#表示节点是否存储数据
node.data: true
#把组播的自动发现给关闭了,为了防止其它机器上的节点自动连入
discovery.zen.ping.multicast.enabled: false
#设置了自动发现的节点,设置所有的有资格成为主节点
discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
2、最小集群节点数配置
#当具备成为主节点的从节点的个数满足这个数字且都认为主节点挂了则会进行选举产生新的主节点
#es集群有三个从节点有资格成为主节点,这时这三个节点都认为主节点挂了则会进行选举,此时如果这个参数的值是4则不会进行选举
#我们可以适当的把这个值改大,减少出现脑裂的概率,官方给出的建议是(n/2)+1,n为有资格成为主节点的节点数node.master=true
discovery.zen.minimum_master_nodes: 2
#此参数指定从节点访问主节点后如果3秒之内没有回复则默认主节点挂了,我们可以适当的把它改大,这样可以减少出现脑裂的概率
discovery.zen.ping_timeout:3
5、常见错误
完成上述步骤后,在本地虚拟机中启动es单实例,直接运行,开发模式下通常会打印如下警告日志,生产模式下会提升到错误级别,es无法正常启动。
- 用户被限制打开的文件描述符数量过低
max file descriptors [4096] for elasticsearch process is too low, increase to at least [65535]
解决方法:编辑 /etc/security/limits.conf,追加以下内容,注意此文件修改后需要重新登录用户,才会生效
elasticsearch hard nofile 65536
elasticsearch soft nofile 65536
执行命令查看用户限制
ulimit -a
- 用户别限制打开的线程数过低
max number of threads [3796] for user [elasticsearch] is too low, increase to at least [4096]
解决方法:需要配置es启动用户允许打开的文件句柄数量,编辑 /etc/security/limits.conf,追加以下内容,注意此文件修改后需要重新登录用户,才会生效
elasticsearch soft nproc 4096
elasticsearch hard nproc 4096
执行命令查看用户限制
ulimit -a
- 虚拟内存区域被限制过低
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决方案: es默认使用mmapfs(内存映射文件系统)去存储它的索引在内存中,默认操作系统限制内存映射数量比较小,可能造成内存溢出的异常,/etc/sysctl.conf文件最后添加一行
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
#使修改立即生效
sysctl -p
查看修改结果:
sysctl -a | grep vm.max_map_count
- 提示默认发现配置不适合生产环境使用
the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
解决方法:修改elasticsearch.yml配置
#打开节点名称的配置
node.name: node-1
#这里的node-1为node-name配置的值
cluster.initial_master_nodes: ["node-1"]
#外部ip访问elasticsearch
network.host: 0.0.0.0
6、开放端口
- 查看开放的端口号
firewall-cmd --list-all
- 设置开放端口号
firewall-cmd --add-port=9200/tcp --permanent
- 重启防火墙
firewall-cmd --reload
7、验证是否部署成功
- 访问部署服务器es实例,默认端口为9200
二、ElasticSearch简单集群搭建
1、本地环境准备
系统版本 | 机器IP | 部署应用 | 应用版本 | 节点名称 |
---|---|---|---|---|
CentOS 7.6 | 192.168.15.151 | ElasticSearch | 7.8.0 | node-1 |
CentOS 7.6 | 192.168.15.155 | ElasticSearch | 7.8.0 | node-2 |
CentOS 7.6 | 192.168.15.156 | ElasticSearch | 7.8.0 | node-3 |
说明:在本地PC机上利用VMware workstation虚拟出如上表所示的几台机器,搭建一个主节点及两个从节点的简单集群。
2、修改各个节点的配置文件
#进入es配置文件所在目录
cd /usr/local/elasticsearch-7.8.0/config
#编辑配置文件
vim elasticsearch.yml
- node-1节点配置文件(elasticsearch.yml)修改
#集群名称
cluster.name: test-app
#节点名称,集群中保持唯一
node.name: node-1
#绑定远程地址,为了安全通常是指定具体的地址,这里仅仅是测试,放开允许所有远程来源访问
network.host: 0.0.0.0
#开放http接口,默认就是9200
http.port: 9200
#集群节点之间(集群协商、指令传输等)通信的端口
transport.tcp.port: 9300
#允许前端跨域访问
http.cors.enabled: true
#设置允许的跨域的来源,*表示允许所有跨域来源
http.cors.allow-origin: "*"
#设置发现集群节点主机列表
discovery.seed_hosts: ["192.168.15.151:9300","192.168.15.155:9300","192.168.15.156:9300"]
#初始化集群的master节点的候选列表,列表中的节点都可能竞选成为master节点
cluster.initial_master_nodes: ["192.168.15.151:9300","192.168.15.155:9300","192.168.15.156:9300"]
- node-2节点配置文件(elasticsearch.yml)修改
#集群名称
cluster.name: test-app
#节点名称,集群中保持唯一
node.name: node-2
#绑定远程地址,为了安全通常是指定具体的地址,这里仅仅是测试,放开允许所有远程来源访问
network.host: 0.0.0.0
#开放http接口,默认就是9200
http.port: 9200
#集群节点之间(集群协商、指令传输等)通信的端口
transport.tcp.port: 9300
#允许前端跨域访问
http.cors.enabled: true
#设置允许的跨域的来源,*表示允许所有跨域来源
http.cors.allow-origin: "*"
#设置发现集群节点主机列表
discovery.seed_hosts: ["192.168.15.151:9300","192.168.15.155:9300","192.168.15.156:9300"]
#初始化集群的master节点的候选列表,列表中的节点都可能竞选成为master节点
cluster.initial_master_nodes: ["192.168.15.151:9300","192.168.15.155:9300","192.168.15.156:9300"]
- node-3节点配置文件(elasticsearch.yml)修改
#集群名称
cluster.name: test-app
#节点名称,集群中保持唯一
node.name: node-3
#绑定远程地址,为了安全通常是指定具体的地址,这里仅仅是测试,放开允许所有远程来源访问
network.host: 0.0.0.0
#开放http接口,默认就是9200
http.port: 9200
#集群节点之间(集群协商、指令传输等)通信的端口
transport.tcp.port: 9300
#允许前端跨域访问
http.cors.enabled: true
#设置允许的跨域的来源,*表示允许所有跨域来源
http.cors.allow-origin: "*"
#设置发现集群节点主机列表
discovery.seed_hosts: ["192.168.15.151:9300","192.168.15.155:9300","192.168.15.156:9300"]
#初始化集群的master节点的候选列表,列表中的节点都可能竞选成为master节点
cluster.initial_master_nodes: ["node-1","node-2","node-3"]
3、启动各个节点服务
注意,在启动各个服务之前,需要保证防火墙开放了以上配置的9200、9300端口,否则集群之间将无法通信,集群构建失败。
#执行es启动命令,分别启动三台服务器下的es服务
/usr/local/elasticsearch-7.8.0/bin/elasticsearch -d
- 节点(node-1)的日志
- 节点(node-2)的日志
- 节点(node-3)的日志
- 访问节点http端点,查看集群状态
说明:从以上日志可以看出,集群搭建成功,node-1、node-2、node-3组成了集群的三个节点,node-1、node-3为slaver节点,node-2为master节点。
三、ElasticSearch原理简答介绍
es索引数据,底层是基于Lucene实现的,而segment段Lucene维护索引的文件,Lucene是尽可能多地将segment段文件数据缓存在操作系统缓存中,这是es高效搜索的重要原因之一。
es写数据
es写数据过程
- 客户端选择一个节点发送请求,此节点称为协调节点
- 协调节点对待写入文档进行路由,将请求转发到对应的节点
- 对应节点的主分片自身处理完毕后,需要将数据同步到副本分片
- 协调节点确认主分片和副本分片都搞定之后,响应结果给客户端
es写数据原理
- 待写入的文档并没有立马写入磁盘。首先写入的是es虚拟机堆内存中(memory cache)
- memory cache中的数据默认没隔一秒中会被refresh刷新到操作系统缓存中(os cache),即在操作系统级别的内存中产生一个segment file;同时会在os cache中记录translog日志,此时被索引的数据可以被搜索到。
- 默认位于os cache中记录的translog日志数据每隔5秒会被写入磁盘持久化。最多丢失5秒的数据
- 默认每隔30分钟或者translog日志文件大小大到一定阈值,会触发commit(flush)操作。会将os cache中的segment file数据强制写入磁盘,并清空和重开translog
说明:默认情况下es被索引的数据需要等待1秒后才能被搜索到,最多丢失5秒的数据。
es段文件合并
es中每个shard每隔1秒都会refresh一次,每次refresh都会生成一个新的segment,按照这个速度过不了多久segment的数量就会爆炸,所以存在太多的segment是一个大问题,因为每一个segment都会占用文件句柄,内存资源,cpu资源,更加重要的是每一个搜索请求都必须访问每一个segment,这就意味着存在的segment越多,搜索请求就会变的更慢。
那么elaticsearch是如何解决这个问题呢? 实际上elasticsearch有一个后台进程专门负责segment的合并,它会把小segments合并成更大的segments,然后反复这样。在合并segments的时候标记删除的document不会被合并到新的更大的segment里面,所有的过程都不需要我们干涉,es会自动在索引和搜索的过程中完成,合并的segment可以是磁盘上已经commit过的索引,也可以在内存中还未commit的segment。
- 在索引时refresh进程每秒会创建一个新的segment并且打开它使得搜索可见
- merge进程会在后台选择一些小体积的segments,然后将其合并成一个更大的segment,这个过程不会打断当前的索引和搜索功能。
- 一旦merge完成,旧的segments就会被删除,新的segment会被flush到磁盘
- 然后会生成新的commit point文件,包含新的segment名称,并排除掉旧的segment和那些被合并过的小的segment
- 接着新的segment会被打开用于搜索,最后旧的segment会被删除掉
说明:如果不加控制,合并一个大的segment会消耗比较多的io和cpu资源,同时也会搜索性能造成影响,所以默认情况下es已经对合并线程做了资源限额以便于它不会搜索性能造成太大影响。
es读数据过程
通过文档id查询,会根据文档id进行hash,然后直接路由到指定的分片查询。
- 客户端发送请求到任意节点,此节点称之为协调节点
- 协调节点对文档id进行哈希路由,将请求转发到对应的节点分片,此时会使用round-robin随机轮询算法,在主分片以及其所有副本分片中随机选择一个,让读请求负载均衡
- 接受请求的节点返回文档数据给协调节点
- 协调节点返回数据给客户端
es搜索数据过程
例如通过某个关键词来搜索结果
- 客户端发送请求到任意节点,此节点称之为协调节点
- 协调节点将搜索请求分发到所有的分片(主分片或者副本分片都可以)
- 每个 shard 将自己的搜索结果(其实就是一些文档id和评分数据)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果
- 接着由协调节点根据文档id去各个节点上拉取实际的文档数据,最终返回给客户端。
四、ElasticSearch性能优化
设计方面的优化
- 将热数据和冷数据分离,分别建立不同的索引,通过分片控制冷热数据在不同的机器上,避免热数据被冷数据覆盖,毕竟热数据相比于冷数据是很小部分的(冷热分离)
- 索引尽量扁平化设计,尽可能只保留搜索所需要的字段,这样能内存中就能保留更多的数据,尽量避免关联嵌套查询
- 仔细推敲字段类型及适用的分词器
- 角色分离。防止脑裂发生
- 禁止操作系统内存交换。或者锁定es内存防止被交换到硬盘
写优化(写密集的业务)
- 批量提交,节省网络IO耗时
- 优化存储设备,使用固态硬盘
- 对于搜索实时性不高的场景,可以减少refresh次数,虽然需要消耗更多的内存,但是可以减少segment段合并次数
- 加大flush设置,当translog的数量达到512M或者30分钟时,会触发一次flush,当然需要为操作系统的文件缓存留下足够的空间。
- 减少副本的数量,当写入索引时数据都需要同步到副本节点,节点越多,写入越慢
读优化(写密集的业务)
- 设置合适地堆大小,使得es能缓存更多的数据,提高缓存命中率,但是不要超过物理内存的50%,需要留给Lucene缓存段文件的空间,堆内存大小也不要超过32G。
- 提前将热点数据预热到内存中,尽可能地减热点数据缓存在内存中(数据预热)。
- 避免大结果集合深度分页查询,假如要查询from开始的size条数据,则需要在每个分片中查询打分排名在前面的from + size条数据,协同节点将收集到 n * (from + size)条数据并聚合,再进行一次排序,当任何一个变量变得很大,需要参加排序的数量也会很大,会消耗更多的CPU资源,效率很低。如果实在需要分页,那么应该采用ES 提供了 Scroll 和 Scroll-Scan 这两种查询模式
- 创建合适的字段路由,可以通过字段为文档增加路由属性的tag,这样查询时可以根据routing信息,直接定位到目标分片,避免分发到每个分片,再经过协调节点二次排序操作
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。