es集群的调优2 JVM 启动 ES 优雅的关闭 ES

1、es的内存千万不能低于8G

2、机器尽量内存不能大于64G,生产上面尽量elk设置内存大小为64G

4 cpu核心数目要求没有那样高,8核或者16核都可以

这里要介绍下磁盘阵列

Raid0

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 之前有部电影是300G,之前是将这300G直接写入到一个600G的磁盘ssd上面

现在有3个200G的ssd磁盘构成了一个磁盘阵列,radio0,就是将300G的电影分成三部分同时写入到三个磁盘上面,这样速度是相对的快,但是如果一旦一个磁盘坏了整个数据就丢失了

磁盘
对于es的生产环境来说,磁盘是非常重要的,尤其是对那些大量写入的es集群,比如互联网公司将每天的实时日志数据以高并发的速度写入es集群。在服务器上,磁盘是最慢的那个资源,所以对于大量写入的es集群来说,会很容易因为磁盘的读写性能造成整个集群的性能瓶颈。

如果我们能够使用SSD固态硬盘,而不是机械硬盘,那么当然是最好的,SSD的性能比机械硬盘可以高很多倍,可以让es的读写性能都高很多倍。

所以,如果公司出的起钱大量使用固态硬盘,那么当然是最好的。

如果我们在用SSD硬盘的化,那么需要检查我们的I/O scheduler,需要正确的配置IO scheduler。当我们将数据写入磁盘时,IO scheduler会决定什么时候数据才会真正的写入磁盘,而不是停留在os cache内存缓冲中。大多数机器上,默认的IO scheduler是cfq,也就是completely fair queuing。

这个scheduler会给每个进程都分配一些时间分片,time slice,然后会优化每个进程的数据如何写入磁盘中,优化的思路主要 是根据磁盘的物理布局来决定如何将数据写入磁盘,进而提升写入磁盘的性能。这是针对机械硬盘做出的优化,因为机械硬盘是一种旋转存储介质,是通过机械旋转磁盘+磁头进行磁盘读写的机制。

但是scheduler的这种默认的执行机制,对于SSD来说是不太高效的,因为SSD跟机械硬盘是不一样的,SSD不涉及到机械磁盘旋转和磁头读取这种传统的读写机制。对于SSD来说,应该用deadline/noop scheduler。deadline scheduler会基于写操作被pending了多长时间来进行写磁盘优化,而noop scheduler就是一个简单的FIFO队列先进先出的机制。

调整io scheduler可以带来很大的性能提升,甚至可以达到数百倍。

如果我们没有办法使用SSD,只能使用机械硬盘,那么至少得尽量正确读写速度最快的磁盘,比如高性能的服务器磁盘。

此外,使用RAID 0也是一种提升磁盘读写速度的高效的方式,无论是对于机械硬盘,还是SSD,都一样。

RAID 0也被称之为条带式存储机制,striping,在RAID各种级别中性能是最高的。RAID 0的基本原理,是把连续的数据分散存储到多个磁盘上进行读写,也就是对数据进行条带式存储。这样系统的磁盘读写请求就可以被分散到多个磁盘上并行执行。但是没有必要使用镜像或者RAID的其他模式,因为我们不需要通过RAID来实现数据高可用存储,es的replica副本机制本身已经实现了数据高可用存储。

最后,我们要避免跟网络相关的存储模式,network-attached storage,NAS,比如基于网络的分布式存储模式。

虽然很多供应商都说他们的NAS解决方案性能非常高,而且比本地存储的可靠性更高。但是实际上用起来会有很多性能和可靠性上的风险,一般因为网络传输会造成较高的延时,同时还有单点故障的风险。

 结论:使用ssd磁盘,磁盘阵列使用raid0,虽然raido没有数据的备份,但是es本身分区的备份功能已经能够保证数据的有效性了

网络
对于es这种分布式系统来说,快速而且可靠的网络是非常的重要的。

因为高速网络通信可以让es的节点间通信达到低延时的效果,高带宽可以让shard的移动和恢复,以及分配等操作更加的快速。

现代的数据中心的网络对于大多数的集群来说,性能都足够高了。比如千兆网卡,这都是可以的。

但是要避免一个集群横跨多个数据中心,比如异地多机房部署一个集群,因为那样的话跨机房,跨地域的传输会导致网络通信和数据传输性能较差。es集群是一种p2p模式的分布式系统架构,不是master-slave主从分布式系统。在es集群中,所有的node都是相等的,任意两个node间的互相通信都是很频繁和正常的。因此如果部署在异地多机房,那么可能会导致node间频繁跨地域进行通信,通信延时会非常高,甚至造成集群运行频繁不正常。

就跟NAS存储模式一样,很多供应商都说跨地域的多数据中心是非常可靠的,而且低延时的。一般来说,可能的确是这样,但是一旦发生了网络故障,那么集群就完了。通常来说,跨地域多机房部署一个es集群带来的效益,远远低于维护这样的集群所带来的额外成本。

结论:网络带宽至少在百M网络以上,一定不能在异地多机房部署一个es集群,因为es集群之后的通信非常频繁

自建集群 vs 云部署
现在一般很容易就可以拿到高性能的机器来部署集群:很多高性能的机器可以有上百G的内存资源,还有几十个cpu core。但是同时我们也可以再云供应商上,比如阿里云,租用大量的小资源的虚拟机。那么对于自己购买昂贵高性能服务器自建集群,以及租用云机器来部署,该选择哪种方案呢?

你是自己购买5台,比如说,8核64G的物理机,搭建es集群

或者是,比如说,上阿里云,或者其他的云服务,购买了2核4G,16台,虚拟机,搭建es集群

你上阿里云,也可以买到大资源量的虚拟机,4/8/16核64G

一般来说,对于es集群而言,是建议拥有少数机器,但是每个机器的资源都非常多,尽量避免拥有大量的少资源的虚拟机。因为对于运维和管理来说,管理5个物理机组成的es集群,远远比管理100个虚拟机组成的es集群要简单的多。

同时即使是自建集群,也要尽量避免那种超大资源量的超级服务器,因为那样可能造成资源无法完全利用,然后在一个物理机上部署多个es节点,这会导致我们的集群管理更加的复杂。

结论:尽量申请大的资源充足的物理机器,不要选择多个资源小的虚拟机,因为管理5个 8cpu 64G内存的物理机,比管理 20个8内存的虚拟机方便的多

第三个jvm规划:

对于最新的es版本,一般多建议用最新的jvm版本,除非es明确说明要用哪个jdk版本。es和lucene都是一种满足特殊需求的软件,lucene的单元测试和集成测试中,经常会发现jvm自身的一些bug。这些bug涵盖的范围很广,因此尽量用最新的jvm版本,bug会少一些。

es 5.x版本以上,建议用jdk 8,而不是jdk 7,同时jdk 6已经不再被支持了。

如果我们用java编写es应用程序,而且在使用transport client或者node client,要确保运行我们的应用程序的jvm版本跟es服务器运行的jvm版本是一样的。

在es中,有些java的本地序列化机制都被使用了,比如ip地址,异常信息,等等。而jvm可能在不同的minor版本之间修改序列化格式,所以如果client和server的jvm版本不一致,可能有序列化的问题。

同时官方推荐,绝对不要随便调整jvm的设置。虽然jvm有几百个配置选项,而且我们可以手动调优jvm的几乎方方面面。同时遇到一个性能场景的时候,每个人都会第一时间想到去调优jvm,但是es官方还是推荐我们不要随便调节jvm参数。因为es是一个非常复杂的分布式软件系统,而且es的默认jvm配置都是基于真实业务场景中长期的实践得到的。随便调节jvm配置反而有可能导致集群性能变得更加差,以及出现一些未知的问题。反而是很多情况下,将自定义的jvm配置全部删除,性能是保持的最好的。

1、jdk尽量使用1.8版本

2、es服务器和es java客户端使用的jdk的版本尽量一致,否则会存在jvm序列化的问题

3、es安装包里面的jvm参数尽量不要做修改

容量规划:

容量规划
在规划你的es集群的时候,一般要规划你需要多少台服务器,每台服务器要有多少资源,能够支撑你预计的多大的数据量。但是这个东西其实不是一概而论的,要视具体的读写场景,包括你执行多么复杂的操作,读写QPS来决定的。不过一般而言,实际经验,对于很多的中小型公司,都是建议es集群承载的数据量在10亿规模以内。用最合理的技术做最合理的事情。

这里可以给出几个在国内es非常适合的几个场景,es是做搜索的,当然可以做某个系统的搜索引擎。比如网站或者app的搜索引擎,或者是某些软件系统的搜索引擎,此外es还可以用来做数据分析。那么针对这几个不同的场景,都可以给出具体建议。比如做网站或者app的搜索引擎,一般数据量会相对来说大一些,但是通常而言,一个网站或者app的内容都是有限的,不会无限膨胀,通常数据量从百万级到亿级不等,因此用于搜索的数据都放在es中是合理的。

然后一些软件系统或者特殊项目的搜索引擎,根据项目情况不同,数据量也是从百万量级到几十亿,甚至几百亿,或者每日增量几亿,都有可能,那么此时就要根据具体的业务场景来决定了。如果数据量特别大,日增量都几亿规模,那么其实建议不要将每天全量的数据都写入es中,es也不适合这种无限规模膨胀的场景。es是很耗费内存的,无限膨胀的数据量,会导致我们无法提供足够的资源来支撑这么大的数据量。可以考虑是不是就将部分热数据,比如最近几天的数据,放到es中做高频高性能搜索,然后将大量的很少访问的冷数据放大数据系统做离线批量处理,比如hadoop系统里面。

比如说,你预计一下,你的数据量有多大,需要多少台机器,每台机器要多少资源,来支撑,可以达到多大的性能

数据量 -> 性能,10亿 -> 要求1s内返回

es达到ms级的化,你必须要有足够的os cache去缓存几乎大部分的索引数据

10亿,每条数据是多大,比如多少个字节,1k -> 假设数据大小为100G

如果有5台,64G,8核,那么总内存就是300G左右 -> 100G的总数据量,300G的内存,一般要分给es jvm heap,150G (每台32G)-> 那么100G的数据总量,100G落地到磁盘文件加入很多es自己的信息,假设100G -> 200G

200G落地磁盘的数据,物理内存剩余的只有150G,可能还有一些操作系统,还有其他的损耗100G

200G落地磁盘的数据,100G物理内存可以用来做os cache,50%的概率是基于os cache做磁盘索引文件的读写,几秒,很正常啦。。。

根据我们的实践经验而言,一般来说,除非是你的机器的内存资源,完全可以容纳所有的落地的磁盘文件的os cache,ms,否则的话,如果不是的话,会大量走磁盘,查询秒级别很正常

同时如果数据量在10亿以内的规模,那么一般而言,如果提供5台以上的机器,每台机器的配置到8核64G的配置,一般而言都能hold住。当然,这个也看具体的使用场景,如果你读写特别频繁,或者查询特别复杂,那么可能还需要更多的机器资源。如果你要承载更大的数据量,那么就相应的提供更多的机器和资源。

要提升你的es的性能,最重要的,还是说规划合理的数据量,物理内存资源大小,os cache

es优化的参数:

1. master 的设置还是选择三个节点吧,每个节点设置的时候把 ingest 设置为 false;
2. 写入的时候,索引的副本数可以设置为 0,刷新时间设置为 60s 或者更大时间;
3. 调整 translog 的设置:
index.translog.durability: async
index.translog.sync_interval: 600s
index.translog.flush_threshold:50000
index.translog.flush_threshold_size: 1024mb
 
4. 对不参与搜索的字段关闭分析器,对不参与聚合、排序的字段设置关闭 doc_values;
5. 调整 logstash 的 bulk 大小,以及消费 kafka 的线程数;
6. 设置索引相关的线程池:
threadpool.index.size: 100 
threadpool.index.queue_size: 500

7. 增大索引实时时间设置:index.engine.robin.refresh_interval: 10s
8. 增大内存缓冲区: indices.memory.index_buffer_size: 20%

 进阶-第92_es生产集群部署之必须根据自己的集群设置的一些重要参数

1、es的默认参数
es的默认参数是非常好的,适合绝大多数的情况,尤其是一些性能相关的配置。因此刚开始部署一个生产环境下的es集群时,几乎所有的配置参数都可以用默认的设置。

比如mysql或者oracle这种关系型数据库,也许是需要非常重的调优,但是es是真的不用。如果我们现在面临着一些es的性能问题,通常建议的解决方案是更好的进行数据结构的布局,或者增加更多的节点和机器资源。在es的性能调优中,真的很少有那种magic knobs,就是某个参数一调节,直接性能提升上百倍。即使有这种参数,es官方也早就将其设置为默认的最佳值了。

但是在生产环境中,还是极少数跟公司和业务相关的配置是需要我们修改的。这些设置都是具体的公司和业务相关联的,是没法预先给予最好的默认配置的。

2、集群名称和节点名称
默认情况下,es会启动一个名称为elasticsearch的集群。通常建议一定要将自己的集群名称重新进行命名,主要是避免公司网络环境中,也许某个开发人员的开发机会无意中加入你的集群。比如将你的集群名称命名为elasticsearch_production。在elasticsearch.yml中,可以设置集群名称:

cluster.name: cluster-elasticsearch-prod
此外,每个node启动的时候,es也会分配一个随机的名称。这个也不适合在生产环境中,因为这会导致我们没法记住每台机器。而且每次重启节点都会随机分配,就导致node名称每次重启都会变化。因此通常我们在生产环境中是需要给每个node都分配一个名称的。在elasticsearch.yml中配置即可:

node.name: node-elasticsearch-01
3、文件路径
(1)数据目录、日志目录以及插件目录

默认情况下,es会将plugin,log,还有data ,config,file都放在es的安装目录中。这有一个问题,就是在进行es升级的时候,可能会导致这些目录被覆盖掉。导致我们丢失之前安装好的plugin,已有的log,还有已有的数据,以及配置好的配置文件。

所以一般建议在生产环境中,必须将这些重要的文件路径,都重新设置一下,放在es安装目录之外。path.data用于设置数据文件的目录,path.logs用于设置日志文件的目录,path.plugins用于设置插件存放的目录。path.data可以指定多个目录,用逗号分隔即可。如果多个目录在不同的磁盘上,那么这就是一个最简单的RAID 0的方式,将数据在本地进行条带化存储了,可以提升整体的磁盘读写性能。es会自动将数据在多个磁盘的多个目录中条带化存储数据。

一般建议的目录地址是:

mkdir -p /var/log/elasticsearch
mkdir -p /var/data/elasticsearch
mkdir -p /var/plugin/elasticsearch
mkdir -p /etc/elasticsearch
cp /usr/local/elasticsearch/config/elasticsearch.yml /etc/elasticsearch/
cp /usr/local/elasticsearch/config/jvm.options /etc/elasticsearch/
cp /usr/local/elasticsearch/config/log4j2.properties /etc/elasticsearch/

[root@localhost /]# cd /etc/elasticsearch/
[root@localhost elasticsearch]# vi elasticsearch.yml
添加如下参数:

path.logs: /var/log/elasticsearch
path.data: /var/data/elasticsearch
path.plugins: /var/plugin/elasticsearch

config:/etc/elasticsearch
在RAID 0的存储级别下,每个磁盘上回存储一部分数据,但是如果一个磁盘故障了,那么可能导致这台机器上的部分数据就丢失了。如果我们的es是有replica的,那么在其他机器上还是会有一份副本的。如果data file指定了多个目录,为了尽量减少数据丢失的风险,es会将某个shard的数据都分配到一个磁盘上去。这就意味着每个shard都仅仅会放在一个磁盘上。es不会将一个shard的数据条带化存储到多个磁盘上去,因为如果一个磁盘丢失了,就会导致整个shard数据丢失。

但是这又引入了性能的问题,如果我们给一个机器添加更多的磁盘来提升单个索引的读写性能,是没有效果的。因为这个索引在这个机器上的shard仅仅存在于一个磁盘上。因此data file指定多个目录,仅仅对于你的一台机器上存储了多个index的多个shard时,才会有效果的。因为不同index的shard可能就被存储到不同的磁盘上去了,对多个index的shard读写可以走不同磁盘,提升了性能。

虽然multiple data path是一个很有用的功能,但是es毕竟不是一个专门的RAID软件。如果我们要对RAID存储策略进行更多的配置,提高存储的健壮性以及灵活性,还是要用专门的RAID软件来进行机器的磁盘数据存储,而不是用multiple data path策略。

综上所述,multiple data path功能在实际的生产环境中,其实是较少使用的。

(2)配置文件目录

es有两个配置文件,elasticsearch.yml,用于配置es,还有一个log4j.properties用来配置es日志打印。这些文件都被放在config目录下,默认就是ES_HOME/config。可以通过下面的命令来重新设置:./bin/elasticsearch -Epath.conf=/path/to/my/config/。

配置文件的格式是yaml格式的,比如下面这种格式:

path:
data: /var/lib/elasticsearch
logs: /var/log/elasticsearch

path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
4、日志配置
es使用log4j2来记录日志,log4j2可以通过log4j2.properties文件来进行配置。比如下面的这份配置文件:

appender.rolling.type = RollingFile
appender.rolling.name = rolling
appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %.10000m%n
appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}.log
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.type = RollingFile,就配置了appender类型是RollingFile

appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log,
就配置了日志路径是/var/log/elasticsearch/production.log

appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}.log
就配置了将日志每天写一份到/var/log/elasticsearch/production-2019-01-01.log文件中

appender.rolling.policies.time.type = TimeBasedTriggeringPolic
这里配置了用基于时间的roll策略

appender.rolling.policies.time.interval = 1
这个设置了每天一份日志文件

appender.rolling.policies.time.modulate = true
这个设置了根据自然天来划分文件,而不是24小时

还可以配置将日志文件保留一段时间内,同时删除之前的日志文件

appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.action.type = Delete
appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}
appender.rolling.strategy.action.condition.type = IfLastModified
appender.rolling.strategy.action.condition.age = 7D
appender.rolling.strategy.action.PathConditions.type = IfFileName
appender.rolling.strategy.action.PathConditions.glob = ${sys:es.logs.cluster_name}-*
第一行是配置了默认的DefaultRolloverStrategy
第二行是配置了Delete action,在rollover之后,就会删除文件
第三行是配置了es log的基础路径
第四行是配置了rollover发生的条件,是基于IfLastModified
第五行是配置了保留的天数,这里是7天
第六行是配置了删除匹配7天前的文件
第七行是配置了一个删除文件的格式,这样就只是删除过期日志文件,但是不要删除慢查询日志

进阶-第93_es生产集群部署之针对生产集群的脑裂问题专门定制的重要参数

两点一刻 2019-03-23 17:44:12 59 收藏
分类专栏: elasticsearch 文章标签: elasticsearch
版权

最少master候选节点以及脑裂问题

discovery.zen.minimum_master_nodes参数对于集群的可靠性来说,是非常重要的。这个设置可以预防脑裂问题,也就是一个集群中存在两个master。

如果因为网络的故障,导致一个集群被划分成了两片,每片都有多个node,以及一个master,那么集群中就出现了两个master了。但是因为master是集群中非常重要的一个角色,主宰了集群状态的维护,以及shard的分配,因此如果有两个master的化,可能会导致破坏数据。

那么那个参数的作用,就是告诉es直到有足够的master候选节点时,才可以选举出一个master,否则就不要选举出一个master。这个参数必须被设置为集群中master候选节点的quorum数量,也就是大多数。至于quorum的算法,就是:master候选节点数量 / 2 + 1。

比如我们有10个节点,都能维护数据,也可以是master候选节点,那么quorum就是10 / 2 + 1 = 6。

如果我们有三个master候选节点,还有100个数据节点,那么quorum就是3 / 2 + 1 = 2

如果我们有2个节点,都可以是master候选节点,那么quorum是2 / 2 + 1 = 2。此时就有问题了,因为如果一个node挂掉了,那么剩下一个master候选节点,是无法满足quorum数量的,也就无法选举出新的master,集群就彻底挂掉了。此时就只能将这个参数设置为1,但是这就无法阻止脑裂的发生了。

2个节点,discovery.zen.minimum_master_nodes分别设置成2和1会怎么样

综上所述,一个生产环境的es集群,至少要有3个节点,同时将这个参数设置为quorum,也就是2。discovery.zen.minimum_master_nodes设置为2,如何避免脑裂呢?

那么这个是参数是如何避免脑裂问题的产生的呢?比如我们有3个节点,quorum是2.现在网络故障,1个节点在一个网络区域,另外2个节点在另外一个网络区域,不同的网络区域内无法通信。这个时候有两种情况情况:

(1)如果master是单独的那个节点,另外2个节点是master候选节点,那么此时那个单独的master节点因为没有指定数量的候选master node在自己当前所在的集群内,因此就会取消当前master的角色,尝试重新选举,但是无法选举成功。然后另外一个网络区域内的node因为无法连接到master,就会发起重新选举,因为有两个master候选节点,满足了quorum,因此可以成功选举出一个master。此时集群中就会还是只有一个master。

(2)如果master和另外一个node在一个网络区域内,然后一个node单独在一个网络区域内。那么此时那个单独的node因为连接不上master,会尝试发起选举,但是因为master候选节点数量不到quorum,因此无法选举出master。而另外一个网络区域内,原先的那个master还会继续工作。这也可以保证集群内只有一个master节点。

综上所述,通过在elasticsearch.yml中配置discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生。

但是因为es集群是可以动态增加和下线节点的,所以可能随时会改变quorum。所以这个参数也是可以通过api随时修改的,特别是在节点上线和下线的时候,都需要作出对应的修改。而且一旦修改过后,这个配置就会持久化保存下来。

PUT /_cluster/settings

{
"persistent" : {
"discovery.zen.minimum_master_nodes" : 2

}

}

进阶-第94_es生产集群部署之针对集群重启时的shard恢复耗时过长问题定制的重要参数

shard recovery配置以及集群重启时的无意义shard重分配问题

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

在集群重启的时候,有一些配置会影响shard恢复的过程。首先,我们需要理解默认配置下,shard恢复过程会发生什么事情。如果我们有10个node,每个node都有一个shard,可能是primary shard或者replica shard,你有一个index,有5个primary shard,每个primary shard有一个replica shard。如果我们将整个集群关闭了进行一些维护性的操作,比如给机器安装新的磁盘之类的事情。当我们重启集群的时候,肯定节点是一个接一个的启动的,可能会出现5个节点先启动了,然后剩下5个节点还没启动。

也许是因为剩下的5个节点没来得及启动,或者是因为一些原因耽搁了,总之不管是什么原因,就是现在只有5个节点是在线的。这5个节点会通过gossip协议互相通信,选举出一个master,然后组成一个集群。他们会发现数据没有被均匀的分布,因为有5个节点没有启动,那么那5个节点上的shard就是不可用的,集群中就少了一半的shard。此时在线的5个node就会将部分replica shard提升为primary shard,同时为每个primary shard复制足够的replica shard。

最后,可能剩下的5个节点加入了集群。但是这些节点发现本来是他们持有的shard已经被重新复制并且放在之前的5个node速度回当了,此时他们就会删除自己本地的数据。然后集群又会开始进行shard的rebalance操作,将最早启动的5个node上的shard均匀分布到后来启动的5个node上去。

在这个过程中,这些shard重新复制,移动,删除,再次移动的过程,会大量的耗费网络和磁盘资源。对于数据量庞大的集群来说,可能导致每次集群重启时,都有TB级别的数据无端移动,可能导致集群启动会耗费很长时间。但是如果所有的节点都可以等待整个集群中的所有节点都完全上线之后,所有的数据都有了以后,再决定是否要复制和移动shard,情况就会好很多。

所以现在问题我们已经知道了,那么我们就可以配置一些设置来解决这个问题。首先我们需要设置一个参数,gateway.recover_after_nodes: 8。这个参数可以让es直到有足够的node都上线之后,再开始shard recovery的过程。所以这个参数是跟具体的集群相关的,要根据我们的集群中节点的数量来决定。此外,还应该设置一个集群中至少要有多少个node,等待那些node的时间:gateway.expected_nodes: 10,gateway.recover_after_time: 5m。经过上面的配置之后,es集群的行为会变成下面这样,等待至少8个节点在线,然后等待最多5分钟,或者10个节点都在线,开始shard recovery的过程。这样就可以避免少数node启动时,就立即开始shard recovery,消耗大量的网络和磁盘资源,甚至可以将shard recovery过程从数小时缩短为数分钟。

进阶-第95_es生产集群部署之绝对不能随意调节jvm和thread pool的原因

es中有很多的配置都让大家忍不住去调优,因为也许大家都太过于迷恋性能优化了,都认为优化一些配置可以大幅度提升性能,就感觉性能调优像个魔法一样,是个万能的东西。但是其实99.99%的情况下,对于es来说,大部分的参数都保留为默认的就可以了。因为这些参数经常被滥用和错误的调节,继而导致严重的稳定性问题以及性能的急剧下降。

1、jvm gc

jvm使用垃圾回收器来释放掉不用的内存,千万不要去调节默认的垃圾回收行为。es默认用的垃圾回收器是CMS。CMS回收器是并发式的回收器,能够跟应用程序工作线程并发工作,最大程度减少垃圾回收时的服务停顿时间。但是CMS还是会有两个停顿阶段,同时在回收特别大的heap时也会有一些问题。尽管有一些缺点,但是CMS对于要求低延时请求响应的软件来说,还是最佳的垃圾回收器,因此官方的推荐就是使用CMS垃圾回收器。

有一种最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停顿时间,而且能够这对大heap有更好的回收表现。它会将heap划分为多个region,然后自动预测哪个region会有最多可以回收的空间。通过回收那些region,就可以最小化停顿时长,而且可以针对大heap进行回收。

听起来还挺不错的,但是不幸的是,G1还是比较年轻的一种垃圾回收器,而且经常会发现一些新的bug,这些bug可能会导致jvm挂掉。lucene的测试套件就检查出来了G1的一些bug。因此es官方不推荐现在使用G1垃圾回收器,也许在不久的未来,等G1更加稳定的时候,可以使用G1。

现在到了额2020年,其实推荐可以使用G1垃圾回收器了

2、threadpool

每个人都很喜欢去调优线程池,而且大部分人都特别喜欢增加线程池的线程数量,无论是大量的写入,还是大量的搜索,或者是感觉服务器的cpu idle空闲率太高,都会增加更多的线程。在es中,默认的threadpool设置是非常合理的,对于所有的threadpool来说,除了搜索的线程池,都是线程数量设置的跟cpu core一样多的。如果我们有8个cpu core,那么就可以并行运行8个线程。那么对于大部分的线程池来说,分配8个线程就是最合理的数量。

不过搜索会有一个更加大的threadpool,一般被配置为:cpu core * 3 / 2 + 1。

也许我们会觉得有些线程可能会因为磁盘IO等操作block住,所以我们需要更多的线程。但是在es中这并不是一个问题,大多数的磁盘IO操作都是由lucene的线程管理的,而不是由es管理的,因此es的线程不需要关心这个问题。此外,threadpool还会通过在彼此之间传递任务来协作执行,我们不需要担心某一个网络线程会因为等待一次磁盘写操作,而导致自己被block住,无法处理网络请求。网络线程可以将那个磁盘写操作交给其他线程池去执行,然后自己接着回来处理网络请求。

其实我们的进程的计算能力是有限的,分配更多的线程只会强迫cpu在多个线程上下文之间频繁来回切换。一个cpu core在同一时间只能运行一条线程,所以如果cpu要切换到另外一个线程去执行,需要将当前的state保存起来,然后加载其他的线程进来执行。如果线程上下文切换发生在一个cpu core内,那么还好一些,但是如果在多个cpu core之间发生线程上下文切换,那么还需要走一个cpu core内部的通信。这种线程上下文切换会消耗掉很多的cpu资源,对于现在的cpu来说,每次线程上下文切换,都会导致30微秒的时间开销,所以宁愿将这些时间花费在任务的处理上。

很多人会将threadpool大小设置为一些很愚蠢的数值,在一个8核的机器上,可能运行了超过60,100,甚至1000个线程。这么多的线程会导致cpu资源利用率很低。所以下次如果我们要调节线程池的话,记住,千万别这么干。如果一定要调节线程数量,也得记住要根据你的cpu core数量来调节,比如设置为cpu core的两倍,如果设置的再多,那么就是一种浪费了。

进阶-第96_es生产集群部署之jvm和服务器内存分配的最佳实践以及原理分析

除了之前讲解的一些配置,根据你的集群环境特殊的配置,我们这一讲来讲解最重要的内存的分配,提出一些问题,生产环境部署es,不可避免要回答一个问题,比如我的机器上有64G的内存,或者32G的内存,那么一般来说我应该分配多少个G的内存给es的jvm heap

1、jvm heap分配

es默认会给jvm heap分配2个G的大小,对于几乎所有的生产环境来说,这个内存都太小了。如果用这个默认的heap size,那么生产环境的集群肯定表现不会太好。

有两个方式来调节es中的jvm heap size。最简单的就是设置环境变量,ES_HEAP_SIZE。当es进程启动的时候,会读取这个环境变量的值,然后设置为jvm的heap size。举例来说,可以这样来设置:export ES_HEAP_SIZE=10g。此外,还可以在启动es进程的时候,传递一个jvm的option,比如:ES_JAVA_OPTS="-Xms10g -Xmx10g" ./bin/elasticsearch,但是要注意-Xms和-Xmx最小和最大堆内存一定设置的一样,避免运行过程中的jvm heap resize,那会是一个非常耗时的过程。

在老版本的es中,比如es 2.x里面,一般推荐用ES_HEAP_SIZE环境变量的方式来设置jvm heap size。

在新版本的es中,比如es 5.x里面,一般推荐在jvm.options文件里面去设置jvm相关的参数。

2、将机器上少于一半的内存分配给es

一个常见的问题就是将es进程的jvm heap size设置的过于大了。比如我们有一台64G的机器,可能我们甚至想要给es jvm size设置64G内存。但是这是错误的。大家可能会觉得说,直接将机器上的可用的内存都分配给es jvm heap,性能是绝对高的,因为大量的数据都可以缓存在内存里面。

虽然heap对于es来说是非常重要的,jvm heap被es用来存放很多内存中的数据结构来提供更快的操作性能。但是还有另外一个内存的用户,那就是lucene。lucene的设计就是要使用底层的os filesystem cache来缓存数据结构。lucene的segment是保存在单独的文件中的。因为这些segment是不可变的,所以这些文件实际上也从来不会改变。这样的话,就可以更好的缓存这些文件,底层的os cache会将hot segment驻留在内存中以供更快的访问。这些segment包括了倒排索引(为了全文检索)以及正排索引(为了聚合操作)。lucene的性能是严重依赖于底层的os的,但是如果我们给了过多的内存到es的jvm heap,那么就没有足够的内存留给lucene。这会极大的影响性能。

这里想告诉大家的是,就是说,es的性能很大的一块,其实是由有多少内存留给操作系统的os cache,供lucene去缓存索引文件,来决定的。所以说lucene的os cache有多少是非常重要的。

一般建议的是,将50%的内存分配给es jvm heap,然后留50%的内存给os cache。留给os cache的内存是不会不使用的,lucene会将剩下的内存全部用光,用来cache segment file。如果我们没有对任何分词的text field进行聚合操作,那么我们就不需要使用fielddata,我们甚至可以考虑给os cache更多的内存,因为fielddata是要用jvm heap。如果我们给jvm heap更少的内存,那么实际上es的性能反而会更好,因为更多的内存留给了lucene用os cache提升索引读写性能,同时es的jvm heap的gc耗时会更少。

es部署的机器上,内存是如何分配的,如何使用的,如何决定我们的操作系统的,我们该如何给jvm和os cache分配内存

3、不要给jvm分配超过32G内存

还有另外一个原因不要将过多的内存分配给es的jvm heap。如果heap小于32G的化,jvm会用一种技术来压缩对象的指针,object pointer。在java中,所有的对象都会被分配到heap中,然后被一个pointer给引用。object pointer会指向heap中的对象,引用的是二进制格式的地址。

对于32位的系统来说,jvm最大的heap size就是4G,解释一下,32位,0和1值,0和1在32位的组合是2^32次方的字节,除以1024就是多少k,再除以1024就是多少mb,再除以1024就是多少gb,最后算下来就是4G。对于64位的系统来说,heap size可以更大,但是64位的object pointer会耗费更多的空间,因为object pointer更大了。比浪费更多内存空间更恶劣的是,过大的object pointer会在cpu,main memory和LLC、L1等多级缓存间移动数据的时候,吃掉更多的带宽。

所以jvm用了一种技术,叫做compressed oops来解决object pointer耗费过大空间的问题。这个技术的核心思想是,不要让object pointer引用内存中的二进制地址,而是让object pointer引用object offset。这就意味着32位的pointer可以引用400万个对象,而不是400万字节。这也意味着,使用32位的pointer,最大的heap大小可以到32G。此时只要heap size在32G以内,jvm就会自动启用32位的object pointer,因为32位的对象指针,足够引用32G的内存了,就可以用32位的pointer替代64位的pointer。但是32位的pointer比64位的pointer可以耗费更少的内存耗费。

如果你给jvm heap分配的内存小于32G,此时jvm会自动使用32位的object pointer,同时是让pointer指向对象的offset,32位的object pointer就足以引用32G的内存,同时32位的pointer占用的内存空间很少,对cpu和memory之间移动数据的带宽开销也很少。这个过程就叫做compressed oops。

但是一旦我们越过了32G这个界限,就是给jvm heap分配了超过32G的内存,比较坑了。就没有办法用32位的pointer+引用object offset(偏移量)的模式了,因为32位的pointer最多引用32G的内存,超过了32G,就没法用32位pointer。不用32位pointer,就只能用64位pointer,才能引用超过32G的内存空间。此时pointer就会退回到传统的object pointer引用对象的二进制地址的模式,此时object pinter的大小会急剧增长,更多的cpu到内存的带宽会被占据,更多的内存被耗费。实际上,不用compressed oops时,你如果给jvm heap分配了一个40~50G的内存的可用空间,实际上被object pointer可能都要占据十几G的内存空间,可用的空间量,可能跟使用了compressed oops时的32GB内存的可用空间,20多个G,几乎是一样的。

因此,即使我们有很多内存,但是还是要分配给heap在32GB以内,否则的话浪费更多的内存,降低cpu性能,而且会让jvm回收更大的heap。

综上所述,如果你给jvm heap分配超过32G的内存,实际上是没有什么意义的,因为用64位的pointer,1/3的内存都给object pointer给占据了,这段内存空间就浪费掉了。还不如分配32G以内,启用compressed oops,可用空间跟你分配50个G的内存,是一样的。

所以也正是因为32G的限制,一般来说,都是建议说,如果你的es要处理的数据量上亿的话,几亿,或者十亿以内的规模的话,建议,就是用64G的内存的机器比较合适,有个5台,差不多也够了。给jvm heap分配32G,留下32G给os cache。

4、在32G以内的话具体应该设置heap为多大?

这个是根据具体情况而定的,不是固定死的,根据不同的jvm和平台而变。一般而言,将jvm heap size设置为31G比较安全一些。主要是要确保说,你设置的这个jvm heap大小,可以让es启用compressed oops这种优化机制。此外,可以给jvm option加入-XX:+PrintFlagsFinal,然后可以打印出来UseCompressedOops是否为true。这就可以让我们找到最佳的内存设置。因为可以不断调节内存大小,然后观察是否启用compressed oops。

举例来说,如果在mac os上启动一个java 1.7,同时将heap size设置为32600mb,那么compressed oops是会开启的;但是如果设置为32766m,compressed oops就不会开启。相反的是,使用jdk 1.8的化,分配32766m,compressed oops是会开启的,设置为32767m,就不会开启。所以说,这个东西不是固定的。根据不同的操作系统以及jvm版本而定。

在es启动日志中,我们可以查看compressed oops是否开启,比如下面的字样:[2015-12-16 13:53:33,417][INFO ][env] [Illyana Rasputin] heap size [989.8mb], compressed ordinary object pointers [true]。

5、对于有1TB内存的超大内存机器该如何分配?

如果我们的机器是一台超级服务器,内存资源甚至达到了1TB,或者512G,128G,该怎么办?首先es官方是建议避免用这种超级服务器来部署es集群的,但是如果我们只有这种机器可以用的话,我们要考虑以下几点:

(1)我们是否在做大量的全文检索?考虑一下分配4~32G的内存给es进程,同时给lucene留下其余所有的内存用来做os filesystem cache。所有的剩余的内存都会用来cache segment file,而且可以提供非常高性能的搜索,几乎所有的数据都是可以在内存中缓存的,es集群的性能会非常高

(2)是否在做大量的排序或者聚合操作?聚合操作是不是针对数字、日期或者未分词的string?如果是的化,那么还是给es 4~32G的内存即可,其他的留给es filesystem cache,可以将聚合好用的正排索引,doc values放在os cache中

(3)如果在针对分词的string做大量的排序或聚合操作?如果是的化,那么就需要使用fielddata,这就得给jvm heap分配更大的内存空间。此时不建议运行一个节点在机器上,而是运行多个节点在一台机器上,那么如果我们的服务器有128G的内存,可以运行两个es节点,然后每个节点分配32G的内存,剩下64G留给os cache。如果在一台机器上运行多个es node,建议设置:cluster.routing.allocation.same_shard.host: true。这会避免在同一台物理机上分配一个primary shard和它的replica shard。

cluster.routing.allocation.same_shard.host:默认值是false,如果设置为true,那么就不允许将一个primary shard和replica shard分配到同一个物理机上,也许这个物理机上启动了多个es实例。

有可能你有一台超级服务器,32核CPU+128G内存,这个时候的话呢,可能你在这台机器上启动两个es进程,但是默认情况下,有可能一个shard的primary shard被分配到了这台物理机上的node1,同时这个primary shard的replica shard被分配到了这台物理机上的node2,此时,primary shard和replica shard就在同一台物理机上了。

可用性是比较低的,因为如果这台物理机挂掉了,比较惨烈了,primary shard和replica shard全部丢失

6、swapping

如果频繁的将es进程的内存swap到磁盘上,绝对会是一个服务器的性能杀手。想象一下,内存中的操作都是要求快速完成的,如果需要将内存页的数据从磁盘swap回main memory的化,性能会有多差。如果内存被swap到了磁盘,那么100微秒的操作会瞬间变成10毫秒,那么如果是大量的这种内存操作呢?这会导致性能急剧下降。

因此通常建议彻底关闭机器上的swap,swapoff -a,如果要永久性关闭,需要在/etc/fstab中配置

如果没法完全关闭swap,那么可以尝试调低swappiness至,这个值是控制os会如何将内存swap到磁盘的。这会在正常情况下阻止swap,但是在紧急情况下,还是会swap。一般用sysctl来设置,vm.swappiness = 1。如果swappiness也不能设置,那么就需要启用mlockall,这就可以让我们的jvm lock住自己的内存不被swap到磁盘上去,在elasticsearch.yml中可以设置:bootstrap.mlockall: true。

白话Elasticsearch71-ES生产集群部署之各个节点以daemon模式运行以及优雅关闭

官方指导
启动ES https://www.elastic.co/guide/en/elasticsearch/reference/current/starting-elasticsearch.html#starting-elasticsearch

停止ES https://www.elastic.co/guide/en/elasticsearch/reference/current/stopping-elasticsearch.html

启动 ES

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

可以从命令行启动Elasticsearch,如下所示:

./bin/elasticsearch
  • 1

默认情况下,Elasticsearch在前台运行,将其日志打印到标准输出(stdout),可以通过按停止Ctrl-C。

后台运行

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

在生产环境中,会使用daemon进程的方式来启动es,而不是直接采用前台进程的方式来启动es,具体命令如下

./bin/elasticsearch -d -p pid


这样es会在后端运行,并且会在es的home目录下面生产一个pid的文件,pid文件中记录了当前es的进程id

上面命令中的-d option用来指定es以daemon进程方式启动,并且-p option指定将进程id记录在指定文件中.

es启动后,日志信息可以在ES_HOME/logs目录中查看

此外,启动es进程的时候,还可以直接覆盖一些配置,使用-E即可

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

优雅的关闭 ES

停止ES https://www.elastic.co/guide/en/elasticsearch/reference/current/stopping-elasticsearch.html

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

优雅的关闭es,可以确保es关闭的很干净,并且优雅关闭资源。举例来说,如果node在一个合理的顺序下关闭了,首先会将自己从cluster中优雅移除,fsync translog日志到磁盘中去,然后执行其他相关的cleanup活动。

如果我们将es用service的方式来运行,那么可以通过server管理功能来停止es。

如果我们是直接启动es的,可以control-C停止es,或者是发送SEGTERM信号给es进程

jps | grep Elasticsearch

kill -SIGTERM 15516
  • 1
  • 2
  • 3

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

优雅的关闭 ES停止ES https://www.elastic.co/guide/en/elasticsearch/reference/current/stopping-elasticsearch.html
在这里插入图片描述
在这里插入图片描述
优雅的关闭es,可以确保es关闭的很干净,并且优雅关闭资源。举例来说,如果node在一个合理的顺序下关闭了,首先会将自己从cluster中优雅移除,fsync translog日志到磁盘中去,然后执行其他相关的cleanup活动。
如果我们将es用service的方式来运行,那么可以通过server管理功能来停止es。
如果我们是直接启动es的,可以control-C停止es,或者是发送SEGTERM信号给es进程
jps | grep Elasticsearch
kill -SIGTERM 15516123在这里插入图片描述 

2、rolling upgrade(节点依次重启策略)

 

 

rolling upgrade会让es集群每次升级一个node,对于终端用户来说,是没有停机时间的。在一个es集群中运行多个版本,长时间的话是不行的,因为shard是没法从一较新版本的node上replicate到较旧版本的node上的。

 

 

 

先部署一个es 5.3.2版本,将配置文件放在外部目录,同时将data和log目录都放在外部,然后插入一些数据,然后再开始下面的升级过程

 

 

 

adduser elasticsearch

 

passwd elasticsearch

 

 

 

chown -R elasticsearch /usr/local/elasticsearch

 

chown -R elasticsearch /var/log/elasticsearch

 

chown -R elasticsearch /var/data/elasticsearch

 

chown -R elasticsearch /etc/elasticsearch

 

 

 

su elasticsearch

 

 

 

elasticsearch -d -Epath.conf=/etc/elasticsearch

 

 

 

curl -XPUT 'http://localhost:9200/forum/article/1?pretty' -d '

 

{

  "title": "first article",

 

  "content": "this is my first article"

 

}'

 

 

 

(1)禁止shard allocation

 

 

停止一个node之后,这个node上的shard全都不可用了,此时shard allocation机制会等待一分钟,然后开始shard recovery过程,也就是将丢失掉的primary shard的replica shard提升为primary shard,同时创建更多的replica shard满足副本数量,但是这个过程会导致大量的IO操作,是没有必要的。因此在开始升级一个node,以及关闭这个node之前,先禁止shard allocation机制:

 

 

 

curl -XPUT 'http://localhost:9200/_cluster/settings?pretty' -d '

 

{

  "persistent": {

    "cluster.routing.allocation.enable": "none"

 

  }

 

}'

 

 

 

(2)停止非核心业务的写入操作,以及执行一次flush操作

 

 

可以在升级期间继续写入数据,但是如果在升级期间一直写入数据的话,可能会导致重启节点的时候,shard recovery的时间变长,因为很多数据都是translog里面,没有flush到磁盘上去。如果我们暂时停止数据的写入,而且还进行一次flush操作,把数据都刷入磁盘中,这样在node重启的时候,几乎没有什么数据要从translog中恢复的,重启速度会很快,因为shard recovery过程会很快。用下面这行命令执行flush:POST _flush/synced。但是flush操作是尽量执行的,有可能会执行失败,如果有大量的index写入操作的话。所以可能需要多次执行flush,直到它执行成功。

 

 

 

curl -XPOST 'http://localhost:9200/_flush/synced?pretty'

 

 

 

(3)停止一个node然后升级这个node

 

 

在完成node的shard allocation禁用以及flush操作之后,就可以停止这个node。

 

 

 

如果你安装了一些插件,或者是自己设置过jvm.options文件的话,需要先将/usr/local/elasticsearch/plugins拷贝出来,作为一个备份,jvm.options也拷贝出来

 

 

 

将老的es安装目录删除,然后将最新版本的es解压缩,而且要确保我们绝对不会覆盖config、data、log等目录,否则就会导致我们丢失数据、日志、配置文件还有安装好的插件。

 

 

 

kill -SIGTERM 15516

 

 

 

(4)升级plugin

 

 

可以将备份的plugins目录拷贝回最新解压开来的es安装目录中,包括你的jvm.options

 

 

 

自己去官网,找到各个plugin的git地址,git地址上,都有每个plugin version跟es version之间的对应关系。要检查一下所有的plugin是否跟要升级的es版本是兼容的,如果不兼容,那么需要先用elasticsearch-plugin脚本重新安装最新版本的plugin。

 

(5)启动es node

 

 

接着要注意在启动es的时候,在命令行里用-Epath.conf= option来指向一个外部已经配置好的config目录。这样的话最新版的es就会复用之前的所有配置了,而且也会根据配置文件中的地址,找到对应的log、data等目录。然后再日志中查看这个node是否正确加入了cluster,也可以通过下面的命令来检查:GET _cat/nodes。

 

 

 

elasticsearch -d -Epath.conf=/etc/elasticsearch

 

 

 

(6)在node上重新启用shard allocation

 

 

一旦node加入了cluster之后,就可以重新启用shard allocation

 

 

 

curl -XPUT 'http://localhost:9200/_cluster/settings?pretty' -d '

 

{

  "persistent": {

    "cluster.routing.allocation.enable": "all"

 

  }

 

}'

 

 

 

(7)等待node完成shard recover过程

 

 

我们要等待cluster完成shard allocation过程,可以通过下面的命令查看进度:GET _cat/health。一定要等待cluster的status从yellow变成green才可以。green就意味着所有的primary shard和replica shard都可以用了。

 

 

 

curl -XGET 'http://localhost:9200/_cat/health?pretty'

 

 

 

在rolling upgrade期间,primary shard如果分配给了一个更新版本的node,是一定不会将其replica复制给较旧的版本的node的,因为较新的版本的数据格式跟较旧的版本是不兼容的。但是如果不允许将replica shard复制给其他node的话,比如说此时集群中只有一个最新版本的node,那么有些replica shard就会是unassgied状态,此时cluster status就会保持为yellow。此时,就可以继续升级其他的node,一旦其他node变成了最新版本,那么就会进行replica shard的复制,然后cluster status会变成green。

 

 

 

如果没有进行过flush操作的shard是需要一些时间去恢复的,因为要从translog中恢复一些数据出来。可以通过下面的命令来查看恢复的进度:GET _cat/recovery。

 

 

 

(8)重复上面的步骤,直到将所有的node都升级完成

 

22.Elasticsearch生产集群中的索引管理(一)

1、创建索引

 

(1)创建索引的语法

 

 

用settings给这个索引在创建时可以添加一些设置,还有可以初始化一些type的mapping

 

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/twitter?pretty' -d '
{
    "settings" : {
        "index" : {
            "number_of_shards" : 3, 
            "number_of_replicas" : 2 
        }
    },
    "mappings" : {
            "properties" : {
                "field1" : { "type" : "text" }
            }
    }
}'

 

(2)索引创建返回消息的解释

默认情况下,索引创建命令会在每个primary shard的副本开始进行复制以后,或者是请求超时以后,返回一个响应消息,类似下面这样的。

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "twitter"
}

acknowledged表明了这个索引有没有创建成功。

shards_acknowledged表明了每个primary shard有没有足够数量的replica开始进行复制了。

有可能这两个参数会为false,但是索引依然可以创建成功。因为这些参数仅仅是表明在请求超时之前,那两个行为有没有成功,也有可能请求超时了,在超时前都没成功,但是超时后在es server端还是都执行了。

如果acknoledged是false,那么就可能是超时了,此时接受到响应消息的时候,cluster state都还没变更,没有加入新创建的index,但是也许之后还是会创建这个index。如果shards_acknowledged是false,那么可能在primary shard进行副本copy之前,就timeout了,但是此时也许index创建成功了,而且cluster state已经加入了新创建的index。

2、删除索引

curl -XDELETE 'http://elasticsearch02:9200/twitter?pretty'

3、查询索引设置信息

curl -XGET 'http://elasticsearch02:9200/twitter?pretty'

4、打开/关闭索引

curl -XPOST 'http://elasticsearch02:9200/twitter/_close?pretty'
curl -XPOST 'http://elasticsearch02:9200/twitter/_open?pretty'

如果关闭了一个索引之后,那么这个索引是不会带来任何的性能开销了,只要保留这个索引的元数据即可,然后对这个索引的读写操作都不会成功。一个关闭的索引可以接着再打开,打开以后会进行shard recovery过程。

比如说你在做一些运维操作的时候,现在你要对某一个索引做一些配置,运维操作,修改一些设置,关闭索引,不允许写入,成功以后再打开索引

5、压缩索引

shrink命令可以将一个已有的索引压缩成一个新的索引,同时primary shard会更少。因为以前提到过,primary shard因为涉及到document的hash路由问题,所以是不允许修改的。但是如果要减少index的primary shard,可以用shrink命令来压缩index。但是压缩后的shard数量必须可以被原来的shard数量整除。举例来说,一个有8个primary shard的index可以被压缩成4个,2个,或者1个primary shard的index。

压缩索引,是这样啊,如果你的索引中本来比如是要保留7天的数据,那么给了10个shard,但是现在需求变了,这个索引只要保留3天的数据就可以了,那么数据量变小了,就不需要10个shard了,就可以做shrink操作,5个shard。

shrink命令的工作流程如下:

(1)首先,它会创建一个跟source index的定义一样的target index,但是唯一的变化就是primary shard变成了指定的数量

(2)接着它会将source index的segment file直接用hard-link的方式连接到target index的segment file,如果操作系统不支持hard-link,那么就会将source index的segment file都拷贝到target index的data dir中,会很耗时。如果用hard-link会很快

(3)最后,会将target index进行shard recovery恢复

如果要shrink index,那么这个index必须先被标记为read only,而且这个index的每个shard的某一个copy,可以是primary或者是replica,都必须被复制到一个节点上去。默认情况下,index的每个shard有可能在不同机器上的,比如说,index有5个shard,shard0和shard1在机器1上,shard2、shard3在机器2上,shard4在机器3上。现在还得把shard0,shard1,shard2,shard3,shard4全部拷贝到一个同一个机器上去,但是可以是shard0的replica shard。而且每个primary shard都必须存在。可以通过下面的命令来完成。其中index.routing.allocation.require._name必须是某个node的名称,这个都是可以自己设置的。

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/twitter/_settings?pretty' -d '
{
  "settings": {
    "index.routing.allocation.require._name": "node-elasticsearch-02", 
    "index.blocks.write": true 
  }
}'

注意:"node-elasticsearch-02"这个是当前es节点的名称,在elasticsearch.yml中配置的名称

这个命令会花费一点时间将source index每个shard的一个copy都复制到指定的node上去,可以通过GET _cat/recovery?v命令来追踪这个过程的进度。

等上面的shard copy relocate过程结束之后,就可以shrink一个index,用下面的命令即可:POST my_source_index/_shrink/my_target_index。如果target index被添加进了cluster state之后,这个命令就会立即返回,不是等待shrink过程完成之后才返回的。当然还可以用下面的命令来shrink的时候修改target index的设置,在settings里就可以设置target index的primary shard的数量。

curl -H "Content-Type:application/json" -XPOST 'http://elasticsearch02:9200/twitter/_shrink/twitter_shrinked?pretty' -d '
{
  "settings": {
    "index.number_of_replicas": 1,
    "index.number_of_shards": 1, 
    "index.codec": "best_compression" 
  }
}'

当然也是需要监控整个shrink的过程的,用GET _cat/recovery?v即可。

6、rollover index

rollover命令可以将一个alias重置到一个新的索引上去,如果已经存在的index被认为太大或者数据太旧了。这个命令可以接收一个alias名称,还有一系列的condition。如果索引满足了condition,那么就会创建一个新的index,同时alias会指向那个新的index。比如下面的命令。举例来说,有一个logs-0000001索引,给了一个别名是logs_write,接着发起了一个rollover的命令,如果logs_write别名之前指向的那个index,也就是logs-0000001,创建了超过7天,或者里面的document已经超过了1000个了,然后就会创建一个logs-000002的索引,同时logs_write别名会指向新的索引。

这个命令其实是很有用的,特别是针对这种用户访问行为日志的数据,或者是一些联机事务系统的数据的进入,你可以写一个shell脚本,每天0:00的时候就执行以下rollover命令,此时就判断,如果说之前的索引已经存在了超过1天了,那么此时就创建一个新的索引出来,同时将别名指向新的索引。自动去滚动创建新的索引,保持每个索引就只有一个小时,一天,七天,三天,一周,一个月。

类似用es来做日志平台,就可能分布式电商平台,可能订单系统的日志,单独的一个索引,要求的是保留最近3天的日志就可以了。交易系统的日志,是单独的一个索引,要求的是保留最近30天的日志。

我们来操作下看下对应的命令

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/logs-000001?pretty' -d ' 
{
  "aliases": {
    "logs_write": {}
  }
}'

在kibana上面执行的命令为:

PUT /logs-000001
{
  
  "aliases": {
    "logs_write": {}
  }
  
}

创建一个索引logs-000001,给这个索引指定别名为logs_write

接下来向索引中添加数据

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/logs-000001/_doc/1?pretty' -d '
{
    "userid": 1,
    "page": 1
}'
curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/logs-000001/_doc/2?pretty' -d '
{
    "userid": 2,
    "page": 2
}'
curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/logs-000001/_doc/3?pretty' -d '
{
    "userid": 3,
    "page": 3
}'
PUT /logs-000001/autuor/4
{
  "userid": 4,
    "page": 4
}

接下来我们设置rollover的条件,当索引超过一天或者文档数目超过了3条,我们就自定创建一个新的索引,别名指向这个新的索引

curl -H "Content-Type:application/json" -XPOST 'http://elasticsearch02:9200/logs_write/_rollover?pretty' -d ' 
{
  "conditions": {
    "max_age":   "1d",
    "max_docs":  3
  }
}'
POST /logs_write/_rollover
{
  "conditions": {
    "max_age":   "1d",
    "max_docs":  3
  }
  
  
}

 这里命令中logs_write是索引的别名,上面命令执行成功之后,返回的命令如下

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "old_index" : "logs-000001",
  "new_index" : "logs-000002",
  "rolled_over" : true,
  "dry_run" : false,
  "conditions" : {
    "[max_age: 1d]" : false,
    "[max_docs: 3]" : true
  }
}

logs_write就指向了新创建的索引logs-000002,不再指向logs-000001

这个过程常见于网站用户行为日志数据,比如按天来自动切分索引,写个脚本定时去执行rollover,就会自动不断创建新的索引,但是别名永远是一个,对于外部的使用者来说,用的都是最新数据的索引。

默认情况下,如果已经存在的那个索引是用-符号加上一个数字结尾的,比如说logs-000001,那么新索引的名称就会是自动给那个数字加1,比如logs-000002,自动就是给一个6位的数字,而且会自动补零

但是我们也可以自己指定要的新的索引名称,比如下面这样:

POST /my_alias/_rollover/my_new_index_name
{
  "conditions": {
    "max_age":   "7d",
    "max_docs":  1000
  }
}

当然,还可以在rollover的时候,给新的index进行新的设置:

POST /logs_write/_rollover
{
  "conditions" : {
    "max_age": "7d",
    "max_docs": 1000
  },
  "settings": {
    "index.number_of_shards": 2
  }
}

 Elasticsearch生产集群中的索引管理(二)

1、mapping管理
下面这个命令是在创建索引

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/twitter?pretty' -d ' 
{
  "mappings": {
      "properties": {
        "message": {
          "type": "text"
        }
      }
  }
}'
PUT /book
{
  "settings":{
    "number_of_shards":5,
    "number_of_replicas":1
  },
  "mappings":{
    "novel":{
      "properties":{
        "name":{
          "type":"text",
          "index":true,
         "store":false
        },
        "author":{
          "type":"keyword"
        },
        "count":{
          "type":"long"
        },
        "on-sale":{
          "type":"date",
          "format":"yyyy-MM-dd HH:mm:ss ||yyyy-MM-dd||epoch_millis"
        },
        "descr":{
          "type":"text"
        }
    }
  }
  }
}

下面这个命令是给一个已有的索引添加一个field

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/twitter/_mapping?pretty' -d '
{
  "properties": {
    "user_name": {
      "type": "text"
    }
  }
}'
POST /book/novel/_mapping
{
  
   "properties": {
    "user_name": {
      "type": "text"
    }
  }
}

 查看索引mapping的信息为

curl -H "Content-Type:application/json" -XGET 'http://elasticsearch02:9200/twitter/_mapping?pretty'

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 2、索引别名管理

curl -H "Content-Type:application/json" -XPOST 'http://elasticsearch02:9200/_aliases?pretty' -d '
{
    "actions" : [
        { "add" : { "index" : "twitter", "alias" : "twitter_prod" } }
    ]
}'
POST /_aliases
{
  "actions" : [
        { "add" : 
        { "index" : "book", 
        "alias" : "twitter_prod" } }
    ]
}

curl -H "Content-Type:application/json" -XPOST 'http://elasticsearch02:9200/_aliases?pretty' -d '{    "actions" : [        { "add" : { "index" : "twitter", "alias" : "twitter_prod" } }    ]}'

给book这个索引设置别名为twitter_prod

删除别名

curl -H "Content-Type:application/json" -XPOST 'http://elasticsearch02:9200/_aliases?pretty' -d '
{
    "actions" : [ 
        { "remove" : { "index" : "twitter", "alias" : "twitter_prod" } }
    ]
}'
POST /_aliases
{
  "actions" : [
        { "remove": 
        { "index" : "book", 
        "alias" : "twitter_prod" } }
    ]
}

重命名alias的命令(先删除再添加)

一个alias绑定多个index

POST /_aliases
{
    "actions" : [
        { "add" : { "indices" : ["test1", "test2"], "alias" : "alias1" } }
    ]
}
POST /_aliases
{
  "actions" : [
        { "add" : { "indices" : ["test1", "test2"], "alias" : "alias1" } }
    ]
}

将索引中的某部分数据保存到一个别名上面

POST /_aliases
{
    "actions" : [
        {
            "add" : {
                 "index" : "test1",
                 "alias" : "alias2",
                 "filter" : { "term" : { "user" : "kimchy" } }
            }
        }
    ]
}

删除别名

DELETE /logs_20162801/_alias/current_day

查看别名

GET /_alias/2016

索引别名常常和之前讲解的那个rollover结合起来,我们为了性能和管理方便,每天的数据都rollover出来一个索引,但是在对数据分析的时候,可能是这样子的,有一个索引access-log,指向了当日最新的数据,用来计算实时数据的; 有一个索引access-log-7days,指向了7天的7个索引,可以让我们进行一些周数据的统计和分析。

3、index settings管理

curl -H "Content-Type:application/json" -XPUT 'http://elasticsearch02:9200/twitter/_settings?pretty' -d '
{
    "index" : {
        "number_of_replicas" : 1
    }
}'

查看setting

curl -XGET 'http://elasticsearch02:9200/twitter/_settings?pretty'

经常可能要对index做一些settings的调整,常常和之前的index open和close结合起来

4、index template管理

我们可以定义一些index template,这样template会自动应用到新创建的索引上去。template中可以包含settings和mappings,还可以包含一个pattern,决定了template会被应用到哪些index上。而且template仅仅在index创建的时候会被应用,修改template,是不会对已有的index产生影响的。

PUT /_template/template_access_log
{
  "template": "access-log-*",
  "settings":{
    "number_of_shards":5,
    "number_of_replicas":1
  },
  "mappings":{
    "novel":{
      "properties":{
        "name":{
          "type":"text",
          "index":true,
         "store":false
        },
        "author":{
          "type":"keyword"
        },
        "count":{
          "type":"long"
        },
        "on-sale":{
          "type":"date",
          "format":"yyyy-MM-dd HH:mm:ss ||yyyy-MM-dd||epoch_millis"
        },
        "descr":{
          "type":"text"
        }
    }
    }
  }
    
}

上面就是创建一个模板,模板的正则表达式为"template": "access-log-*",,如果索引的名称是以access-log开头的就使用上面的这个模块,template_access_log是模板的名称

索引的type类型为novel

我们创建一个索引

PUT /access-log-32

上面的索引创建之后,默认就上面上面的模板

我们来查看索引的信息GET /access-log-32/

{
  "access-log-32" : {
    "aliases" : { },
    "mappings" : {
      "novel" : {
        "properties" : {
          "author" : {
            "type" : "keyword"
          },
          "count" : {
            "type" : "long"
          },
          "descr" : {
            "type" : "text"
          },
          "name" : {
            "type" : "text"
          },
          "on-sale" : {
            "type" : "date",
            "format" : "yyyy-MM-dd HH:mm:ss ||yyyy-MM-dd||epoch_millis"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1602099132085",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "gAqr-dFuSrOPLuvsutdSKw",
        "version" : {
          "created" : "6081299"
        },
        "provided_name" : "access-log-32"
      }
    }
  }
}

删除模板

curl -H "Content-Type:application/json" -XDELETE 'http://elasticsearch02:9200/_template/template_access_log?pretty'
DELETE /_template/template_access_log

查看模板的信息

GET /_template/template_access_log

执行命令之后的结果如下

{
  "template_access_log" : {
    "order" : 0,
    "index_patterns" : [
      "access-log-*"
    ],
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "number_of_replicas" : "1"
      }
    },
    "mappings" : {
      "novel" : {
        "properties" : {
          "descr" : {
            "type" : "text"
          },
          "author" : {
            "type" : "keyword"
          },
          "name" : {
            "index" : true,
            "store" : false,
            "type" : "text"
          },
          "count" : {
            "type" : "long"
          },
          "on-sale" : {
            "format" : "yyyy-MM-dd HH:mm:ss ||yyyy-MM-dd||epoch_millis",
            "type" : "date"
          }
        }
      }
    },
    "aliases" : { }
  }
}

1、indice stat

indice stat对index上发生的不同类型的操作都提供了统计。这个api提供了index level的统计信息,不过大多数统计信息也可以从node level获取。

curl -H "Content-Type:application/json" -XGET 'http://elasticsearch02:9200/twitter/_stats?pretty'
GET /access-log-32/_stats

整个命令执行的结果如下

{
  "_shards" : {
    "total" : 10,
    "successful" : 5,
    "failed" : 0
  },
  "_all" : {
    "primaries" : {
      "docs" : {
        "count" : 0,
        "deleted" : 0
      },
      "store" : {
        "size_in_bytes" : 1305
      },
      "indexing" : {
        "index_total" : 0,
        "index_time_in_millis" : 0,
        "index_current" : 0,
        "index_failed" : 0,
        "delete_total" : 0,
        "delete_time_in_millis" : 0,
        "delete_current" : 0,
        "noop_update_total" : 0,
        "is_throttled" : false,
        "throttle_time_in_millis" : 0
      },
      "get" : {
        "total" : 0,
        "time_in_millis" : 0,
        "exists_total" : 0,
        "exists_time_in_millis" : 0,
        "missing_total" : 0,
        "missing_time_in_millis" : 0,
        "current" : 0
      },
      "search" : {
        "open_contexts" : 0,
        "query_total" : 0,
        "query_time_in_millis" : 0,
        "query_current" : 0,
        "fetch_total" : 0,
        "fetch_time_in_millis" : 0,
        "fetch_current" : 0,
        "scroll_total" : 0,
        "scroll_time_in_millis" : 0,
        "scroll_current" : 0,
        "suggest_total" : 0,
        "suggest_time_in_millis" : 0,
        "suggest_current" : 0
      },
      "merges" : {
        "current" : 0,
        "current_docs" : 0,
        "current_size_in_bytes" : 0,
        "total" : 0,
        "total_time_in_millis" : 0,
        "total_docs" : 0,
        "total_size_in_bytes" : 0,
        "total_stopped_time_in_millis" : 0,
        "total_throttled_time_in_millis" : 0,
        "total_auto_throttle_in_bytes" : 104857600
      },
      "refresh" : {
        "total" : 25,
        "total_time_in_millis" : 0,
        "listeners" : 0
      },
      "flush" : {
        "total" : 5,
        "periodic" : 0,
        "total_time_in_millis" : 1
      },
      "warmer" : {
        "current" : 0,
        "total" : 10,
        "total_time_in_millis" : 0
      },
      "query_cache" : {
        "memory_size_in_bytes" : 0,
        "total_count" : 0,
        "hit_count" : 0,
        "miss_count" : 0,
        "cache_size" : 0,
        "cache_count" : 0,
        "evictions" : 0
      },
      "fielddata" : {
        "memory_size_in_bytes" : 0,
        "evictions" : 0
      },
      "completion" : {
        "size_in_bytes" : 0
      },
      "segments" : {
        "count" : 0,
        "memory_in_bytes" : 0,
        "terms_memory_in_bytes" : 0,
        "stored_fields_memory_in_bytes" : 0,
        "term_vectors_memory_in_bytes" : 0,
        "norms_memory_in_bytes" : 0,
        "points_memory_in_bytes" : 0,
        "doc_values_memory_in_bytes" : 0,
        "index_writer_memory_in_bytes" : 0,
        "version_map_memory_in_bytes" : 0,
        "fixed_bit_set_memory_in_bytes" : 0,
        "max_unsafe_auto_id_timestamp" : -1,
        "file_sizes" : { }
      },
      "translog" : {
        "operations" : 0,
        "size_in_bytes" : 550,
        "uncommitted_operations" : 0,
        "uncommitted_size_in_bytes" : 275,
        "earliest_last_modified_age" : 0
      },
      "request_cache" : {
        "memory_size_in_bytes" : 0,
        "evictions" : 0,
        "hit_count" : 0,
        "miss_count" : 0
      },
      "recovery" : {
        "current_as_source" : 0,
        "current_as_target" : 0,
        "throttle_time_in_millis" : 0
      }
    },
    "total" : {
      "docs" : {
        "count" : 0,
        "deleted" : 0
      },
      "store" : {
        "size_in_bytes" : 1305
      },
      "indexing" : {
        "index_total" : 0,
        "index_time_in_millis" : 0,
        "index_current" : 0,
        "index_failed" : 0,
        "delete_total" : 0,
        "delete_time_in_millis" : 0,
        "delete_current" : 0,
        "noop_update_total" : 0,
        "is_throttled" : false,
        "throttle_time_in_millis" : 0
      },
      "get" : {
        "total" : 0,
        "time_in_millis" : 0,
        "exists_total" : 0,
        "exists_time_in_millis" : 0,
        "missing_total" : 0,
        "missing_time_in_millis" : 0,
        "current" : 0
      },
      "search" : {
        "open_contexts" : 0,
        "query_total" : 0,
        "query_time_in_millis" : 0,
        "query_current" : 0,
        "fetch_total" : 0,
        "fetch_time_in_millis" : 0,
        "fetch_current" : 0,
        "scroll_total" : 0,
        "scroll_time_in_millis" : 0,
        "scroll_current" : 0,
        "suggest_total" : 0,
        "suggest_time_in_millis" : 0,
        "suggest_current" : 0
      },
      "merges" : {
        "current" : 0,
        "current_docs" : 0,
        "current_size_in_bytes" : 0,
        "total" : 0,
        "total_time_in_millis" : 0,
        "total_docs" : 0,
        "total_size_in_bytes" : 0,
        "total_stopped_time_in_millis" : 0,
        "total_throttled_time_in_millis" : 0,
        "total_auto_throttle_in_bytes" : 104857600
      },
      "refresh" : {
        "total" : 25,
        "total_time_in_millis" : 0,
        "listeners" : 0
      },
      "flush" : {
        "total" : 5,
        "periodic" : 0,
        "total_time_in_millis" : 1
      },
      "warmer" : {
        "current" : 0,
        "total" : 10,
        "total_time_in_millis" : 0
      },
      "query_cache" : {
        "memory_size_in_bytes" : 0,
        "total_count" : 0,
        "hit_count" : 0,
        "miss_count" : 0,
        "cache_size" : 0,
        "cache_count" : 0,
        "evictions" : 0
      },
      "fielddata" : {
        "memory_size_in_bytes" : 0,
        "evictions" : 0
      },
      "completion" : {
        "size_in_bytes" : 0
      },
      "segments" : {
        "count" : 0,
        "memory_in_bytes" : 0,
        "terms_memory_in_bytes" : 0,
        "stored_fields_memory_in_bytes" : 0,
        "term_vectors_memory_in_bytes" : 0,
        "norms_memory_in_bytes" : 0,
        "points_memory_in_bytes" : 0,
        "doc_values_memory_in_bytes" : 0,
        "index_writer_memory_in_bytes" : 0,
        "version_map_memory_in_bytes" : 0,
        "fixed_bit_set_memory_in_bytes" : 0,
        "max_unsafe_auto_id_timestamp" : -1,
        "file_sizes" : { }
      },
      "translog" : {
        "operations" : 0,
        "size_in_bytes" : 550,
        "uncommitted_operations" : 0,
        "uncommitted_size_in_bytes" : 275,
        "earliest_last_modified_age" : 0
      },
      "request_cache" : {
        "memory_size_in_bytes" : 0,
        "evictions" : 0,
        "hit_count" : 0,
        "miss_count" : 0
      },
      "recovery" : {
        "current_as_source" : 0,
        "current_as_target" : 0,
        "throttle_time_in_millis" : 0
      }
    }
  },
  "indices" : {
    "access-log-32" : {
      "uuid" : "gAqr-dFuSrOPLuvsutdSKw",
      "primaries" : {
        "docs" : {
          "count" : 0,
          "deleted" : 0
        },
        "store" : {
          "size_in_bytes" : 1305
        },
        "indexing" : {
          "index_total" : 0,
          "index_time_in_millis" : 0,
          "index_current" : 0,
          "index_failed" : 0,
          "delete_total" : 0,
          "delete_time_in_millis" : 0,
          "delete_current" : 0,
          "noop_update_total" : 0,
          "is_throttled" : false,
          "throttle_time_in_millis" : 0
        },
        "get" : {
          "total" : 0,
          "time_in_millis" : 0,
          "exists_total" : 0,
          "exists_time_in_millis" : 0,
          "missing_total" : 0,
          "missing_time_in_millis" : 0,
          "current" : 0
        },
        "search" : {
          "open_contexts" : 0,
          "query_total" : 0,
          "query_time_in_millis" : 0,
          "query_current" : 0,
          "fetch_total" : 0,
          "fetch_time_in_millis" : 0,
          "fetch_current" : 0,
          "scroll_total" : 0,
          "scroll_time_in_millis" : 0,
          "scroll_current" : 0,
          "suggest_total" : 0,
          "suggest_time_in_millis" : 0,
          "suggest_current" : 0
        },
        "merges" : {
          "current" : 0,
          "current_docs" : 0,
          "current_size_in_bytes" : 0,
          "total" : 0,
          "total_time_in_millis" : 0,
          "total_docs" : 0,
          "total_size_in_bytes" : 0,
          "total_stopped_time_in_millis" : 0,
          "total_throttled_time_in_millis" : 0,
          "total_auto_throttle_in_bytes" : 104857600
        },
        "refresh" : {
          "total" : 25,
          "total_time_in_millis" : 0,
          "listeners" : 0
        },
        "flush" : {
          "total" : 5,
          "periodic" : 0,
          "total_time_in_millis" : 1
        },
        "warmer" : {
          "current" : 0,
          "total" : 10,
          "total_time_in_millis" : 0
        },
        "query_cache" : {
          "memory_size_in_bytes" : 0,
          "total_count" : 0,
          "hit_count" : 0,
          "miss_count" : 0,
          "cache_size" : 0,
          "cache_count" : 0,
          "evictions" : 0
        },
        "fielddata" : {
          "memory_size_in_bytes" : 0,
          "evictions" : 0
        },
        "completion" : {
          "size_in_bytes" : 0
        },
        "segments" : {
          "count" : 0,
          "memory_in_bytes" : 0,
          "terms_memory_in_bytes" : 0,
          "stored_fields_memory_in_bytes" : 0,
          "term_vectors_memory_in_bytes" : 0,
          "norms_memory_in_bytes" : 0,
          "points_memory_in_bytes" : 0,
          "doc_values_memory_in_bytes" : 0,
          "index_writer_memory_in_bytes" : 0,
          "version_map_memory_in_bytes" : 0,
          "fixed_bit_set_memory_in_bytes" : 0,
          "max_unsafe_auto_id_timestamp" : -1,
          "file_sizes" : { }
        },
        "translog" : {
          "operations" : 0,
          "size_in_bytes" : 550,
          "uncommitted_operations" : 0,
          "uncommitted_size_in_bytes" : 275,
          "earliest_last_modified_age" : 0
        },
        "request_cache" : {
          "memory_size_in_bytes" : 0,
          "evictions" : 0,
          "hit_count" : 0,
          "miss_count" : 0
        },
        "recovery" : {
          "current_as_source" : 0,
          "current_as_target" : 0,
          "throttle_time_in_millis" : 0
        }
      },
      "total" : {
        "docs" : {
          "count" : 0,
          "deleted" : 0
        },
        "store" : {
          "size_in_bytes" : 1305
        },
        "indexing" : {
          "index_total" : 0,
          "index_time_in_millis" : 0,
          "index_current" : 0,
          "index_failed" : 0,
          "delete_total" : 0,
          "delete_time_in_millis" : 0,
          "delete_current" : 0,
          "noop_update_total" : 0,
          "is_throttled" : false,
          "throttle_time_in_millis" : 0
        },
        "get" : {
          "total" : 0,
          "time_in_millis" : 0,
          "exists_total" : 0,
          "exists_time_in_millis" : 0,
          "missing_total" : 0,
          "missing_time_in_millis" : 0,
          "current" : 0
        },
        "search" : {
          "open_contexts" : 0,
          "query_total" : 0,
          "query_time_in_millis" : 0,
          "query_current" : 0,
          "fetch_total" : 0,
          "fetch_time_in_millis" : 0,
          "fetch_current" : 0,
          "scroll_total" : 0,
          "scroll_time_in_millis" : 0,
          "scroll_current" : 0,
          "suggest_total" : 0,
          "suggest_time_in_millis" : 0,
          "suggest_current" : 0
        },
        "merges" : {
          "current" : 0,
          "current_docs" : 0,
          "current_size_in_bytes" : 0,
          "total" : 0,
          "total_time_in_millis" : 0,
          "total_docs" : 0,
          "total_size_in_bytes" : 0,
          "total_stopped_time_in_millis" : 0,
          "total_throttled_time_in_millis" : 0,
          "total_auto_throttle_in_bytes" : 104857600
        },
        "refresh" : {
          "total" : 25,
          "total_time_in_millis" : 0,
          "listeners" : 0
        },
        "flush" : {
          "total" : 5,
          "periodic" : 0,
          "total_time_in_millis" : 1
        },
        "warmer" : {
          "current" : 0,
          "total" : 10,
          "total_time_in_millis" : 0
        },
        "query_cache" : {
          "memory_size_in_bytes" : 0,
          "total_count" : 0,
          "hit_count" : 0,
          "miss_count" : 0,
          "cache_size" : 0,
          "cache_count" : 0,
          "evictions" : 0
        },
        "fielddata" : {
          "memory_size_in_bytes" : 0,
          "evictions" : 0
        },
        "completion" : {
          "size_in_bytes" : 0
        },
        "segments" : {
          "count" : 0,
          "memory_in_bytes" : 0,
          "terms_memory_in_bytes" : 0,
          "stored_fields_memory_in_bytes" : 0,
          "term_vectors_memory_in_bytes" : 0,
          "norms_memory_in_bytes" : 0,
          "points_memory_in_bytes" : 0,
          "doc_values_memory_in_bytes" : 0,
          "index_writer_memory_in_bytes" : 0,
          "version_map_memory_in_bytes" : 0,
          "fixed_bit_set_memory_in_bytes" : 0,
          "max_unsafe_auto_id_timestamp" : -1,
          "file_sizes" : { }
        },
        "translog" : {
          "operations" : 0,
          "size_in_bytes" : 550,
          "uncommitted_operations" : 0,
          "uncommitted_size_in_bytes" : 275,
          "earliest_last_modified_age" : 0
        },
        "request_cache" : {
          "memory_size_in_bytes" : 0,
          "evictions" : 0,
          "hit_count" : 0,
          "miss_count" : 0
        },
        "recovery" : {
          "current_as_source" : 0,
          "current_as_target" : 0,
          "throttle_time_in_millis" : 0
        }
      }
    }
  }
}

这里包括了doc数量,index size,segment的内存使用量,merge,flush,refresh,translog等底层机制的统计信息。

2、segment

查看low level的lucene的segment信息,可以用来查看shard和index的更多的信息,包括一些优化信息,因为delete而浪费的数据空间,等等。

查看low level的lucene的segment信息,可以用来查看shard和index的更多的信息,包括一些优化信息,因为delete而浪费的数据空间,等等。

我们来创建一个索引,给这个索引添加一条数据,我们来看下segement的信息

默认情况下,一个索引是5个分区,一个副本一个分区下面的文件是由多个segements组成的

POST  /book/novel
{
  
    
}
PUT  /book/novel/2
{
   "name": "盘龙",
   "author": "天下第一",
   "count": 1000,
   "on-sale": "2020-09-25",
   "descr": "sjdkjdfskjkjfsdkjkjfskjsfdkjfdkjs"
  
}
GET /book/_segments

对应的结果为

{
  "_shards" : {
    "total" : 10,
    "successful" : 5,
    "failed" : 0
  },
  "indices" : {
    "book" : {
      "shards" : {
        "0" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "2yARaxaeQ5mDZp9Wdp_g_Q"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "1" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "2yARaxaeQ5mDZp9Wdp_g_Q"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 1,
            "segments" : {
              "_0" : {
                "generation" : 0,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 2385,
                "memory_in_bytes" : 670,
                "committed" : false,
                "search" : true,
                "version" : "7.7.3",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "2" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "2yARaxaeQ5mDZp9Wdp_g_Q"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 1,
            "segments" : {
              "_1" : {
                "generation" : 1,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 4049,
                "memory_in_bytes" : 1476,
                "committed" : false,
                "search" : true,
                "version" : "7.7.3",
                "compound" : false,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ],
        "3" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "2yARaxaeQ5mDZp9Wdp_g_Q"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 0,
            "segments" : { }
          }
        ],
        "4" : [
          {
            "routing" : {
              "state" : "STARTED",
              "primary" : true,
              "node" : "2yARaxaeQ5mDZp9Wdp_g_Q"
            },
            "num_committed_segments" : 0,
            "num_search_segments" : 1,
            "segments" : {
              "_0" : {
                "generation" : 0,
                "num_docs" : 1,
                "deleted_docs" : 0,
                "size_in_bytes" : 2385,
                "memory_in_bytes" : 670,
                "committed" : false,
                "search" : true,
                "version" : "7.7.3",
                "compound" : true,
                "attributes" : {
                  "Lucene50StoredFieldsFormat.mode" : "BEST_SPEED"
                }
              }
            }
          }
        ]
      }
    }
  }
}

_3: 是segment的名称,这个名称跟这个segment files的文件名有关系,一个segment的所有文件都是用这个名称开头的

generation:每次新生成一个segment,就会递增一个数值,segment名称也就是这个数值

num_docs:在这个segment中存储的没有被删除的document的数量

deleted_docs:在这个segment中存储的被删除的document数量,这个数值是无所谓的,因为每次segment merge的时候都会删除这些document

size_in_bytes:这个segment占用的磁盘空间

memory_in_bytes:segment需要将一些数据缓存在内存中,这样搜索性能才能更高,这个数值就是segment占用的内存的空间大小

committed:segment是否被sync到磁盘上去了,commit/sync的segment可以确保数据不会丢失,但是即使这个值是false也不要紧,因为数据同时被存储在了translog里面,es进程重启的时候,是可以重放translog中的日志来恢复数据的

search:这个segment能不被搜索,如果是false的话,可能这个segment已经被sync到磁盘上,但是还没有进行refresh,所以不能被搜索

version:lucene的版本号

compound:如果是true的话,意味着lucene将这个segment所有的文件都merge成了一个文件,进而可以节省file descriptor的消耗

3、shard存储信息

查询索引shard copy的存储信息,可以看到哪些节点上有哪些shard copy,shard copy的allocation id,每个shard copy的唯一标识,包括打开索引的时候遇到的报错。默认情况下,会显示至少有一个未分配的copy的shard,如果cluster health是yellow,会显示至少有一个未分配的replica的shard,当cluster health是red,会显示有未分配的primary的shard。但是用status=green可以看到每个shard的信息。

curl -H "Content-Type:application/json" -XGET 'http://elasticsearch02:9200/twitter/_shard_stores?pretty'
curl -XGET 'http://elasticsearch02:9200/twitter/_shard_stores?status=green&pretty'
GET /book/_shard_stores
{
  "indices" : {
    "book" : {
      "shards" : {
        "4" : {
          "stores" : [
            {
              "2yARaxaeQ5mDZp9Wdp_g_Q" : {
                "name" : "2yARaxa",
                "ephemeral_id" : "AvkJI2xuRVe1HbZE0gwcnA",
                "transport_address" : "192.168.1.4:9300",
                "attributes" : {
                  "ml.machine_memory" : "8445952000",
                  "xpack.installed" : "true",
                  "ml.max_open_jobs" : "20",
                  "ml.enabled" : "true"
                }
              },
              "allocation_id" : "ymqCAIuPSEq6aQGakqZ-dw",
              "allocation" : "primary"
            }
          ]
        },
        "1" : {
          "stores" : [
            {
              "2yARaxaeQ5mDZp9Wdp_g_Q" : {
                "name" : "2yARaxa",
                "ephemeral_id" : "AvkJI2xuRVe1HbZE0gwcnA",
                "transport_address" : "192.168.1.4:9300",
                "attributes" : {
                  "ml.machine_memory" : "8445952000",
                  "xpack.installed" : "true",
                  "ml.max_open_jobs" : "20",
                  "ml.enabled" : "true"
                }
              },
              "allocation_id" : "EX6D9GpBQFqKNe6o5fn8cg",
              "allocation" : "primary"
            }
          ]
        },
        "2" : {
          "stores" : [
            {
              "2yARaxaeQ5mDZp9Wdp_g_Q" : {
                "name" : "2yARaxa",
                "ephemeral_id" : "AvkJI2xuRVe1HbZE0gwcnA",
                "transport_address" : "192.168.1.4:9300",
                "attributes" : {
                  "ml.machine_memory" : "8445952000",
                  "xpack.installed" : "true",
                  "ml.max_open_jobs" : "20",
                  "ml.enabled" : "true"
                }
              },
              "allocation_id" : "fsHj6zvoRGWSwvTQU5ioMA",
              "allocation" : "primary"
            }
          ]
        },
        "3" : {
          "stores" : [
            {
              "2yARaxaeQ5mDZp9Wdp_g_Q" : {
                "name" : "2yARaxa",
                "ephemeral_id" : "AvkJI2xuRVe1HbZE0gwcnA",
                "transport_address" : "192.168.1.4:9300",
                "attributes" : {
                  "ml.machine_memory" : "8445952000",
                  "xpack.installed" : "true",
                  "ml.max_open_jobs" : "20",
                  "ml.enabled" : "true"
                }
              },
              "allocation_id" : "c50uxaKIQLq5FU4LzFE8Gw",
              "allocation" : "primary"
            }
          ]
        },
        "0" : {
          "stores" : [
            {
              "2yARaxaeQ5mDZp9Wdp_g_Q" : {
                "name" : "2yARaxa",
                "ephemeral_id" : "AvkJI2xuRVe1HbZE0gwcnA",
                "transport_address" : "192.168.1.4:9300",
                "attributes" : {
                  "ml.machine_memory" : "8445952000",
                  "xpack.installed" : "true",
                  "ml.max_open_jobs" : "20",
                  "ml.enabled" : "true"
                }
              },
              "allocation_id" : "nbxj6tw6QkiZTWEwqLMiIw",
              "allocation" : "primary"
            }
          ]
        }
      }
    }
  }
}

0:shard id

stores:shard的每个copy的store信息

sPa3OgxLSYGvQ4oPs-Tajw:node id,持有一个copy的node信息

allocationi_id:copy的allocationid

allocation:shard copy的角色

5、flush

flush API可以让我们去强制flush多个索引,索引flush以后,就会释放掉这个索引占用的内存,因为会将os cache里的数据强制fsync到磁盘上去,同时还会清理掉translog中的日志。默认情况下,es会不定时自动触发flush操作,以便于及时清理掉内存。POST twitter/_flush,这条命令即可。

flush命令可以接受下面两个参数,wait_if_going,如果设置为true,那么flush api会等到flush操作执行完以后再返回,即使需要等待其他的flush操作先完成。默认的值是false,这样的话,如果有其他flush操作在执行,就会报错;force,如果没有必要flush的话,是不是会强制一个flush

POST /book/_flush

6、refresh

refresh用来显式的刷新一个index,这样可以让这个refresh之前执行的所有操作,都处于可见的状态。POST twitter/_refresh

POST /book/_refresh

7、force merge

force merge API可以强制合并多个索引文件,可以将一个shard对应的lucene index的多个segment file都合并起来,可以减少segment file的数量。POST /twitter/_forcemerge。

POST /book/_forcemerge

Elasticsearch生产集群中的索引管理(四)

2、fielddata
fielddata cache,在对field进行排序或者聚合的时候,会用到这个cache。这个cache会将所有的field value加载到内存里来,这样可以加速排序或者聚合的性能。但是每个field的field data cache的构建是很成本很高昂的,因此建议给机器提供充足的内存来保持fielddata cache。

indices.fielddata.cache.size,这个参数可以控制这个cache的大小,可以是30%这种相对大小,或者是12GB这种绝对大小,默认是没有限制的。

fielddata的原理之前讲解过了,其实是对分词后的field进行排序或者聚合的时候,才会使用fielddata这种jvm内存数据结构。如果是对普通的未分词的field进行排序或者聚合,其实默认是用的doc value数据结构,是在os cache中缓存的。

Fielddata针对text字段在默认时是禁用的,因为文本聚合占用大量的空间,查看文档

https://www.cnblogs.com/sanduzxcvbnm/p/12092298.html

3、node query cache
query cache用来缓存query的结果,每个node都有一个query cache,使用的是LRU策略,会自动清理数据。但是query cache仅仅会对那些filter后的数据进行缓存,对search后的数据是不会进行缓存的。indices.queries.cache.size,控制query cache的大小,默认是jvm heap的10%。

如果只是要根据一些field进行等值的查询或过滤,那么用filter操作,性能会比较好,query cache

对filter的查询会做缓存的处理

4、index buffer

index buffer用来存储最新索引的的document。如果这个buffer满了之后,document就会被写入一个segment file,但是此时其实是写入os cache中,没有用fsync同步到磁盘,这就是refresh过程,写入os cache中,就可以被搜索到了。然后flush之后,就fsync到了磁盘上。indices.memory.index_buffer_size,控制index buffer的大小,默认是10%。indices.memory.min_index_buffer_size,buffer的最小大小,默认是48mb。

index buffer,增删改document,数据先写入index buffer,写到磁盘文件里面去,不可见的,refresh刷入磁盘文件对应的os cache里面,还有translog一份数据

插入一个文档首先会写入到index  buffer中

Elasticsearch生产集群中的索引管理(五)

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 segement合并的时候会将标记为delete的文档真正的从内存和物理空间删除,对于merger后台的线程如果是机械硬盘,默认参数设置为1

2、translog

默认情况下,对于索引的增删改查为了保证数据的不丢失,都需要将translog从os cache保存到磁盘上面,也就是所谓的fsync操作成功之后,才对这次请求返回是否成功

2、默认情况下,当translog的日志文件大小默认达到了512M或者每隔5秒钟,都会支持fsync操作,将将translog从os cache保存到磁盘上面

对于每一次文档的增删改查,我们不一定必须每执行一次请求都等待需要将translog从os cache保存到磁盘上面,这样会大大提高效率

因为默认每隔5秒都会执行一次

fsync操作,将将translog从os cache保存到磁盘上面

如果es挂了,也之后丢失5秒内数据的操作

对于默认的5秒或者512M数据,执行fsync操作的参数,我们可以按照下面的参数进行修改

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 底层模块深入解析之threadpool

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 Elasticsearch性能调优之慢查询日志

es里面的操作,主要分为两种,一种写入(增删改),另一种是查询(搜索)

我们分别要识别出来,哪些写入操作性能比较慢,哪些查询操作性能比较慢,先要识别出来有性能问题的这些慢查询,慢写入,然后才能去考虑如何优化写入的性能,如何优化搜索的性能

搜索慢查询日志

无论是慢查询日志,还是慢写入日志,都是针对shard级别的,因为大家应该知道,无论你是执行增删改,还是执行搜索,都是对某个数据执行写入或者是搜索,其实都是到某个shard上面去执行的

shard上面执行的慢的写入或者是搜索,都会记录在针对这个shard的日志中

阈值的意思,就是说,什么叫做慢?搜索,5s叫做慢?还是10s叫做慢?或者是1s叫做慢?

比如说,你设置一个阈值,5s就是搜索的阈值,5s就叫做慢,那么一旦一个搜索请求超过了5s之后,就会记录一条慢搜索日志到日志文件中

shard level的搜索慢查询日志,辉将搜索性能较慢的查询写入一个专门的日志文件中。可以针对query phase和fetch phase单独设置慢查询的阈值,而具体的慢查询阈值设置如下所示:

在elasticsearch.yml中,设置

index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s
index.search.slowlog.threshold.query.trace: 500ms

index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug: 500ms
index.search.slowlog.threshold.fetch.trace: 200ms

quer超过10s会以warm的日志级别打印日常

超过5秒以info格式打印日志

而慢查询日志具体的格式,都是在log4j2.properties中配置的,比如下面的配置:

appender.index_search_slowlog_rolling.type = RollingFile
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.fileName = ${sys:es.logs}_index_search_slowlog.log
appender.index_search_slowlog_rolling.layout.type = PatternLayout
appender.index_search_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %.10000m%n
appender.index_search_slowlog_rolling.filePattern = ${sys:es.logs}_index_search_slowlog-%d{yyyy-MM-dd}.log
appender.index_search_slowlog_rolling.policies.type = Policies
appender.index_search_slowlog_rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.index_search_slowlog_rolling.policies.time.interval = 1
appender.index_search_slowlog_rolling.policies.time.modulate = true

logger.index_search_slowlog_rolling.name = index.search.slowlog
logger.index_search_slowlog_rolling.level = trace
logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
logger.index_search_slowlog_rolling.additivity = false

索引慢写入日志

可以用如下的配置来设置索引写入慢日志的阈值:

index.indexing.slowlog.threshold.index.warn: 10s
index.indexing.slowlog.threshold.index.info: 5s
index.indexing.slowlog.threshold.index.debug: 2s
index.indexing.slowlog.threshold.index.trace: 500ms
index.indexing.slowlog.level: info
index.indexing.slowlog.source: 1000

用下面的log4j.properties配置就可以设置索引慢写入日志的格式:

appender.index_indexing_slowlog_rolling.type = RollingFile
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.fileName = ${sys:es.logs}_index_indexing_slowlog.log
appender.index_indexing_slowlog_rolling.layout.type = PatternLayout
appender.index_indexing_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %marker%.10000m%n
appender.index_indexing_slowlog_rolling.filePattern = ${sys:es.logs}_index_indexing_slowlog-%d{yyyy-MM-dd}.log
appender.index_indexing_slowlog_rolling.policies.type = Policies
appender.index_indexing_slowlog_rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.index_indexing_slowlog_rolling.policies.time.interval = 1
appender.index_indexing_slowlog_rolling.policies.time.modulate = true

logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = trace
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
logger.index_indexing_slowlog.additivity = false
把这个东西记录好了以后,就可以什么呢?es集群运维管理员,就需要经常每天去看看,正常情况下,慢查询应该是比较少数的。所以比如你每天检查一次,如果发现某个查询特别慢,就要通知写这个查询的RD,开发人员,让他们去优化一下性能。

查询慢于 10 秒输出一个 WARN 日志。


获取慢于 500 毫秒输出一个 DEBUG 日志。


索引慢于 5 秒输出一个 INFO 日志。

Elasticsearch性能调优之索引写入性能优化

转载自博客:es写入性能优化对于skywalking和elk来说都是相当的经典呀

https://blog.****.net/lm324114/category_9764695.html

1、用bulk批量写入

你如果要往es里面灌入数据的话,那么根据你的业务场景来,如果你的业务场景可以支持,可以做到,让你将一批数据聚合起来,一次性写入es,那么就尽量采用bulk的方式,每次批量写个几百条这样子。

bulk批量写入的性能比你一条一条写入大量的document的性能要好很多。但是如果要知道一个bulk请求最佳的大小,需要对单个es node的单个shard做压测。先bulk写入100个document,然后200个,400个,以此类推,每次都将bulk size加倍一次。如果bulk写入性能开始变平缓的时候,那么这个就是最佳的bulk大小。并不是bulk size越大越好,而是根据你的集群等环境具体要测试出来的,因为越大的bulk size会导致内存压力过大,因此最好一个请求不要发送超过10mb的数据量。

2、使用多线程将数据写入es

单线程发送bulk请求是无法最大化es集群写入的吞吐量的。如果要利用集群的所有资源,就需要使用多线程并发将数据bulk写入集群中。为了更好的利用集群的资源,这样多线程并发写入,可以减少每次底层磁盘fsync的次数和开销。一样,可以对单个es节点的单个shard做压测,比如说,先是2个线程,然后是4个线程,然后是8个线程,16个,每次线程数量倍增。一旦发现es返回了TOO_MANY_REQUESTS的错误,JavaClient也就是EsRejectedExecutionException,那么就说明es是说已经到了一个并发写入的最大瓶颈了,此时我们就知道最多只能支撑这么高的并发写入了。

3、增加refresh间隔

默认的refresh间隔是1s,用index.refresh_interval参数可以设置,这样会其强迫es每秒中都将内存中的数据写入磁盘中,创建一个新的segment file。正是这个间隔,让我们每次写入数据后,1s以后才能看到。但是如果我们将这个间隔调大,比如30s,可以接受写入的数据30s后才看到,那么我们就可以获取更大的写入吞吐量,因为30s内都是写内存的,每隔30s才会创建一个segment file。

4、禁止refresh和replia

如果我们要一次性加载大批量的数据进es,可以先禁止refresh和replica复制,将index.refresh_interval设置为-1,将index.number_of_replicas设置为0即可。这可能会导致我们的数据丢失,因为没有refresh和replica机制了。但是不需要创建segment file,也不需要将数据replica复制到其他的replica shasrd上面去。此时写入的速度会非常快,一旦写完之后,可以将refresh和replica修改回正常的状态。

5、禁止swapping交换内存

可以将swapping禁止掉,有的时候,如果要将es jvm内存交换到磁盘,再交换回内存,大量磁盘IO,性能很差,需要锁定es的内存

6、给filesystem cache更多的内存

filesystem cache被用来执行更多的IO操作,如果我们能给filesystem cache更多的内存资源,那么es的写入性能会好很多。

7、使用自动生成的id

如果我们要手动给es document设置一个id,那么es需要每次都去确认一下那个id是否存在,这个过程是比较耗费时间的。如果我们使用自动生成的id,那么es就可以跳过这个步骤,写入性能会更好。对于你的业务中的表id,可以作为es document的一个field。

8、用性能更好的硬件

我们可以给filesystem cache更多的内存,也可以使用SSD替代机械硬盘,避免使用NAS等网络存储,考虑使用RAID 0来条带化存储提升磁盘并行读写效率,等等。

9、index buffer

如果我们要进行非常重的高并发写入操作,那么最好将index buffer调大一些,indices.memory.index_buffer_size,这个可以调节大一些,设置的这个index buffer大小,是所有的shard公用的,但是如果除以shard数量以后,算出来平均每个shard可以使用的内存大小,一般建议,但是对于每个shard来说,最多给512mb,因为再大性能就没什么提升了。es会将这个设置作为每个shard共享的index buffer,那些特别活跃的shard会更多的使用这个buffer。默认这个参数的值是10%,也就是jvm heap的10%,如果我们给jvm heap分配10gb内存,那么这个index buffer就有1gb,对于两个shard共享来说,是足够的

Elasticsearch性能调优之搜索性能优化


1、给filesysgtem cache更多的内存

es的搜索引擎严重依赖于底层的filesystem cache,你如果给filesystem cache更多的内存,尽量让内存可以容纳所有的indx segment file索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。

比如说,你,es节点有3台机器,每台机器,看起来内存很多,64G,总内存,64 * 3

每台机器给es jvm heap是32G,那么剩下来留给filesystem cache的就是每台机器才32g,总共集群里给filesystem cache的就是32 * 3 = 96gb内存

如果你此时,你整个,磁盘上索引数据文件,在3台机器上,一共占用了1T的磁盘容量,你的es数据量是1t

你觉得你的性能能好吗?filesystem cache的内存才100g,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差

归根结底,你要让es性能要好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半

比如说,你一共要在es中存储1T的数据,那么你的多台机器留个filesystem cache的内存加起来综合,至少要到512G,至少半数的情况下,搜索是走内存的,性能一般可以到几秒钟,2秒,3秒,5秒

如果最佳的情况下,我们自己的生产环境实践经验,最好是用es就存少量的数据,就是你要用来搜索的那些索引,内存留给filesystem cache的,就100G,那么你就控制在100gb以内,相当于是,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在1秒以内

尽量在es里,就存储必须用来搜索的数据,比如说你现在有一份数据,有100个字段,其实用来搜索的只有10个字段,建议是将10个字段的数据,存入es,剩下90个字段的数据,可以放mysql,hadoop hbase,都可以,es中存储简单的搜索数据,具体的详细列表信息存储在mysql等数据库中

这样的话,es数据量很少,10个字段的数据,都可以放内存,就用来搜索,搜索出来一些id,通过id去mysql,hbase里面去查询明细的数据

本质上就是要让filesystem cache能够存储足够多的segment file数据,这样搜索走的就是内存,不再走磁盘,效率相当的高

2、用更快的硬件资源

(1)给filesystem cache更多的内存资源
(2)用SSD固态硬盘
(3)使用本地存储系统,不要用NFS等网络存储系统
(4)给更多的CPU资源

3、document模型设计

document模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es能支持的操作就是那么多,不要考虑用es做一些它不好操作的事情。如果真的有那种操作,尽量在document模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如join,nested,parent-child搜索都要尽量避免,性能都很差的。

两个思路,在搜索/查询的时候,要执行一些业务强相关的特别复杂的操作:

(1)在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入加的字段里面
(2)自己用java程序封装,es能做的,用es来做,搜索出来的数据,在java程序里面去做,比如说我们,基于es,用java封装一些特别复杂的操作

es搜索的时候不要使用es提供复杂的api,这样会影响搜索的性能,列如我们可以将复杂的操作的结果预先计算出来,然后在写入数据的时候,就设计好模型,加几个字段,把处理好的数据写入加的字段里面

4、预先index data

为了性能,提前优化data index时的数据模型,比如说document有一个price field,然后大多数查询都对一个固定的范围,对这个field使用range范围查询,那么可以提前将这个price的范围处理出来,写入一个字段中。比如下面这样:

PUT index/type/1
{
  "designation": "spoon",
  "price": 13
}
GET index/_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 10 },
          { "from": 10, "to": 100 },
          { "from": 100 }
        ]
      }
    }
  }
}

我们完全可以增加一个price_range字段:

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "price_range": {
          "type": "keyword"
        }
      }
    }
  }
}

然后写入的时候,直接计算出来这个range:

PUT index/type/1
{
  "designation": "spoon",
  "price": 13,
  "price_range": "10-100"
}
然后搜索的时候,就可以直接用term查询了,性能非常高:
GET index/_search
{
  "aggs": {
    "price_ranges": {
      "terms": {
        "field": "price_range"
      }
    }
  }
}

5、预热filesystem cache

如果我们重启了es,那么filesystem cache是空壳的,就需要不断的查询才能重新让filesystem cache热起来,我们可以先说动对一些数据进行查询。

比如说,你本来一个查询,要用户点击以后才执行,才能从磁盘加载到filesystem cache里,第一次执行要10s,以后每次就几百毫秒

你完全可以,自己早上的时候,就程序执行那个查询,预热,数据就加载到filesystem cahce,程序执行的时候是10s,以后用户真的来看的时候就才几百毫秒

提前让需要被搜索的索引的segement file数据都提交加载到filesystem cache,这样查询的时候都是通过查询操作系统的filesystem cache,不再通过磁盘来进行查询,效率就会相当的高

6、避免使用script脚本

说实话,一般是避免使用es script的,实际生产中更是少用,性能不高,尽量不要使用

总结:本质上要搜索的快,一定要保证操作系统的filesystem cache要足够的大,你要搜索的索引的数据如果都能够全部加载到filesystem cache中,那么性能最好

Elasticsearch性能调优之磁盘读写性能优化(减少磁盘空间的使用量)

聚合:doc values:就是数据本身的值
搜索:倒排索引,index
评分:norms
近似匹配:index_options(freqs)

任何一个功能不需要,就把对应的存储的数据给干掉,这样可以节约磁盘空间的占用,也可以优化磁盘的读写性能

默认情况下,es在写入document到索引的时候,都会给大多数的field增加一份doc values,就是正排索引,用来进行聚合或者排序的。比如说,如果我们有一个叫做foo的数字类型field,我们要对这个字段运行histograms aggr聚合操作,但是可能我们并不需要对这个字段进行搜索,那么就可以禁止为这个字段生成倒排索引,只需要doc value正排索引即可。禁用倒排索引:

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "integer",
          "index": false
        }
      }
    }
  }
}

这样字段foo就不能够被搜索了,但是能够做聚合、排序等操作

text类型的field会存储norm值,用来计算doc的相关度分数,如果我们需要对一个text field进行搜索,但是不关心这个field的分数,那么可以禁用norm值

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "norms": false
        }
      }
    }
  }
}

text field还会存储出现频率以及位置,出现频率也是用来计算相关度分数的,位置是用来进行phrase query这种近似匹配操作的,如果我们不需要执行phrase query近似匹配,那么可以禁用位置这个属性:

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "index_options": "freqs"
        }
      }
    }
  }
}

此外,如果我们不关心相关度频分,我们可以配置es仅仅为每个term索引对应的document,我们可以对这个field进行搜索,但是phrase query这种近似匹配会报错,而且相关度评分会不准确:

PUT index
{
  "mappings": {
    "type": {
      "properties": {
        "foo": {
          "type": "text",
          "norms": false,
          "index_options": "freqs"
        }
      }
    }
  }
}

2、不要用默认的动态string类型映射

默认的动态string类型映射会将string类型的field同时映射为text类型以及keyword类型,这会浪费磁盘空间,因为我们不一定两种都需要。通常来说,id field这种字段可能只需要keyword映射,而body field可能只需要text field。

text类型会被分词构建倒排索引

keyword类型不会被分词,对应的是正向排序

映射一个content,content: text,content.内置字段: keyword

可以通过手动设置mappings映射来避免字符串类型的field被自动映射为text和keyword:

PUT index
{
  "mappings": {
    "type": {
      "dynamic_templates": [
        {
          "strings": {
            "match_mapping_type": "string",
            "mapping": {
              "type": "keyword"
            }
          }
        }
      ]
    }
  }
}

3、禁止_all field

_all field会将document中所有field的值都合并在一起进行索引,很耗费空空间,如果不需要一次性对所有的field都进行搜索,那么最好禁用_all field。

4、使用best_compression

_source field和其他field都很耗费磁盘空间,最好是对其使用best_compression进行压缩。用elasticsearch.yml中的index.codec来设置,将其设置为best_compression即可。

5、用最小的最合适的数字类型

es支持4种数字类型,byte,short,integer,long。如果最小的类型就合适,那么就用最小的类型。

数据存储占用空间少了,那么操作系统的file systemcache就能够加载更多的数据,整个搜索的性能就会大大的加深

关于倒排索引和正向索引:参考自己的博客https://www.cnblogs.com/kebibuluan/p/13021918.html

es的存储空间存储的数据为:

1、文档的元数据 doc values ,也就是正排索引。文档的元数据适合做聚合或者排序

2、倒排索引,倒排索引主要用来对字段做搜索,搜索的时候通过搜索得到搜索的关键字属于哪个文档id,然后通过文档id查询得到改文档的具体值,文档写入的时候

需要对文档对分词,分词之后构建倒排索引,倒排索引也需要存储到磁盘上面,一个字段如果设置了可以被搜索,就会构建倒排索引,被分词的字段是不适合做聚合或者排序的,如果一定要多分词的字段做聚合或者排序操作,一定要使用FieldData字段,不清楚的看自己的博客

https://www.cnblogs.com/kebibuluan/p/13021918.html

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 3、

1.ElasticSearch原理是倒排索引和正排索引的转化版
2.DocValues满足非analyed字段的正排索引转化版,Fielddata对应analyed
3.DocValues存在于磁盘,消耗Lucene内存来提升效率,Fielddata存在于ElasticSearch内存(jvm)

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 元数据区存储的就是doc values 正向索引数据

索引区存储的是就是倒排索引的数据

 Elasticsearch生产集群监控之基于cat API进行监控

es集群监控,最好是自己干吧,因为官方出了那种非常棒的x-pack做权限认证,监控,等等,做的都非常好,但是。。。是收费的。。。

自己做es集群监控,就是根据es的一些api,自己写一个java web的应用,自己做前端界面,程序里不断的每隔几秒钟,调用一次后端的接口,获取到各种监控信息,然后用前端页面显示出来,开发开发一个可视化的es集群的监控的工作台

1、GET /_cat/aliases?v

看到集群中有哪些索引别名

alias index filter routing.index routing.search
alias1 test1 - - -
alias2 test1 * - -
alias3 test1 - 1 1
alias4 test1 - 2 1,2

2、GET /_cat/allocation?v

看到每个节点分配了几个shard,对磁盘的占用空间大小,使用率,等等

shards disk.indices disk.used disk.avail disk.total disk.percent host ip node
5 260b 47.3gb 43.4gb 100.7gb 46 127.0.0.1 127.0.0.1 CSUXak2

3、GET /_cat/count?v

看每个索引的document数量

epoch timestamp count
1475868259 15:24:20 120

4、GET /_cat/fielddata?v

看每个节点的jvm heap内存中的fielddata内存占用情况(对分词的field进行聚合/排序要用jvm heap中的正排索引,fielddata)

id host ip node field size
Nqk-6inXQq-OxUfOUI8jNQ 127.0.0.1 127.0.0.1 Nqk-6in body 544b
Nqk-6inXQq-OxUfOUI8jNQ 127.0.0.1 127.0.0.1 Nqk-6in soul 480b

5、GET /_cat/health?v

比较全面的看一个es集群的整体健康状况,主要是看是green,yellow,red

epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1475871424 16:17:04 elasticsearch green 1 1 5 5 0 0 0 0 - 100.0%

6、GET /_cat/indices?v

每个索引的具体的情况,比如有几个shard,多少个document,被删除的document有多少,占用了多少磁盘空间

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open twitter u8FNjxh8Rfy_awN11oDKYQ 1 1 1200 0 88.1kb 88.1kb

7、GET /_cat/master?v

看master node当前的具体的情况,哪个node是当前的master node

id host ip node
YzWoH_2BT-6UjVGDyPdqYg 127.0.0.1 127.0.0.1 YzWoH_2

8、GET /_cat/nodes?v

看每个node的具体的情况,就比如jvm heap内存使用率,内存使用率,cpu load,是什么角色

ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1 65 99 42 3.07 mdi * mJw06l1

9、GET /_cat/pending_tasks?v

看当前pending没执行完的task的具体情况,执行的是什么操作

insertOrder timeInQueue priority source
1685 855ms HIGH update-mapping [foo][t]
1686 843ms HIGH update-mapping [foo][t]
1693 753ms HIGH refresh-mapping [foo][[t]]
1688 816ms HIGH update-mapping [foo][t]
1689 802ms HIGH update-mapping [foo][t]
1690 787ms HIGH update-mapping [foo][t]
1691 773ms HIGH update-mapping [foo][t]

10、GET /_cat/plugins?v&s=component&h=name,component,version,description

看当前集群安装了哪些插件

name component version description
U7321H6 analysis-icu 5.5.1 The ICU Analysis plugin integrates Lucene ICU module into elasticsearch, adding ICU relates analysis components.
U7321H6 analysis-kuromoji 5.5.1 The Japanese (kuromoji) Analysis plugin integrates Lucene kuromoji analysis module into elasticsearch.
U7321H6 analysis-phonetic 5.5.1 The Phonetic Analysis plugin integrates phonetic token filter analysis with elasticsearch.
U7321H6 analysis-smartcn 5.5.1 Smart Chinese Analysis plugin integrates Lucene Smart Chinese analysis module into elasticsearch.
U7321H6 analysis-stempel 5.5.1 The Stempel (Polish) Analysis plugin integrates Lucene stempel (polish) analysis module into elasticsearch.
U7321H6 analysis-ukrainian 5.5.1 The Ukrainian Analysis plugin integrates the Lucene UkrainianMorfologikAnalyzer into elasticsearch.
U7321H6 discovery-azure-classic 5.5.1 The Azure Classic Discovery plugin allows to use Azure Classic API for the unicast discovery mechanism
U7321H6 discovery-ec2 5.5.1 The EC2 discovery plugin allows to use AWS API for the unicast discovery mechanism.
U7321H6 discovery-file 5.5.1 Discovery file plugin enables unicast discovery from hosts stored in a file.
U7321H6 discovery-gce 5.5.1 The Google Compute Engine (GCE) Discovery plugin allows to use GCE API for the unicast discovery mechanism.
U7321H6 ingest-attachment 5.5.1 Ingest processor that uses Apache Tika to extract contents
U7321H6 ingest-geoip 5.5.1 Ingest processor that uses looksup geo data based on ip adresses using the Maxmind geo database
U7321H6 ingest-user-agent 5.5.1 Ingest processor that extracts information from a user agent
U7321H6 jvm-example 5.5.1 Demonstrates all the pluggable Java entry points in Elasticsearch
U7321H6 lang-javascript 5.5.1 The JavaScript language plugin allows to have javascript as the language of scripts to execute.
U7321H6 lang-python 5.5.1 The Python language plugin allows to have python as the language of scripts to execute.
U7321H6 mapper-attachments 5.5.1 The mapper attachments plugin adds the attachment type to Elasticsearch using Apache Tika.
U7321H6 mapper-murmur3 5.5.1 The Mapper Murmur3 plugin allows to compute hashes of a field's values at index-time and to store them in the index.
U7321H6 mapper-size 5.5.1 The Mapper Size plugin allows document to record their uncompressed size at index time.
U7321H6 store-smb 5.5.1 The Store SMB plugin adds support for SMB stores.

11、GET _cat/recovery?v

看shard recovery恢复的一个过程的具体情况

index shard time type stage source_host source_node target_host target_node repository snapshot files files_recovered files_percent files_total bytes bytes_recovered bytes_percent bytes_total translog_ops translog_ops_recovered translog_ops_percent

twitter 0 13ms store done n/a n/a node0 node-0 n/a n/a 0 0 100% 13 0 0 100% 9928 0 0 100.0%

12、GET /_cat/repositories?v

查看用于snapshotting的repository有哪些

id type
repo1 fs
repo2 s3

13、GET /_cat/thread_pool?v

看每个线程池的具体的情况

Z6MkIvC bulk 0 0 0
Z6MkIvC fetch_shard_started 0 0 0
Z6MkIvC fetch_shard_store 0 0 0
Z6MkIvC flush 0 0 0
Z6MkIvC force_merge 0 0 0
Z6MkIvC generic 0 0 0
Z6MkIvC get 0 0 0
Z6MkIvC index 0 0 0
Z6MkIvC listener 0 0 0
Z6MkIvC management 1 0 0
Z6MkIvC refresh 0 0 0
Z6MkIvC search 0 0 0
Z6MkIvC snapshot 0 0 0
Z6MkIvC warmer 0 0 0

14、GET _cat/shards?v

看每个shard的具体的情况

twitter 0 p STARTED 3014 31.1mb 192.168.56.10 H5dfFeA
twitter 0 r UNASSIGNED

15、GET /_cat/segments?v

看每个segement,索引segment文件的情况,在哪个node上,有多少个document,占用了多少磁盘空间,有多少数据在内存中,是否可以搜索

index shard prirep ip segment generation docs.count docs.deleted size size.memory committed searchable version compound
test 3 p 127.0.0.1 _0 0 1 0 3kb 2042 false true 6.5.1 true
test1 3 p 127.0.0.1 _0 0 1 0 3kb 2042 false true 6.5.1 true

16、GET /_cat/snapshots?v&s=id

看当前执行的snapshot的操作

id status start_epoch start_time end_epoch end_time duration indices successful_shards failed_shards total_shards
snap1 FAILED 1445616705 18:11:45 1445616978 18:16:18 4.6m 1 4 1 5
snap2 SUCCESS 1445634298 23:04:58 1445634672 23:11:12 6.2m 2 10 0 10

17、GET /_cat/templates?v&s=name

看当前有的那些tempalte,具体的情况是什么

name template order version
template0 te* 0
template1 tea* 1
template2 teak* 2 7



es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 kibana的权限管控参看博客:

1、https://blog.****.net/snail_bi/article/details/103531415

狠难说!:
https://blog.****.net/snail_bi/article/details/103531415

狠难说!:
elk这块kibana6.8版本之后是支持对权限做控制也支持租户的,可以实现不同的用户访问不同的索引:
1、登录kibana可以设置输入用户名和密码;
2、A系统的用户的日志存储在A系统的对应的索引中,在kibana中可以创建A系统的一个管理员,设置管理员只能访问A系统的索引;
3、B系统的用户的日志存储在B系统的对应的索引中,在kibana中可以创建B系统的一个管理员,设置管理员只能访问B系统的索引;

狠难说!:
https://www.elastic.co/cn/blog/getting-started-with-elasticsearch-security

磁盘优化:可以参考下面这篇文章

es集群的调优2
JVM
启动 ES
优雅的关闭 ES

 如果一个字段不需要被索引,可以将index设置为false,这样这个字段就不需要建立倒排索引,就就能节约空间

第二个如果一个字段需要被搜索,但是不需要进行聚合、排序等可以将doc_values设置为false

一般情况doc_values也是es的数据结构也是存储在磁盘上面的,doc_values是用来做数据的聚合和排序的

如果一个字段不需要做聚合排序,可以将doc_values设置为false

对于一个字段,现在将index已经设置了true,但是也需要进行聚合或者排序,就需要使用到fileddata这个熟悉

Doc_values
默认情况下,大多数字段都已编入索引,这使它们可搜索。反向索引允许查询在唯一的术语排序列表中查找搜索词,并从中立即访问包含该词的文档列表。

sort,aggregtion和访问脚本中的字段值需要不同的数据访问模式。除了查找术语和查找文档外,我们还需要能够查找文档并查找其在字段中具有的术语。

Doc values是在文档索引时构建的磁盘数据结构,这使这种数据访问模式成为可能。它们存储与_source相同的值,但以面向列的方式存储,这对于排序和聚合而言更为有效。几乎所有字段类型都支持Doc值,但对字符串字段除外。字符串一般用来设置搜索

默认情况下,所有支持doc值的字段均已启用它们。如果您确定不需要对字段进行排序或汇总,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间:

比如我们可以通过如下的方式来使得city字段不可以做sort或aggregation:

前言:本文的目的是为后续磁盘空间利用优化做铺垫。主要知识点来源于官网文档
一、doc_value是什么
绝大多数的fields在默认情况下是indexed,因此字段数据是可被搜索的。倒排索引中按照一定顺序存放着terms供搜索,当命中搜索时,返回包含term的document。

terms 中包含很多term

当Sorting、aggregations、scripts access to field这三种情况的时候,我们需要另外的data access模式。这种模式和上述在terms中寻找term并且返回document是不同的

Doc values 为on-disk 数据结构,在document索引时被创建。Doc values 存放的values和 _source这个meta-Fields是一致的。支持除了analyzed string 以外的所有类型。

二、doc_value特性
doc_value 默认情况下是enable的。
column-oriented 存放field,以便sort、aggregate、access the field from a script
disable doc_value:

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "mystring": { 
          "type":       "keyword",
          "doc_values": false
        }
      }
    }
  }
}

三、disable doc_value会怎样
消极影响:sort、aggregate、access the field from script将会无法使用
积极影响:节省磁盘空间