在生产环境中, 要保证服务在各种极限情况下的稳定和高可用, 所以在部署ES集群时, 需要考虑服务器的内存、CPU、磁盘, 集群的网络、节点个数, 并且要优化JVM的各项参数. 首先从这些方面着手进行部署前的规划.

1 服务器的内存

ES非常消耗内存 —— 不是JVM用到的内存, 而是机器的物理内存, 因为ES在运行期间对JVM Heap(堆内存)的需求较小.

ES底层是基于Lucene创建的, 而Lucene是基于磁盘中的文件来读写和保存索引数据(包括倒排索引、正排索引);
Lucene的特点是基于OS File System Cache(操作系统的文件缓存系统), 它会尽可能地把频繁访问的磁盘文件缓存在操作系统的内存中, 从而提高对磁盘文件的读写性能.
关于Lucene的更多资料, 可参考: https://www.cnblogs.com/shoufeng/category/1259723.html.

可以这样说: ES的性能很大程度上(80%)取决于分配给JVM Heap内存之后、服务器的剩余内存大小.
——这些剩余内存会缓存Lucene的索引文件, 缓存的越多, 索引的读写性能都越高, 尤其是检索和聚合操作, 它们需要读取几乎所有的索引数据.

实践建议: 数据量过亿, 建议单台服务器的内存至少要有64GB.

2 服务器的CPU

ES集群对CPU的要求比较低, 一般来说2~8个CPU Core即可满足集群的需求.

实践建议: 尽可能使用多核处理器, 因为并发处理能力会更好.

3 服务器的磁盘

ES生产环境中, 磁盘的读写能力是非常重要的, 尤其对于大量写操作的集群, 比如电商公司将每天的实时日志数据以高并发的方式写入ES集群.

(1) 在磁盘的使用上, 推荐使用SSD(固态硬盘), 而不是(HDD)机械硬盘.

使用SSD, 就需要配置SSD的I/O Scheduler —— 数据写入磁盘时, IO Scheduler会决定将数据从OS Cache刷入到磁盘的时机.
大部分SSD的默认IO Scheduler是CFQ (completely fair queuing), 它会为每个进程都分配一些时间片(time slice), 然后通过磁盘的物理布局来决定如何将数据写入磁盘 (对各个进程的数据写入进行优化), 进而提升写入磁盘的性能.
但是默认的CFQ并不高效. 对SSD来说, 推荐使用Deadline/Noop Scheduler, 它基于写操作被Pending的时间长短来进行写磁盘优化, 而Noop Scheduler就是一个简单的FIFO(先进先出)队列机制.

(2) 此外, 使用RAID 0也是一种提升磁盘读写速度的高效方式, 无论是HDD, 或者SSD都支持RAID 0.

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

(3)最后, 要避免使用与网络相关的存储模式 (network-attached storage, NAS), 比如基于网络的分布式存储模式.

虽然很多供应商都说他们的NAS解决方案性能非常高, 而且比本地存储的可靠性更高, 但在实际使用上还是会有很多风险: 网络传输可能存在比较高的时延, 还可能存在单点故障.

实践建议: 推荐使用SSD, 并调整其IO Scheduler为Deadline/Noop Scheduler, 这可以带来很大的性能提升, 理想情况下可能达到上百倍.

4 集群的网络

对ES这种分布式系统来说, 快速可靠的网络是非常重要的:

高速网络通信可以让ES节点间的通信时延降低;
高带宽可以让Shard的移动、恢复, 以及分配等操作更加快速.

不低于千兆网卡对大多数集群来说都已足够, 但要避免一个集群横跨多个数据中心, 比如异地多机房部署一个集群 —— 跨地域跨机房会降低网络通信和数据传输的效率.

1、 ES集群是一种p2p模式的分布式系统架构, 并不是master-slave主从分布式系统.
2、 ES集群中, 所有节点都是平等的, 任意两个节点之间的通信都很频繁, 如果部署在异地多机房, 就会导致各个节点之间频繁跨地域通信, 通信时延会非常高, 甚至有可能造成集群运行频繁出现异常.
3、 与NAS存储模式一样, 很多供应商都声称他们的跨地域多数据中心是可靠、低时延的, 即使果真如此, 一旦网络出现故障, 整个集群就会不可用. 大多数情况下, 跨地域多机房部署一个ES集群带来的效益要远远低于维护这类集群所付出的额外成本.

实践建议: 不低于千兆网卡, 且不要垮多个数据中心, 尤其不要跨地域多机房.

5 集群的节点个数

ES集群的节点个数:

1、 建议部署少个节点, 但每个节点对应服务器的资源都必须充足;
2、 不建议在一台高性能服务器上部署多个节点: 不仅降低了集群的可用性, 而且集群的维护复杂度也变得更高了.

尽量避免部署大量的低资源的服务器, 因为对运维和管理而言, 管理5个物理机组成的集群, 要比管理10个虚拟机组成的集群要简单简单太多.

实践建议: 小规模、高配置, 但无需超高配置, 会造成资源的浪费.

6 JVM的参数设置

ES的版本越新, 使用的JDK的版本也应该越新, 既提高性能, 也避免一些不常见的系统Bug(包括Lucene和JDK的).

以本系列博文为例, 示例的ES版本为6.6.0, 使用的JDK版本是jdk1.8.0_151.

(1) 如果通过Java API操作ES服务, 那么编译Java程序的JVM版本最好与ES服务器所运行的JVM版本一致.

ES中用到了很多与JVM版本相关的特性, 比如本地序列化机制 (包括IP地址、异常信息等等), 而JVM在不同的minor版本中可能会修改序列化机制, 版本不同可能会导致序列化异常.

(2) 同时官方强烈建议: 不要随意调整JVM的参数设置.

ES是一个非常复杂的分布式软件系统, 它默认的JVM配置经过了大量真实业务场景下的检验, 除非你很明确地知道自己的服务瓶颈出在哪几个参数上, 否则不要调整.

ES服务中, JVM Heap堆内存的大小一般不超过服务器物理内存的一半, 以1/4为宜, 且最多不宜超过32GB.

7 集群的数据量

对很多中小型公司, 建议ES集群承载的数据量在百亿级规模以内.

(1)ES的常见使用场景有:

1、 构建业务搜索功能模块, 且多是垂直领域的搜索: 以网站或APP为例, 数据规模相对比较大, 通常是百万级到亿级;
2、 进行数据分析: 需要消耗更大的内存, 但支持的数据规模要小很多, 通常是十万级到千万级;
3、 用于大规模数据的实时OLAP(联机处理分析), 经典的如ELK Stack, 数据规模可能达到千亿或更多, 集群规模可能达到几十上百节点.

(2)数据量特别大时的处理思路:

如果应用的数据量特别大, 日增量几十上百万, 那就不建议将数据全量写入ES中, ES也不适合这种数据无限膨胀的场景 —— ES消耗内存, 无限膨胀的数据量会导致无法提供足够的内存来支撑大规模数据的快速检索.
此时可以考虑: 将部分热数据 (比如最近一月的数据) 存放到ES中做高频高性能搜索, 将大量的、较少访问的冷数据存放至大数据系统 (比如Hadoop) 中做离线批量处理.

不同数据规模与内存容量下的检索性能表现: 如果服务器的内存可以将ES所需的文件全部纳入到OS Cache中, 就能达到ms(毫秒)级的检索性能; 否则检索会大量访问磁盘, 检索时间就会上升到s(秒)级.

8 总结

要提升ES的性能, 最重要的是规划合理的数据量, 配置足够的物理内存用作OS Cache, 尽可能减少从磁盘中访问数据.