AI摘要 本文讨论了灾备容灾体系的建设，强调了数据在数字信息化建设中的核心地位，并指出了硬件故障、软件错误、网络攻击、自然灾害等因素可能导致的系统崩溃或数据丢失问题。文章分析了公司当前在灾备方面的现状，包括云上生产系统缺乏同城/异地灾备、云下生产系统完全单点、两套系统之间无数据同步和切换能力等问题。提出了建设灾备环境的目标，包括每套生产系统拥有独立灾备、数据恢复点目标（RPO）和业务恢复时间目标（RTO）等，并强调了演练频率和回切能力的重要性。最终目的是在云上生产或云下生产发生整体故障时，能够快速切换/恢复业务与数据，实现对外服务不中断或极短中断。 此内容根据文章生成，仅用于文章内容的解释与总结 灾备容灾体系建设一、背景在当前数字信息化建设中，数据是核心资产。无论是数据中台的运行、数字孪生的建模，还是数字可视化的展示，都需要依赖高质量的数据支持。然而，硬件故障、软件错误、网络攻击、自然灾害等不可预见的因素，都可能导致系统崩溃或数据丢失。灾备演练的核心目标是验证企业在突发情况下的应对能力，确保在最短时间内恢复业务运行，最大限度地减少损失。为保障业务连续性和数据安全性，公司亟需建立健全的灾备容灾体系，确保在突发事件发生时，能够快速检测故障并将业务切换至备用系统，将业务中断时间（RTO）和数据丢失量（RPO）控制在可接受范围内。1. 当前现状云上系统：无同城/异地灾备，依赖单地域单可用区或多可用区但未做应用级容灾云下系统：完全单点，无任何本地或异地备份环境数据库、中间件、静态资源均无跨地域备份与恢复机制缺乏灾备切换SOP与演练，RTO/RPO 未定义且实际趋近于无穷大2. 期望效果为云上、云下两套生产系统分别建设一套灾备环境，实现以下核心目标：目标项具体指标要求每套生产系统拥有独立灾备云上生产 → 云上灾备 （异地云/多云）/ 云下生产 → 云下灾备(异地数据中心)数据恢复点目标（RPO）核心数据库 ≤ 3 分钟（DTS数据通过/binlog复制）关键文件/对象存储 ≤ 5 小时业务恢复时间目标（RTO）关键系统 ≤ 5 分钟切换方式手动切换（DNS/负载均衡/全局流量调度）或可升级为半自动/自动切换演练频率每半年至少进行一次完整切换演练并出具报告回切能力具备支持故障修复后平滑回切至原生产环境最终效果： 无论云上生产还是云下生产发生整体故障，都能在可控时间内将其业务与数据快速切换/恢复到各自的灾备环境，对外服务不中断或极短中断，真正做到“两套系统各自有后路”。二、灾备方案1.主流方案排名方案名称RPO / RTO 典型值成本（相对生产）优点缺点1异地主备（Global Active-Passive）RPO ≤ 5分钟 / RTO 30秒～3分钟1.2～1.6倍改造最小、切换最可靠、演练最简单、100%过等保/监管/审计、成本最低RTO 无法做到秒级，极端情况下有几十秒到几分钟中断2同城双活 + 异地主备（2+1）RPO ≤ 1分钟 / RTO 10秒～1分钟1.8～2.5倍同城基本无感、异地仍有可靠兜底、监管最认可的“高级灾备”模式需要同城两套完整环境，建设和网络要求高，成本明显上升3异地双活/多活（单元化）RPO ≈ 0 / RTO ≤ 5秒2.5～4倍用户完全无感知、真正“业务不停”、极端场景下仍可扛区域级故障技术复杂度极高、业务改造巨大、容易脑裂/双写、演练风险极大、成本最高4三中心两主一备（2主1备）RPO ≤ 1分钟 / RTO ≤ 30秒2.2～3倍两个主中心互为备份+异地灾备，三重保险，监管评分最高建设和运维复杂度高，两个主中心要保持强一致，实际落地和演练都很难5同城双活 + 异地异步容灾RPO ≤ 15分钟 / RTO ≤ 4小时1.5～2倍过渡方案，同城做到高可用，异地成本低异地恢复时间长（几小时），属于“冷灾备”，不满足大部分监管要求2.方案选举2.1 主备灾备技术通常采用一主一备的架构，故障时需切换，用户可能感知等待时间。在这种架构下，主备之间无法同时在线，一旦系统出现问题，需要进行架构切换，用户可能会面临系统切换的等候时间。2.2 双活双活技术通过两套同时在线的架构互为灾备，实现无缝切换，确保业务连续性。双活技术采用两套同时在线的架构，互相作为对方的灾备。这种设计确保在主架构出现问题时，另一套双活架构能够无缝接管，实现无缝切换，用户无感知。三、灾备的难点及解决方案1.主备1.1 数据一致性问题2.双活
一、背景介绍近一年内对公司的 ELK 日志系统做过性能优化，也对 SkyWalking 使用的 ES 存储进行过性能优化，在此做一些总结。本篇主要是讲 ES 在 ELK 架构中作为日志存储时的性能优化方案。ELK 架构作为日志存储方案二、现状分析1. 版本及硬件配置JDK：JDK1.8\_171-b11 (64 位)ES集群：由3台16核32G的虚拟机部署 ES 集群，每个节点分配 20 G 堆内存ELK版本：6.3.0垃圾回收器：ES 默认指定的老年代（CMS）+ 新生代（ParNew）操作系统：CentOS Linux release 7.4.1708(Core)2. 性能问题随着接入 ELK 的应用越来越多，每日新增索引约 230 个，新增 document 约 3000 万到 5000 万。每日上午和下午是日志上传高峰期，在 Kibana 上查看日志，发现问题：(1) 日志会有 5-40 分钟的延迟(2) 有很多日志丢失，无法查到3. 问题分析3.1 日志延迟首先了解清楚：数据什么时候可以被查到？数据先是存放在 ES 的内存 buffer，然后执行 refresh 操作写入到操作系统的内存缓存 os cache，此后数据就可以被搜索到。所以，日志延迟可能是我们的数据积压在 buffer 中没有进入 os cache 。3.2 日志丢失查看日志发现很多 write 拒绝执行的情况write 线程池拒绝任务的日志.png从日志中可以看出 ES 的 write 线程池已经满负荷，执行任务的线程已经达到最大 16 个线程，而 200 容量的队列也已经放不下新的 task。查看线程池的情况也可以看出 write 线程池有很多写入的任务GET /_cat/thread_pool?v&h=host,name,type,size,active,largest,rejected,completed,queue,queue_sizewrite 线程池拒绝任务的情况.png所以我们需要优化 ES 的 write 的性能。4.解决思路4.1 分析场景ES 的优化分为很多方面，我们要根据使用场景考虑对 ES 的要求。根据个人实践经验，列举三种不同场景下的特点：SkyWalking：一般配套使用 ES 作为数据存储，存储链路追踪数据、指标数据等信息。ELK：一般用来存储系统日志，并进行分析，搜索，定位应用的问题。全文搜索的业务：业务中常用 ES 作为全文搜索引擎，例如在外卖应用中，ES 用来存储商家、美食的业务数据，用户在客户端可以根据关键字、地理位置等查询条件搜索商家、美食信息。这三类场景的特点如下： SkyWalkingELK全文搜索的业务并发写高并发写高并发写并发一般不高并发读并发低并发低并发高实时性要求5分钟以内30秒以内1分钟内数据完整性可容忍丢失少量数据可容忍丢失少量数据数据尽量100%不丢失关于实时性SkyWalking 在实际使用中，一般使用频率不太高，往往是发现应用的问题后，再去 SkyWalking 查历史链路追踪数据或指标数据，所以可以接受几分钟的延迟。ELK 不管是开发、测试等阶段，时常用来定位应用的问题，如果不能快速查询出数据，延迟太久，会耽误很多时间，大大降低工作效率；如果是查日志定位生产问题，那更是刻不容缓。全文搜索的业务中一般可以接受在1分钟内查看到最新数据，比如新商品上架一分钟后才看到，但尽量实时，在几秒内可以可看到。4.2 优化的方向可以从三方面进行优化：JVM 性能调优、ES 性能调优、控制数据来源三、ES性能优化可以从三方面进行优化：JVM 性能调优、ES 性能调优、控制数据来源1. JVM调优第一步是 JVM 调优。因为 ES 是依赖于 JVM 运行，没有合理的设置 JVM 参数，将浪费资源，甚至导致 ES 很容易 OOM 而崩溃。1.1 监控 JVM 运行情况（1）查看 GC 日志问题：Young GC 和 Full GC 都很频繁，特别是 Young GC 频率高，累积耗时非常多。(2) 使用 jstat 看下每秒的 GC 情况参数说明S0：幸存1区当前使用比例S1：幸存2区当前使用比例E：伊甸园区使用比例O：老年代使用比例M：元数据区使用比例CCS：压缩使用比例YGC：年轻代垃圾回收次数FGC：老年代垃圾回收次数FGCT：老年代垃圾回收消耗时间GCT：垃圾回收消耗总时间问题：从 jstat gc 中也可以看出，每秒的 eden 增长速度非常快，很快就满了。1.2 定位 Young GC 频繁的原因1.2.1 检查是否新生代的空间是否太小用下面几种方式都可查看新、老年代内存大小 (1) 使用 jstat -gc pid 查看 Eden 区、老年代空间大小 (2) 使用 jmap -heap pid 查看 Eden 区、老年代空间大小 (3) 查看 GC 日志中的 GC 明细其中 996800K 为新生代可用空间大小，即 Eden 区 +1 个 Survivor 区的空间大小，所以新生代总内存是996800K/0.9， 约1081M上面的几种方式都查询出，新生代总内存约1081M，即1G左右；老年代总内存为19864000K，约19G。新、老比例约1:19，出乎意料。1.2.1 新老年代空间比例为什么不是 JDK 默认的1:2【重点！】这真是一个容易踩坑的地方。如果没有显示设置新生代大小，JVM 在使用 CMS 收集器时会自动调参，新生代的大小在没有设置的情况下是通过计算得出的，其大小可能与 NewRatio 的默认配置没什么关系而与 ParallelGCThreads 的配置有一定的关系。参考文末链接：CMS GC 默认新生代是多大？所以：新生代大小有不确定性，最好配置 JVM 参数 -XX:NewSize、-XX:MaxNewSize 或者 -xmn ，免得遇到一些奇怪的 GC，让人措手不及。1.3 上面现象造成的影响新生代过小，老年代过大的影响新生代过小: (1) 会导致新生代 Eden 区很快用完，而触发 Young GC，Young GC 的过程中会 STW(Stop The World)，也就是所有工作线程停止，只有 GC 的线程在进行垃圾回收，这会导致 ES 短时间停顿。频繁的 Young GC，积少成多，对系统性能影响较大。(2) 大部分对象很快进入老年代，老年代很容易用完而触发 Full GC。老年代过大：会导致 Full GC 的执行时间过长，Full GC 虽然有并行处理的步骤，但是还是比 Young GC 的 STW 时间更久，而 GC 导致的停顿时间在几十毫秒到几秒内，很影响 ES 的性能，同时也会导致请求 ES 服务端的客户端在一定时间内没有响应而发生 timeout 异常，导致请求失败。1.4 JVM优化1.4.1 配置堆内存空间大小32G 的内存，分配 20G 给堆内存是不妥当的，所以调整为总内存的50%，即16G。修改 elasticsearch 的 jvm.options 文件-Xms16g -Xmx16g设置要求:Xms 与 Xmx 大小相同。在 jvm 的参数中 -Xms 和 -Xmx 设置的不一致，在初始化时只会初始 -Xms 大小的空间存储信息，每当空间不够用时再向操作系统申请，这样的话必然要进行一次 GC，GC会带来 STW。而剩余空间很多时，会触发缩容。再次不够用时再扩容，如此反复，这些过程会影响系统性能。同理在 MetaSpace 区也有类似的问题。jvm 建议不要超过 32G，否则 jvm 会禁用内存对象指针压缩技术，造成内存浪费Xmx 和 Xms 不要超过物理 RAM 的50%。参考文末：官方堆内存设置的建议Xmx 和 Xms 不要超过物理内存的50%。Elasticsearch 需要内存用于JVM堆以外的其他用途，为此留出空间非常重要。例如，Elasticsearch 使用堆外缓冲区进行有效的网络通信，依靠操作系统的文件系统缓存来高效地访问文件，而 JVM 本身也需要一些内存。1.4.2 配置堆内存新生代空间大小因为指定新生代空间大小，导致 JVM 自动调参只分配了 1G 内存给新生代。修改 elasticsearch 的 jvm.options 文件，加上-XX:NewSize=8G -XX:MaxNewSize=8G老年代则自动分配 16G-8G=8G 内存，新生代老年代的比例为 1:1。修改后每次 Young GC 频率更低，且每次 GC 后只有少数数据会进入老年代。2.3 使用G1垃圾回收器(未实践)G1垃圾回收器让系统使用者来设定垃圾回收堆系统的影响，然后把内存拆分为大量的小 Region，追踪每个 Region 中可以回收的对象大小和回收完成的预计花费的时间， 最后在垃圾回收的时候，尽量把垃圾回收对系统造成的影响控制在我们指定的时间范围内，同时在有限的时间内尽量回收更多的垃圾对象。G1垃圾回收器一般在大数量、大内存的情况下有更好的性能。ES默认使用的垃圾回收器是：老年代（CMS）+ 新生代（ParNew）。如果是JDK1.9，ES 默认使用 G1 垃圾回收器。因为使用的是 JDK1.8，所以并未切换垃圾回收器。后续如果再有性能问题再切换G1垃圾回收器，测试是否有更好的性能。1.5 优化的效果1.5.1 新生代使用内存的增长率更低优化前优化前.png每秒打印一次 GC 数据。可以看出，年轻代增长速度很快，几秒钟年轻代就满了，导致 Young GC 触发很频繁，几秒钟就会触发一次。而每次 Young GC 很大可能有存活对象进入老年代，而且，存活对象多的时候(看上图中第一个红框中的old gc数据)，有(51.44-51.08)/100 * 19000M = 约68M。每次进入老年代的对象较多，加上频繁的 Young GC，会导致新老年代的分代模式失去了作用，相当于老年代取代了新生代来存放近期内生成的对象。当老年代满了，触发 Full GC，存活的对象也会很多，因为这些对象很可能还是近期加入的，还存活着，所以一次 Full GC 回收对象不多。而这会恶性循环，老年代很快又满了，又 Full GC，又残留一大部分存活的，又很容易满了，所以导致一直频繁 Full GC。优化后优化后.png每秒打印一次 GC 数据。可以看出，新生代增长速度慢了许多，至少要 60 秒才会满，如上图红框中数据，进入老年代的对象约(15.68-15.60)/100 * 10000 = 8M，非常的少。所以要很久才会触发一次 Full GC 。而且等到 Full GC 时，老年代里很多对象都是存活了很久的，一般都是不会被引用，所以很大一部分会被回收掉，留一个比较干净的老年代空间，可以继续放很多对象。1.5.2 新生代和老年代 GC 频率更低ES 启动后，运行14个小时优化前优化前.pngYoung GC 每次的时间是不长的，从上面监控数据中可以看出每次GC时长 1467.995/27276 约等于 0.05 秒。那一秒钟有多少时间是在处理 Young GC ?计算公式：1467 秒/( 60 秒× 60 分 14 小时）= 约 0.028 秒，也就是 100 秒中就有 2.8 秒在Young GC，也就是有 2.8S 的停顿，这对性能还是有很大消耗的。同时也可以算出多久一次 Young GC， 方程是：60秒×60分*14小时/ 27276次 = 1次/X秒，计算得出X = 0.54，也就是 0.54 秒就会有一次 Young GC，可见 Young GC 频率非常频繁。优化后优化后效果.pngYoung GC 次数只有修改前的十分之一，Young GC 时间也是约八分之一。Full GC 的次数也是只有原来的八分之一，GC 时间大约是四分之一。GC 对系统的影响大大降低，性能已经得到很大的提升。2.ES 调优上面已经分析过 ES 作为日志存储时的特性是：高并发写、读少、接受 30 秒内的延时、可容忍部分日志数据丢失。下面我们针对这些特性对ES进行调优。2.1 优化 ES 索引设置2.2.1 ES 写数据底层原理ES写入数据的原理.pngrefresh ES 接收数据请求时先存入 ES 的内存中，默认每隔一秒会从内存 buffer 中将数据写入操作系统缓存 os cache，这个过程叫做 refresh；到了 os cache 数据就能被搜索到（所以我们才说 ES 是近实时的，因为 1 s 的延迟后执行 refresh 便可让数据被搜索到）fsync translog 会每隔 5 秒或者在一个变更请求完成之后执行一次 fsync 操作，将 translog 从缓存刷入磁盘，这个操作比较耗时，如果对数据一致性要求不是很高时建议将索引改为异步，如果节点宕机时会有5秒数据丢失;flush ES 默认每隔30分钟会将 os cache 中的数据刷入磁盘同时清空 translog 日志文件，这个过程叫做 flush。mergeES 的一个 index 由多个 shard 组成，而一个 shard 其实就是一个 Lucene 的 index ，它又由多个 segment 组成，且 Lucene 会不断地把一些小的 segment 合并成一个大的 segment ，这个过程被称为段merge（参考文末链接）。执行索引操作时，ES 会先生成小的segment，ES 有离线的逻辑对小的 segment 进行合并，优化查询性能。但是合并过程中会消耗较多磁盘 IO，会影响查询性能。2.2.2 优化方向2.2.2.1 优化 fsync为了保证不丢失数据，就要保护 translog 文件的安全:Elasticsearch 2.0 之后, 每次写请求(如 index 、delete、update、bulk 等)完成时, 都会触发fsync将 translog 中的 segment 刷到磁盘, 然后才会返回200 OK的响应;或者: 默认每隔5s就将 translog 中的数据通过fsync强制刷新到磁盘.该方式提高数据安全性的同时， 降低了一点性能.==> 频繁地执行 fsync 操作, 可能会产生阻塞导致部分操作耗时较久. 如果允许部分数据丢失, 可设置异步刷新 translog 来提高效率，还有降低 flush 的阀值，优化如下："index.translog.durability": "async", "index.translog.flush_threshold_size":"1024mb", "index.translog.sync_interval": "120s"2.2.2.2 优化 refresh写入 Lucene 的数据，并不是实时可搜索的，ES 必须通过 refresh 的过程把内存中的数据转换成 Lucene 的完整 segment 后，才可以被搜索。默认 1秒后，写入的数据可以很快被查询到，但势必会产生大量的 segment，检索性能会受到影响。所以，加大时长可以降低系统开销。对于日志搜索来说，实时性要求不是那么高，设置为 5 秒或者 10s；对于 SkyWalking，实时性要求更低一些，我们可以设置为 30s。设置如下："index.refresh_interval":"5s"2.2.2.3 优化 mergeindex.merge.scheduler.max\_thread\_count 控制并发的 merge 线程数，如果存储是并发性能较好的 SSD，可以用系统默认的 max(1, min(4, availableProcessors / 2))，当节点配置的 cpu 核数较高时，merge 占用的资源可能会偏高，影响集群的性能，普通磁盘的话设为1，发生磁盘 IO 堵塞。设置max\_thread\_count 后，会有 max\_thread\_count + 2 个线程同时进行磁盘操作，也就是设置为 1 允许3个线程。设置如下："index.merge.scheduler.max_thread_count":"1"2.2.2 优化设置2.2.2.1 对现有索引做索引设置# 需要先 close 索引,然后再执行,最后成功之后再打开 # 关闭索引 curl -XPOST 'http://localhost:9200/_all/_close' # 修改索引设置 curl -XPUT -H "Content-Type:application/json" 'http://localhost:9200/_all/_settings?preserve_existing=true' -d '{"index.merge.scheduler.max_thread_count" : "1","index.refresh_interval" : "10s","index.translog.durability" : "async","index.translog.flush_threshold_size":"1024mb","index.translog.sync_interval" : "120s"}' # 打开索引 curl -XPOST 'http://localhost:9200/_all/_open'该方式可对已经生成的索引做修改，但是对于后续新建的索引不生效，所以我们可以制作 ES 模板，新建的索引按模板创建索引。2.2.2.2 制作索引模板# 制作模板 大部分索引都是业务应用的日志相关的索引，且索引名称是 202* 这种带着日期的格式 PUT _template/business_log { "index_patterns": ["*202*.*.*"], "settings": { "index.merge.scheduler.max_thread_count" : "1","index.refresh_interval" : "5s","index.translog.durability" : "async","index.translog.flush_threshold_size":"1024mb","index.translog.sync_interval" : "120s"} } # 查询模板是否创建成功 GET _template/business_log因为我们的业务日志是按天维度创建索引，索引名称示例：user-service-prod-2020.12.12，所以用通配符**202.*.**匹配对应要创建的业务日志索引。2.2 优化线程池配置前文已经提到过，write 线程池满负荷，导致拒绝任务，而有的数据无法写入。而经过上面的优化后，拒绝的情况少了很多，但是还是有拒绝任务的情况。所以我们还需要优化 write 线程池。从 prometheus 监控中可以看到线程池的情况：为了更直观看到 ES 线程池的运行情况，我们安装了 elasticsearch\_exporter 收集 ES 的指标数据到 prometheus，再通过 grafana 进行查看。经过上面的各种优化，拒绝的数据量少了很多，但是还是存在拒绝的情况，如下图：write 线程池如何设置：参考文末链接：ElasticSearch线程池writeFor single-document index/delete/update and bulk requests. Thread pool type is fixed with a size of # of available processors, queue\_size of 200. The maximum size for this pool is 1 + # of available processors.write 线程池采用 fixed 类型的线程池，也就是核心线程数与最大线程数值相同。线程数默认等于 cpu 核数，可设置的最大值只能是 cpu 核数加 1，也就是 16 核 CPU，能设置的线程数最大值为 17。优化的方案：线程数改为 17，也就是 cpu 总核数加 1队列容量加大。队列在此时的作用是消峰。不过队列容量加大本身不会提升处理速度，只是起到缓冲作用。此外，队列容量也不能太大，否则积压很多任务时会占用过多堆内存。config/elasticsearch.yml文件增加配置# 线程数设置 thread_pool: write: # 线程数默认等于cpu核数，即16 size: 17 # 因为任务多时存在任务拒绝的情况，所以加大队列大小，可以在间歇性任务量陡增的情况下，缓存任务在队列，等高峰过去逐步消费完。 queue_size: 10000优化后效果：可以看到，已经没有拒绝的情况，这样也就是解决了日志丢失的问题。2.３ 锁定内存，不让 JVM 使用 SwapSwap 交换分区:当系统的物理内存不够用的时候，就需要将物理内存中的一部分空间释放出来，以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序，**这些被释放的空间被临时保存到 Swap 中，等到那些程序要运行时，再从 Swap 中恢复保存的数据到内存中。**这样，系统总是在物理内存不够时，才进行 Swap 交换。参考文末链接：ElasticSearch官方解释为什么要禁用交换内存Swap 交换分区对性能和节点稳定性非常不利，一定要禁用。它会导致垃圾回收持续几分钟而不是几毫秒，并会导致节点响应缓慢，甚至与集群断开连接。有三种方式可以实现 ES 不使用 Swap 分区2.3.1 Linux 系统中的关闭 Swap (临时有效)执行命令sudo swapoff -a可以临时禁用 Swap 内存，但是操作系统重启后失效2.3.2 Linux 系统中的尽可能减少 Swap 的使用(永久有效)执行下列命令echo "vm.swappiness = 1">> /etc/sysctl.conf正常情况下不会使用 Swap，除非紧急情况下才会 Swap。2.3.3 启用 bootstrap.memory\_lockconfig/elasticsearch.yml 文件增加配置#锁定内存，不让 JVM 写入 Swap，避免降低 ES 的性能 bootstrap.memory_lock: true2.4 减少分片数、副本数分片索引的大小取决于分片与段的大小，分片过小，可能导致段过小，进而导致开销增加；分片过大可能导致分片频繁 Merge，产生大量 IO 操作，影响写入性能。因为我们每个索引的大小在 15G 以下，而默认是 5 个分片，没有必要这么多，所以调整为 3 个。"index.number_of_shards": "3"分片的设置我们也可以配置在索引模板。副本数减少集群副本分片数，过多副本会导致 ES 内部写扩大。副本数默认为 1，如果某索引所在的 1 个节点宕机，拥有副本的另一台机器拥有索引备份数据，可以让索引数据正常使用。但是数据写入副本会影响写入性能。对于日志数据，有 1 个副本即可。对于大数据量的索引，可以设置副本数为0，减少对性能的影响。"index.number_of_replicas": "1"分片的设置我们也可以配置在索引模板。3.控制数据来源3.1 应用按规范打印日志有的应用 1 天生成 10G 日志，而一般的应用只有几百到 1G。一天生成 10G 日志一般是因为部分应用日志使用不当，很多大数量的日志可以不打，比如大数据量的列表查询接口、报表数据、debug 级别日志等数据是不用上传到日志服务器，这些即影响日志存储的性能，更影响应用自身性能。四、ES性能优化后的效果优化后的两周内 ELK 性能良好，没有使用上的问题：ES 数据不再丢失数据延时在 10 秒之内，一般在 5 秒可以查出每个 ES 节点负载比较稳定，CPU 和内存使用率都不会过高，如下图
AI摘要 本文详细介绍了ELK大数据日志分析平台的构建和优化方法。平台由Filebeat、Kafka、Logstash、Elasticsearch和Kibana五个组件构成，各组件负责不同的功能，如日志收集、消息队列、数据处理、数据存储和可视化。文章还提供了各组件的配置示例和调优建议，以确保平台的高效运行。 此内容根据文章生成，仅用于文章内容的解释与总结 Filebeat+Kafka+Logstash+Elasticsearch构建日志分析系统在现代分布式系统中，日志管理是至关重要的一环。日志不仅用于排查问题，还可以用于监控系统性能、分析用户行为等。为了高效地收集、处理和可视化日志，企业通常采用 Filebeat + Kafka + Logstash + Elasticsearch + Kibana 的组合架构。本文将深入探讨这一架构的工作原理、优势、适用场景以及实现步骤。组件介绍2.1 Filebeat• 功能：轻量级的日志收集工具，专为日志文件设计。• 特点：低资源消耗、支持多种输入输出、易于部署。• 适用场景：收集应用日志、系统日志等。2.2 Kafka• 功能：分布式消息队列，用于解耦数据生产和消费。• 特点：高吞吐量、低延迟、持久化存储、可扩展。• 适用场景：缓冲日志数据、解耦日志收集和数据处理。2.3 Logstash• 功能：数据管道工具，用于收集、过滤、转换和输出数据。• 特点：强大的插件生态系统、支持复杂的数据处理。• 适用场景：日志解析、数据格式化、数据过滤。2.4 Elasticsearch• 功能：分布式搜索引擎，用于存储和索引数据。• 特点：实时搜索、高扩展性、支持全文搜索。• 适用场景：日志存储、实时数据分析。2.5 Kibana• 功能：数据可视化工具，用于查询和展示 Elasticsearch 中的数据。• 特点：丰富的图表类型、交互式仪表盘、易于使用。• 适用场景：日志可视化、监控仪表盘。流程图组件交互详解Filebeat 收集日志并发送到 KafkaFilebeat 是一个轻量级的日志收集器，安装在各个日志源节点（如 Web 服务器、应用程序、容器等）上。Filebeat 的主要功能是读取日志文件并将其发送到 Logstash 或 Kafka。配置 Filebeat 发送到 Kafka： 在 Filebeat 配置文件 filebeat.yml 中，你可以设置 Kafka 输出配置，将日志发送到 Kafka 集群中的特定主题。Filebeat 配置示例：filebeat.inputs: - type: log enabled: true paths: - /var/log/*.log output.kafka: hosts: ["kafka_host:9092"] topic: "logs_topic" codec: jsonKafka 作为消息队列Kafka 在日志收集过程中充当消息队列的角色，将日志数据从 Filebeat 转发到 Logstash。Kafka 作为一个可靠的缓冲层，能够缓解高负载、网络延迟等问题。Kafka 接收 Filebeat 发送的日志，并将其存储在一个或多个分区（broker）中。它支持高吞吐量、持久化和容错，保证日志数据不会丢失。logstash 从kafka消费数据并处理logstash可以配置为kafka消费者，定期从kafka中读取日志数据。logstash可以对数据进行过滤、解析、转换等操作，处理后的数据会被转发到elasticsearch配置logstash从kafka消费数据：在logstash配置文件中，使用kafka input plugin来从kafka中读取数据，并使用elasticsearh output plugin将数据发送到elasticsearchlogstash 配置实例input { kafka { bootstrap_servers => "kafka_host:9092" topics => ["logs_topic"] group_id => "logstash-group" } } filter { # 可在这里进行数据过滤、解析和转换 json { source => "message" } } output { elasticsearch { hosts => ["http://elasticsearch_host:9200"] index => "logs-index-%{+YYYY.MM.dd}" } }kafka 输入插件从kafka中读取数据json过滤将日志数据解析为json格式（假设日志数据是json格式的）elasticsearch输出插件将处理后的数据发送到elasticsearchelasticsearch 存储和索引日志elasticsearch将logstash发送的数据存储为索引文档，并提供快速搜索和聚合功能。可以通过kibana或其他工具查询elasticsearch中的数据elasticseach会对日志数据进行倒排索引，使得可以非常高效的进行全文搜索和数据分析数据会根据指定的索引模版或者动态索引进行映射，并根据需求进行实时分析和可视化示意图+----------+ Kafka +----------+ Elasticsearch | Filebeat| --------> | Logstash | ------------> | Elasticsearch | +----------+ +----------+ +---------------+各组件的优点和作用filebeat： 轻量级、资源占用低的日志采集工具，支持各种日志源的监控（文件、容器等）。filebeat的优点是快速启动，低延迟，能够高效的将日志发送到kafkakafkka： 作为中间层的消息队列，kafka提供了数据流的缓冲、持久化、流量调节和高可用性。能够保障日志数据的可靠传输，即使elasticsearch和logstash暂时不可用，数据也不会丢失。logstash: 作为数据处理引擎，logstash能够处理复杂的日志解析、过滤、转换等任务。它还可以处理来自多种输入的数据流，支持多种输出（包括elasticsearch）elasticsearch：最终的存储和搜索引擎，支持日志数据的存储、索引和快速查询，支持复杂的聚合和可视化使用场景优点：解耦：通过kafka，filebeat和logstash之间的联系被解耦，增加了系统的灵活性。kafka的存在使得各个组件之间的依赖减少，提高了整体系统的可靠性高可用性和容错性：kafka和elasticsearch都具备高可用特性，可以处理大规模的数据流和高并发请求。kafka提供了数据持久化和复制，确保消息不会丢失灵活的流量调节：kafka可以在日志数据流量高峰时作为缓冲区，避免logstash和elasticsear被瞬间流量压垮实时处理：通过logstash和elasticsearch的集成，可以实现实时的日志分析和监控使用场景：大规模日志收集：由于适用分布式系统的日志收集中，kafka和filebeat可以高效的收集和转发日志数据，logstash负责日志处理，elasticsearch用于存储和分析日志实时监控和分析：通过elasticsearch提供的搜索和聚合功能，配合kibana，可以实现实时的日志分析、性能监控、故障排查等事件驱动架构：kafka作为事件队列，可以用于处理系统中产生的大量实时事件，数据可以流入elasticsearch进行分析总结通过将filebeat、kafka、logstash、elasticsearch结合使用，可以构建一个高效、可靠、可扩展的日志收集和分析系统。kafka在此架构中扮演着消息队列的角色，确保了数据传输的高吞吐量、可靠性和灵活性。logstash和elasticsearch提供了强大的数据处理、存储和分析能力调优建议logstash调优consumer\_threads（并发线程数）在logstash的kafka input插件中，consumer\_threads 是一个用于控制并发消费线程数量的参数。它影响logstash从kafka分区读取数据的效率和吞吐量基本概念1.作用：指定logstash从kafka消费数据时，使用的线程数量每个线程可以消费一个或多个kafka分区2.默认值：如果未设置，consumer\_threads默认值为1，即使用单线程消费3.调优点：增加consumer\_threads数量，可以提高数据消费的并发能力配置方式在losgstash.conf文件中配置kafka input插件时，可以添加consumer\_threads参数，例如：input { kafka { bootstrap_servers => "broker1:9092,broker2:9092" topics => ["your-topic"] group_id => "logstash-consumer-group" consumer_threads => 4 } } output { elasticsearch { hosts => ["http://es-host:9200"] index => "your-index-%{+YYYY.MM.dd}" } }注意事项1.线程数与kafka分区数的关系kafka的分区是消费并发度的上线每个线程最多只能处理一个分区如果consumer\_threads的值大于分区数，多余的线程会空闲实例topic有6个分区如果consumer\_threads设置为8，只有6个线程会实际工作，剩余2个线程闲置2.消费者组的分区分配如果有多个logstash实例（属于同一个consumer group），kafka会将分区分配到不同的logstash节点每个logstash节点使用自己的consumer\_threads参数处理分配它的区位3.线程数的合理设置避免设置过多的线程线程过多可能会导致上下文切换频繁，反而降低性能建议设置的线程数与分区数接近，但不要超过分区总数配置jvm内存logstash默认的jvm内存不能处理高吞吐场景建议：修改jvm.options 文件，增加堆内存-Xms4g -Xmx4g启用多管道多管道可以进行处理不通类型的数据建议：在piplines.yml配置多个管道，根据不同数据源或逻辑划分管道，提升吞吐量。- pipeline.id: pipeline1 path.config: "/etc/logstash/conf.d/pipeline1.conf" - pipeline.id: pipeline2 path.config: "/etc/logstash/conf.d/pipeline2.conf"调整批处理大小批处理大小决定了logstash每次从kafka消费的记录数建议：在kafka input配置中增加fetch\_min\_bytes 和 consumer\_threads。在output（如 Elasticsearch）中，增加批处理大小：flush\_size：定义每次批量发送的事件数量（默认值为 5000），批量大小太小可能导致性能下降，太大可能导致内存问题workers：定义用于并发处理的工作线程数（默认值为 1），适当增加可以提高吞吐量，但不要超过 Elasticsearch 节点的处理能力output { elasticsearch { hosts => ["http://es-host:9200"] index => "your-index" flush_size => 5000 workers => 4 } }配置持久队列当 Logstash 出现高负载时，内存队列可能会丢失数据。建议：启用持久队列，避免数据丢失queue.type: persisted queue.max_bytes: 2gbqueue.type: persisted启用 Logstash 的持久化队列。默认情况下，Logstash 使用的是内存队列（memory），所有数据缓存在内存中。而 persisted 队列会将数据缓存在磁盘上，避免因 Logstash 重启或崩溃导致数据丢失。queue.max\_bytes: 2gb设置持久化队列的最大容量为 2GB。当队列已满时，Logstash 会停止接收新的数据，直到队列有足够的空间。可以根据磁盘大小和流量需求调整此值。elasticsearch 调优1. index.number\_of\_shards作用：设置索引的分片数量。分片决定了数据如何分布在节点上。建议值：根据数据量和集群规模合理设置，可以设置为3-5个后果：分片数过少：单个分片过大，可能会导致查询和写入性能下降分片数过多：会增加开销，可能浪费资源并影响集群的稳定性2. index.number\_of\_replicas作用：设置每个索引的副本数量，用于数据冗余和提高查询性能建议值：建议设置为1到2个副本后果：副本数过少，节点故障时可能丢失数据，影响系统的高可用性副本数过多，会浪费存储空间并增加数据同步的负担3. index.refresh\_interval作用：控制索引的刷新频率，决定数据文档从内存刷新到磁盘的间隔时间建议值：可以适当增加刷新时间，设置为5秒或者10秒后果：刷新时间过段，频繁的刷新会增加io开销，影响写入性能刷新时间过长，查询时无法看到最新的数据，影响实时性4. indices.memory.index\_buffer\_size作用：设置索引时内存缓冲区大小，影响写入性能建议值：通常设置为物理内存的10%到15%后果：设置过小会导致频繁的磁盘io，影响写入性能如果过大会导致内存耗尽，影响其他操作的稳定性
OpenVPN原理及部署使用1. 简介 VPN技术通过密钥交换、封装、认证、加密手段在公共网络上建立起私密的隧道，保障传输数据的完整性、私密性和有效性。OpenVPN是近年来新出现的开放源码项目，实现了SSL VPN的一种解决方案。2.应用场景有一些类似这样的情形或需求：1、开发:通过OpenVPN让开发人员连接网站,进行开发测试(在家的时候)2、运维:通过OpenVPN让运维连接内网服务器,或者是连接JUMPSERVER在家里想进入公司网站的管理界面，但管理系统限制了仅允许来自公司IP可以访问想连接到某个特别网站的特别网络应用，但本地的ISP或路由节点不允许您去连接它本地的ISP或路由节点可能会监听您的数据而您不想被它拦截，想找一种可以跳过它的办法,您想访问的网站和应用是基于明文传输，但您希望这个线路是私密的和可靠的。这时候就需要用到VPN来实现。3.OpenVPN服务端配置3.1 环境准备serverIPopenvpn-server公网:100.100.1.1 内网:10.100.240.3client能访问Internet3.2 证书准备流程1) 安装openvpn服务端、证书创建工具#openvpn 服务端、证书工具安装 [root@openvpn-server ~]# yum install -y openvpn easy-rsa #证书工具目录 [root@openvpn-server ~]# rpm -ql easy-rsa /usr/share/easy-rsa/3.0.8/easyrsa ...2) 服务器充当权威机构，创建ca证书#路径太深，新建目录 [root@openvpn-server ~]# mkdir -p /opt/easy-rsa [root@openvpn-server ~]# cp -a /usr/share/easy-rsa/3.0.8/* /opt/easy-rsa/ [root@openvpn-server ~]# cp /usr/share/doc/easy-rsa-3.0.8/vars.example /opt/easy-rsa/vars #修改vars文件，配置默认参数值 [root@openvpn-server ~]# cat > /opt/easy-rsa/vars if [ -z "$EASYRSA_CALLER" ]; then echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2 echo "This is no longer necessary and is disallowed. See the section called" >&2 echo "'How to use this file' near the top comments for more details." >&2 return 1 fi set_var EASYRSA_DN "cn_only" set_var EASYRSA_REQ_COUNTRY "CN" # 国家 set_var EASYRSA_REQ_PROVINCE "Beijing" # 省 set_var EASYRSA_REQ_CITY "ShangHai" # 城市 set_var EASYRSA_REQ_ORG "zxc.com" # 组织 set_var EASYRSA_REQ_EMAIL "123456@qq.com" # 邮箱 set_var EASYRSA_NS_SUPPORT "yes" EOF #配置完var后 检查 权威机构信息 [root@openvpn-server easy-rsa]# tree . |-- easyrsa |-- openssl-easyrsa.cnf |-- vars #var ca权威机构信息 `-- x509-types |-- ca |-- client |-- code-signing |-- COMMON |-- email |-- kdc |-- server `-- serverClient 1 directory, 11 files #初始化，会在当前目录创建PKI目录，用于存储证书 [root@openvpn-server easy-rsa]# ./easyrsa init-pki Note: using Easy-RSA configuration from: /opt/easy-rsa/vars #正在使用来自于vars init-pki complete; you may now create a CA or requests. #初始化完成 显示你可以尝试创建 CA证书 Your newly created PKI dir is: /opt/easy-rsa/pki #初始化后的目录在 pki下面 #创建根证书，会提示设置密码，用于ca对之后生成的server和client证书签名时使用，其他可默认 [root@openvpn-server easy-rsa]# ./easyrsa build-ca Note: using Easy-RSA configuration from: /opt/easy-rsa/vars Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Enter New CA Key Passphrase: #密码1234 Re-Enter New CA Key Passphrase: #密码1234 Generating RSA private key, 2048 bit long modulus ..............+++ ...............................................+++ e is 65537 (0x10001) You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Common Name (eg: your user, host, or server name) [Easy-RSA CA]:zxc.com #域名即可 CA creation complete and you may now import and sign cert requests. Your new CA certificate file for publishing is at: /opt/easy-rsa/pki/ca.crt #查看ca证书和私钥 [root@openvpn-server easy-rsa]# tree pki/ pki/ |-- ca.crt #ca证书 |-- certs_by_serial |-- index.txt |-- index.txt.attr |-- issued |-- openssl-easyrsa.cnf |-- private | `-- ca.key #ca私钥 |-- renewed | |-- certs_by_serial | |-- private_by_serial | `-- reqs_by_serial |-- reqs |-- revoked | |-- certs_by_serial | |-- private_by_serial | `-- reqs_by_serial |-- safessl-easyrsa.cnf `-- serial 12 directories, 7 files 3) 创建server端证书#创建server端证书和私钥文件 # 创建请求文件及服务端私钥，gen-req请求文件、server可以自定义、nopass表示不加密私钥文件，其他可默认 [root@openvpn-server easy-rsa]# ./easyrsa gen-req server nopass Note: using Easy-RSA configuration from: /opt/easy-rsa/vars Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 Generating a 2048 bit RSA private key ....................................................+++ ......................................+++ writing new private key to '/opt/easy-rsa/pki/easy-rsa-5343.MYL42w/tmp.4slsGH' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Common Name (eg: your user, host, or server name) [server]: Keypair and certificate request completed. Your files are: req: /opt/easy-rsa/pki/reqs/server.req #server.req 用于创建证书(证书资料) key: /opt/easy-rsa/pki/private/server.key #server私钥文件 #给server端证书签名，sign签名、第一个server是服务器类型、第二个server是需要签名的证书名称 #首先是对一些信息的确认，输入yes，然后输入创建ca根证书时设置的密码 [root@openvpn-server easy-rsa]# ./easyrsa sign server server Note: using Easy-RSA configuration from: /opt/easy-rsa/vars Using SSL: openssl OpenSSL 1.0.2k-fips 26 Jan 2017 You are about to sign the following certificate. Please check over the details shown below for accuracy. Note that this request has not been cryptographically verified. Please be sure it came from a trusted source or that you have verified the request checksum with the sender. Request subject, to be signed as a server certificate for 825 days: subject= commonName = server Type the word 'yes' to continue, or any other input to abort. Confirm request details: yes #输入yes Using configuration from /opt/easy-rsa/pki/easy-rsa-7938.RZEfrP/tmp.NCVg6D Enter pass phrase for /opt/easy-rsa/pki/private/ca.key: #输入创建ca时设置的密码 Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows commonName :ASN.1 12:'server' Certificate is to be certified until May 7 15:26:38 2026 GMT (825 days) Write out database with 1 new entries Data Base Updated Certificate created at: /opt/easy-rsa/pki/issued/server.crt #生成服务端证书 #验证是否创建了server的证书 [root@openvpn-server easy-rsa]# tree pki/ pki/ |-- ca.crt #ca证书 |-- certs_by_serial | `-- 63D708FF122452B7558651207A1AF01B.pem |-- index.txt |-- index.txt.attr |-- index.txt.attr.old |-- index.txt.old |-- issued | `-- server.crt #server证书 |-- openssl-easyrsa.cnf |-- private | |-- ca.key | `-- server.key #server私钥 |-- renewed | |-- certs_by_serial | |-- private_by_serial | `-- reqs_by_serial |-- reqs | `-- server.req |-- revoked | |-- certs_by_serial | |-- private_by_serial | `-- reqs_by_serial |-- safessl-easyrsa.cnf |-- serial `-- serial.old 12 directories, 14 files4) 创建client端证书#创建client端证书和私钥文件 # 创建请求文件及服务端私钥，gen-req请求文件、client可以自定义、nopass表示不加密私钥文件，其他可默认 [root@openvpn-server easy-rsa]# ./easyrsa gen-req client nopass Common Name (eg: your user, host, or server name) [client]: #直接回车 Keypair and certificate request completed. Your files are: req: /opt/easy-rsa/pki/reqs/client.req key: /opt/easy-rsa/pki/private/client.key #给client端证书签名，sign签名、第一个client是服务器类型、第二个client是需要签名的证书名称 #给client端证书签名时，首先是对一些信息的确认，可以输入yes，然后创建ca根证书时设置的密码 [root@openvpn-server easy-rsa]# ./easyrsa sign client client ...... Type the word 'yes' to continue, or any other input to abort. Confirm request details: yes Using configuration from /opt/easy-rsa/pki/easy-rsa-32687.69KFgb/tmp.y1hokA Enter pass phrase for /opt/easy-rsa/pki/private/ca.key: ...... Certificate created at: /opt/easy-rsa/pki/issued/client.crt5) 创建dh-pem算法文件和目录汇总#创建Diffie-Hellman文件，秘钥交换时的Diffie-Hellman算法 [root@openvpn-server easy-rsa]# ./easyrsa gen-dh ....... DH parameters of size 2048 created at /opt/easy-rsa/pki/dh.pem #目录汇总 [root@openvpn-server easy-rsa]# tree . |-- easyrsa |-- openssl-easyrsa.cnf |-- pki | |-- ca.crt #ca证书 | |-- certs_by_serial | | |-- 58D4C3BEB5AA4931D7CE3D9D0119C3CF.pem | | `-- 63D708FF122452B7558651207A1AF01B.pem | |-- dh.pem #df算法文件 | |-- index.txt | |-- index.txt.attr | |-- index.txt.attr.old | |-- index.txt.old | |-- issued | | |-- client.crt #client证书 | | `-- server.crt #server证书 | |-- openssl-easyrsa.cnf | |-- private | | |-- ca.key | | |-- client.key #客户端私钥 | | `-- server.key #服务端私钥 | |-- renewed | | |-- certs_by_serial | | |-- private_by_serial | | `-- reqs_by_serial | |-- reqs | | |-- client.req | | `-- server.req | |-- revoked | | |-- certs_by_serial | | |-- private_by_serial | | `-- reqs_by_serial | |-- safessl-easyrsa.cnf | |-- serial | `-- serial.old |-- vars `-- x509-types |-- ca |-- client |-- code-signing |-- COMMON |-- email |-- kdc |-- server `-- serverClient6) 小结1、创建vars文件(伪装ca机构),创建ca证书 build-ca2、创建server证书和私钥 gen-req server nopass sign server server3、创建client证书和私钥 gen-req client nopass sign client client4、dh.pem 文件3.3 服务端配置文件#配置文件中 各种路径默认相对于 /etc/openvpn [root@openvpn-server ~]# tree -F /etc/openvpn/ /etc/openvpn/ |-- client/ `-- server/ 2 directories, 0 files #服务端配置文件 [root@openvpn-server ~]# vim /etc/opnvpn/server/server.conf [root@openvpn-server server]# cat server.conf port 1194 #端口 proto udp #协议 dev tun #采用路由隧道模式tun ca ca.crt #ca证书文件位置 /etc/openvpn /etc/opnevpn/server cert server/server.crt #服务端公钥名称 /etc/openvpn key server/server.key #服务端私钥名称 /etc/openvpn dh server/dh.pem #加密算法文件, 交换证书 校验算法 /etc/openvpn server 192.168.1.0 255.255.255.0 #给客户端分配地址池(ip地址范围)，注意：不能和VPN服务器内网网段有相同 push "route 10.100.240.0 255.255.255.0" #客户端连接后,推送给客户端的路由规则 #客户端想访问10.100.240.0/24网段联系openvpn服务端 #ifconfig-pool-persist ipp.txt #地址池记录文件位置 未来让openvpn 客户端固定ip地址使用的. keepalive 10 120 #存活时间，10秒ping一次,120 如未收到响应则视为断线 max-clients 100 #最多允许100个客户端连接 status /var/log/openvpn-status.log #日志记录位置openvpn状态 log /var/log/openvpn.log #openvpn日志记录位置 verb 3 #verbose 日志输出级别 数字越大越详细最多11(debug) client-to-client #客户端与客户端之间支持通信 persist-key #通过keepalive检测超时后，重新启动VPN，不重新读取keys，保留第一次使用的keys 对私钥进行缓存. persist-tun #检测超时后，重新启动VPN，一直保持tun是linkup的。否则网络会先linkdown然后再linkup duplicate-cn #客户端密钥(证书和私钥)是否可以重复 #复制证书及密钥 [root@openvpn-server server]# sudo vim server.conf [root@openvpn-server server]# cd /opt/easy-rsa/pki/ [root@openvpn-server pki]# cp ca.crt /etc/openvpn/ [root@openvpn-server pki]# cp dh.pem /etc/openvpn/server/ [root@openvpn-server pki]# cp issued/server.crt private/server.key /etc/openvpn/server/ #修改systemctl 配置,因为新建的文件夹通过systemctl启动openvpn时找不到路径 [root@openvpn-server server]# cat /usr/lib/systemd/system/openvpn@.service [Unit] Description=OpenVPN Robust And Highly Flexible Tunneling Application On %I After=network.target [Service] Type=notify PrivateTmp=true ExecStart=/usr/sbin/openvpn --cd /etc/openvpn/ --config %i/%i.conf #增加%i即可，只修改这一行 #%i/%i 就是运行systemctl start openvpn@server时，会把@后面的server传给%i，就会读到server/server.conf [Install] WantedBy=multi-user.target #重新加载 [root@openvpn-server server]# systemctl daemon-reload #启动和加入开机自启 [root@openvpn-server server]# systemctl enable openvpn@server [root@openvpn-server server]# systemctl start openvpn@server #检查进程与端口 [root@openvpn-server openvpn]# ss -lntup |grep 1194 udp UNCONN 0 0 *:1194 *:* users:(("openvpn",pid=31362,fd=5)) [root@openvpn-server openvpn]# ps -ef |grep openvpn root 960 1 0 Feb02 ? 00:00:00 /sbin/dhclient -q -lf /var/lib/dhclient/dhclient--eth0.lease -pf /var/run/dhclient-eth0.pid -H openvpn-server eth0 root 31362 1 0 03:05 ? 00:00:00 /usr/sbin/openvpn --cd /etc/openvpn/ --config server/server.conf root 31912 10767 0 03:07 pts/1 00:00:00 grep --color=auto openvpn [root@openvpn-server openvpn]# ip a s tun0 3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100 link/none inet 192.168.1.1 peer 192.168.1.2/32 scope global tun0 valid_lft forever preferred_lft forever inet6 fe80::6813:2c79:d971:d16e/64 scope link flags 800 valid_lft forever preferred_lft forever3.4 客户端配置文件1) windows#openvpn客户端下载：https://swupdate.openvpn.org/community/releases/OpenVPN-2.6.8-I001-amd64.msi #把需要的证书先放到client，方便下载 [root@openvpn-server openvpn]# cp /opt/easy-rsa/pki/private/client.key /opt/easy-rsa/pki/issued/client.crt /etc/openvpn/client/ #创建客户端配置文件 [root@openvpn-server openvpn]# cat /etc/openvpn/client/clinet.ovpn client #指定当前VPN是客户端 dev tun #使用tun隧道传输协议 proto udp #使用udp协议传输数据 remote 100.100.1.1 1194 #openvpn服务器IP地址（公网）、端口号 resolv-retry infinite #断线自动重新连接，在网络不稳定的情况下非常有用 nobind #不绑定本地特定的端口号 ca ca.crt #指定CA证书的文件路径 cert client.crt #指定当前客户端的证书文件路径 key client.key #指定当前客户端的私钥文件路径 verb 3 #指定日志文件的记录详细级别，可选0-9，等级越高日志内容越详细 persist-key #通过keepalive检测超时后，重新启动VPN，不重新读取keys，保留第一次使用的keys #安装完客户端 在C:\Program Files\OpenVPN\config\zxc.com\ ca.crt #ca证书 client.crt #客户端证书 client.key #客户端私钥 client.ovpn #客户端配置文件 类似于client.conf 有多个VPN，可以新建config\zxc1.com\目录2) OpenVPN客户端配置（linux端）root@openvpn-client ~]# cat /etc/openvpn/client/clinet.conf client #指定当前VPN是客户端 dev tun #使用tun隧道传输协议 proto udp #使用udp协议传输数据 remote 100.100.1.1 1194 #openvpn服务器IP地址（公网）、端口号 resolv-retry infinite #断线自动重新连接，在网络不稳定的情况下非常有用 nobind #不绑定本地特定的端口号 ca ca.crt #指定CA证书的文件路径 cert client/client.crt #指定当前客户端的证书文件路径 key client/client.key #指定当前客户端的私钥文件路径 verb 3 #指定日志文件的记录详细级别，可选0-9，等级越高日志内容越详细 #修改systemctl 配置 [root@openvpn-client ~]# cat /usr/lib/systemd/system/openvpn@.serv [Unit] Description=OpenVPN Robust And Highly Flexible Tunneling Application On %I After=network.target [Service] Type=notify PrivateTmp=true ExecStart=/usr/sbin/openvpn --cd /etc/openvpn/ --config %i/%i.conf #增加%i即可，只修改这一行 #%i/%i 就是运行systemctl start openvpn@client时，会把@后面的client传给%i，就会读到client/client.conf [Install] WantedBy=multi-user.target #重新加载 [root@openvpn-client ~]# systemctl daemon-reload #启动和加入开机自启 [root@openvpn-client ~]# systemctl enable openvpn@client [root@openvpn-client ~]# systemctl start openvpn@client #检查进程与端口 [root@openvpn-client ~]# ss -lntup |grep openvpn udp UNCONN 0 0 *:53781 *:* users:(("openvpn",pid=12553,fd=3)) [root@openvpn-client ~]# ip a 4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100 link/none inet 192.168.1.6 peer 192.168.1.5/32 scope global tun0 valid_lft forever preferred_lft forever inet6 fe80::b198:27bb:5967:356b/64 scope link flags 800 valid_lft forever preferred_lft forever 3）效果4、OpenVPN加密/认证考虑到万一证书被泄露或者丢失，外人直接就可以连接到内网，所以再加上 用户/密码 验证4.1 先配置服务端支持密码认证[root@openvpn-server ~]# cat /etc/openvpn/server/server.conf port 1194 #端口 proto udp #协议 dev tun #采用路由隧道模式tun ca ca.crt #ca证书文件位置 /etc/openvpn /etc/opnevpn/server cert server/server.crt #服务端公钥名称 /etc/openvpn key server/server.key #服务端私钥名称 /etc/openvpn dh server/dh.pem #加密算法文件, 交换证书 校验算法 /etc/openvpn server 192.168.1.0 255.255.255.0 #给客户端分配地址池(ip地址范围)，注意：不能和VPN服务器内网网段有相同 push "route 10.100.240.0 255.255.255.0" #客户端连接后,推送给客户端的路由规则 #客户端想访问10.100.240.0/24网段联系openvpn服务端 #ifconfig-pool-persist ipp.txt #地址池记录文件位置 未来让openvpn 客户端固定ip地址使用的. keepalive 10 120 #存活时间，10秒ping一次,120 如未收到响应则视为断线 max-clients 100 #最多允许100个客户端连接 status /var/log/openvpn-status.log #日志记录位置openvpn状态 log /var/log/openvpn.log #openvpn日志记录位置 verb 3 #verbose 日志输出级别 数字越大越详细最多11(debug) client-to-client #客户端与客户端之间支持通信 persist-key #通过keepalive检测超时后，重新启动VPN，不重新读取keys，保留第一次使用的keys 对私钥进行缓存. persist-tun #检测超时后，重新启动VPN，一直保持tun是linkup的。否则网络会先linkdown然后再linkup duplicate-cn #客户端密钥(证书和私钥)是否可以重复 #下面是要用户认证时才配置 script-security 3 #允许使用自定义脚本 auth-user-pass-verify /etc/openvpn/check.sh via-env #指定认证脚本 username-as-common-name #用户密码登陆方式验证4.2 编写用户验证 check.sh 脚本[root@openvpn-server ~]# cat /etc/openvpn/check.sh #!/bin/bash PASSFILE="/etc/openvpn/openvpnfile" #密码文件 用户名 密码明文 LOG_FILE="/var/log/openvpn-password.log" #用户登录情况的日志 TIME_STAMP=`date "+%Y-%m-%d %T"` if [ ! -r "${PASSFILE}" ]; then echo "${TIME_STAMP}: Could not open password file \"${PASSFILE}\" for reading." >> ${LOG_FILE} exit 1 fi CORRECT_PASSWORD=`awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${PASSFILE}` if [ "${CORRECT_PASSWORD}" = "" ]; then echo "${TIME_STAMP}: User does not exist: username=\"${username}\",password=\"${password}\"." >> ${LOG_FILE} exit 1 fi if [ "${password}" = "${CORRECT_PASSWORD}" ]; then echo "${TIME_STAMP}: Successful authentication: username=\"${username}\"." >> ${LOG_FILE} exit 0 fi echo "${TIME_STAMP}: Incorrect password: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE} exit 14.3 脚本授权[root@openvpn-server ~]# chmod 700 /etc/openvpn/check.sh4.4 添加用户[root@openvpn-server ~]# cat /etc/openvpn/openvpnfile zxc 1234564.5 重启openvpn服务[root@openvpn-server ~]# systemctl restart openvpn@server4.6 客户端只需要在配置文件加个参数在client.ovpn或者client.conf文件最后一行添加 auth-user-pass4.7 最终效果5、OpenVPN连接内网其它服务器#先在openvpn服务端开启内核转发。 [root@openvpn-server ~]# echo 'net.ipv4.ip_forward = 1' >>/etc/sysctl.conf [root@openvpn-server ~]# sysctl -p net.ipv4.ip_forward = 1 #客户端尝试ping其他内网服务器，会显示请求超时。 [c:\~]$ ping 10.100.240.14 -t 正在 Ping 10.100.240.14 具有 32 字节的数据: 请求超时。 请求超时。 请求超时。 请求超时。 请求超时。 请求超时。 #服务端抓包，发现只有请求的包，没有响应的包。 [root@openvpn-server ~]# tcpdump -i eth0 host 10.100.240.14 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 16:15:48.527017 IP 192.168.1.6 > 10.100.240.14: ICMP echo request, id 1, seq 738, length 40 16:15:53.087817 IP 192.168.1.6 > 10.100.240.14: ICMP echo request, id 1, seq 739, length 40 16:15:58.101622 IP 192.168.1.6 > 10.100.240.14: ICMP echo request, id 1, seq 740, length 40 16:16:03.083202 IP 192.168.1.6 > 10.100.240.14: ICMP echo request, id 1, seq 741, length 40 #一般都是10.100.240.14上没有192.168.1.0的路由，在10.100.240.14上添加路由 [root@localhost ~]# route add -net 192.168.1.0/24 gw 10.100.240.3 #在重新连vpn，再ping 10.100.240.14 会通 [c:\~]$ ping 10.100.240.14 -t 正在 Ping 10.100.240.14 具有 32 字节的数据: 来自 10.100.240.14 的回复: 字节=32 时间=1ms TTL=64 来自 10.100.240.14 的回复: 字节=32 时间<1ms TTL=64 来自 10.100.240.14 的回复: 字节=32 时间=1ms TTL=64 来自 10.100.240.14 的回复: 字节=32 时间<1ms TTL=64以上就是手把手教你搭建OpenVPN（保姆级教程）！
每次新机器都配置很麻烦，记录一下如何配置网络yum一、配置本地yum源首先将centos自带的网络yum源删掉（自带的默认从centos官网下载软件，服务器在国外，非常不稳定） [root@cloud ~]# rm -rf /etc/yum.repos.d/*创建挂载点并挂载镜像[root@cloud ~]# mkdir /opt/centos [root@cloud ~]# mount /dev/cdrom /opt/centos/ mount: /dev/sr0 写保护，将以只读方式挂载创建并编写本地yum源配置文件，编写完成之后wq保存退出[root@cloud ~]# vi /etc/yum.repos.d/local.repo [centos] name=centos # Yum源的名称，用于描述这个源 baseurl=file:///opt/centos # 本地Yum源的位置，指向挂载点目录 gpgcheck=0 # 不进行GPG签名检查 enabled=1 # 启用该Yum源清除缓存原来的生成新的缓存并列出当前系统中已配置和启用的Yum仓库（repositories）及其状态[root@cloud ~]# yum clean all 已加载插件：fastestmirror, langpacks 正在清理软件源： centos Cleaning up list of fastest mirrors Other repos take up 1.0 G of disk space (use --verbose for details) [root@cloud ~]# yum makecache 已加载插件：fastestmirror, langpacks Determining fastest mirrors centos | 3.6 kB 00:00:00 (1/4): centos/group_gz | 153 kB 00:00:00 (2/4): centos/primary_db | 3.3 MB 00:00:00 (3/4): centos/other_db | 1.3 MB 00:00:00 (4/4): centos/filelists_db | 3.3 MB 00:00:00 元数据缓存已建立 [root@cloud ~]# yum repolist 已加载插件：fastestmirror, langpacks Loading mirror speeds from cached hostfile 源标识 源名称 状态 centos centos 4,070 repolist: 4,070下载httpd测试一下[root@cloud ~]# yum install -y httpd 已加载插件：fastestmirror, langpacks Loading mirror speeds from cached hostfile 正在解决依赖关系 --> 正在检查事务 ---> 软件包 httpd.x86_64.0.2.4.6-95.el7.centos 将被 安装 --> 正在处理依赖关系 httpd-tools = 2.4.6-95.el7.centos，它被软件包 httpd-2.4.6-95.el7.centos.x86_64 需要 --> 正在处理依赖关系 /etc/mime.types，它被软件包 httpd-2.4.6-95.el7.centos.x86_64 需要 --> 正在处理依赖关系 libaprutil-1.so.0()(64bit)，它被软件包 httpd-2.4.6-95.el7.centos.x86_64 需要 --> 正在处理依赖关系 libapr-1.so.0()(64bit)，它被软件包 httpd-2.4.6-95.el7.centos.x86_64 需要 --> 正在检查事务 ---> 软件包 apr.x86_64.0.1.4.8-7.el7 将被 安装 ---> 软件包 apr-util.x86_64.0.1.5.2-6.el7 将被 安装 ---> 软件包 httpd-tools.x86_64.0.2.4.6-95.el7.centos 将被 安装 ---> 软件包 mailcap.noarch.0.2.1.41-2.el7 将被 安装 --> 解决依赖关系完成 依赖关系解决 ======================================================================================================================== Package 架构 版本 源 大小 ======================================================================================================================== 正在安装: httpd x86_64 2.4.6-95.el7.centos centos 2.7 M 为依赖而安装: apr x86_64 1.4.8-7.el7 centos 104 k apr-util x86_64 1.5.2-6.el7 centos 92 k httpd-tools x86_64 2.4.6-95.el7.centos centos 93 k mailcap noarch 2.1.41-2.el7 centos 31 k 事务概要 ======================================================================================================================== 安装 1 软件包 (+4 依赖软件包) 总下载量：3.0 M 安装大小：10 M Downloading packages: ------------------------------------------------------------------------------------------------------------------------ 总计 95 MB/s | 3.0 MB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction 正在安装 : apr-1.4.8-7.el7.x86_64 1/5 正在安装 : apr-util-1.5.2-6.el7.x86_64 2/5 正在安装 : httpd-tools-2.4.6-95.el7.centos.x86_64 3/5 正在安装 : mailcap-2.1.41-2.el7.noarch 4/5 正在安装 : httpd-2.4.6-95.el7.centos.x86_64 5/5 验证中 : httpd-tools-2.4.6-95.el7.centos.x86_64 1/5 验证中 : mailcap-2.1.41-2.el7.noarch 2/5 验证中 : apr-1.4.8-7.el7.x86_64 3/5 验证中 : httpd-2.4.6-95.el7.centos.x86_64 4/5 验证中 : apr-util-1.5.2-6.el7.x86_64 5/5 已安装: httpd.x86_64 0:2.4.6-95.el7.centos 作为依赖被安装: apr.x86_64 0:1.4.8-7.el7 apr-util.x86_64 0:1.5.2-6.el7 httpd-tools.x86_64 0:2.4.6-95.el7.centos mailcap.noarch 0:2.1.41-2.el7 完毕！下载成功二、配置网络yum源优点：软件包和元数据由源服务器自动更新，国内镜像下载速度非常快缺点：需要稳定的网络连接，网络不稳定或断开时无法使用首先找到国内的镜像站（我这里以阿里云举例）找到自己的Linux版本（以centos为例）找到对应的系统版本（我的是7）并且复制这段命令到虚拟机粘贴同样需要先删除原有的yum仓库列出当前系统中已配置和启用的Yum仓库（repositories）及其状态[root@cloud ~]# rm -rf /etc/yum.repos.d/* [root@cloud ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo --2024-06-09 12:36:26-- https://mirrors.aliyun.com/repo/Centos-7.repo 正在解析主机 mirrors.aliyun.com (mirrors.aliyun.com)... 183.215.53.217, 183.215.53.222, 183.215.53.224, ... 正在连接 mirrors.aliyun.com (mirrors.aliyun.com)|183.215.53.217|:443... 已连接。 已发出 HTTP 请求，正在等待回应... 200 OK 长度：2523 (2.5K) [application/octet-stream] 正在保存至: “/etc/yum.repos.d/CentOS-Base.repo” 100%[======================================>] 2,523 --.-K/s 用时 0.006s 2024-06-09 12:36:26 (401 KB/s) - 已保存 “/etc/yum.repos.d/CentOS-Base.repo” [2523/2523]) [root@cloud ~]# yum repolist 已加载插件：fastestmirror, langpacks Loading mirror speeds from cached hostfile * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com 源标识 源名称 状态 !base/7/x86_64 CentOS-7 - Base - mirrors.aliyun.com 10,072 !extras/7/x86_64 CentOS-7 - Extras - mirrors.aliyun.com 526 !updates/7/x86_64 CentOS-7 - Updates - mirrors.aliyun.com 5,890 repolist: 16,488可以看到已经保存成功，网络yum源就配置好了
Linux磁盘扩容需求：新增第二块磁盘 sdb 的空间（30G）扩展到当前的根目录 / 下，典型的 “添加新磁盘 → 扩容已有 LVM 根分区” 的操作流程。你的系统使用了 LVM（逻辑卷管理），所以我们可以把 sdb 加入到现有的 LVM 卷组并扩展根目录。✅ 操作目标将 sdb → 加入 LVM 卷组 centos → 扩容 centos-root → 扩容 / 挂载点空间🧰 操作步骤（谨慎操作，建议备份重要数据）① 确认当前卷组信息vgs输出示例（你应该会看到一个名为 centos 的卷组）：VG #PV #LV #SN Attr VSize VFree centos 1 2 0 wz--n- <29.00g 0② 对 sdb 创建物理卷（PV）pvcreate /dev/sdb成功输出类似：Physical volume "/dev/sdb" successfully created.③ 把 /dev/sdb 加入现有的卷组（VG）vgextend centos /dev/sdb成功输出类似：Volume group "centos" successfully extended④ 扩容逻辑卷（LV）到占满所有空间lvextend -l +100%FREE /dev/centos/root（这会将 VG 中剩余所有空间分配给 centos-root，即 /）⑤ 扩容文件系统如果你用的是 xfs 文件系统（CentOS 7+ 默认）：xfs_growfs /如果你用的是 ext4 文件系统（检查用 df -T /）：resize2fs /dev/centos/root✅ 完成后确认df -h /你应该可以看到 / 挂载点从原来的 26G 增加到了接近 56G（即原有 26G + sdb 的 30G）。[root@cicd_gitlab ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 2.9G 0 2.9G 0% /dev tmpfs 2.9G 84K 2.9G 1% /dev/shm tmpfs 2.9G 18M 2.9G 1% /run tmpfs 2.9G 0 2.9G 0% /sys/fs/cgroup /dev/mapper/centos-root 56G 25G 32G 45% / /dev/sda1 1014M 189M 826M 19% /boot tmpfs 581M 0 581M 0% /run/user/0 [root@cicd_gitlab ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 30G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 29G 0 part ├─centos-root 253:0 0 56G 0 lvm / └─centos-swap 253:1 0 3G 0 lvm [SWAP] sdb 8:16 0 30G 0 disk └─centos-root 253:0 0 56G 0 lvm / sr0 11:0 1 1024M 0 rom [root@cicd_gitlab ~]# 🛑 注意事项操作过程对系统影响较大，请在低峰时段操作或备份重要数据。如果后续计划卸载或缩容，建议新磁盘单独挂载，而不是扩展 /。
Node.js 安装（兼容旧版 glibc 的方案）在 CentOS 等 Linux 系统中运行 Node.js 时，可能遇到如下错误：node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)这是由于系统中的 glibc 版本过低（如 CentOS 7 的默认 glibc 为 2.17），而某些 Node.js 版本依赖更新版本的 glibc（如 2.27 及以上）。✅ 解决方案为避免升级系统或使用 Docker，可使用 专为旧版 glibc 编译的 Node.js 预构建版本，例如：node-v18.18.0-linux-x64-glibc-217.tar.gz该版本可在 CentOS 7 等使用 glibc 2.17 的系统中正常运行。🔧 安装步骤1️⃣ 下载 Node.js 版本进入 Node.js 非官方兼容版本下载页面，下载：node-v18.18.0-linux-x64-glibc-217.tar.gz2️⃣ 上传至服务器例如上传到目录：/root/node/3️⃣ 解压安装包cd /root/node tar -xzvf node-v18.18.0-linux-x64-glibc-217.tar.gz4️⃣ 重命名目录（可选）mv node-v18.18.0-linux-x64-glibc-217 node-v185️⃣ 配置环境变量创建配置文件 /etc/profile.d/nodejs.sh：vi /etc/profile.d/nodejs.sh添加内容如下：export NODE_HOME=/root/node/node-v18 export PATH=.:$NODE_HOME/bin:$PATH保存后使其生效：source /etc/profile6️⃣ 配置国内 npm 镜像源（建议）为加快依赖安装速度，设置为阿里云镜像：npm config set registry=https://registry.npmmirror.com✅ 验证安装node -v npm -v确保输出的版本正确，即代表安装成功。📌 总结通过该方法，可 避免升级系统 glibc，而直接使用为旧系统编译的 Node.js 版本，适合在 CentOS 7 等老系统上运行现代 Node.js 应用。
Linux 安装 JDK 1.8 教程（RPM 包方式）1. 创建安装目录首先，创建一个目录用于存放 JDK 安装文件：bash 复制编辑 sudo mkdir -p /usr/local/java cd /usr/local/java2. 下载 JDK RPM 安装包使用 wget 命令从 Oracle 官方网站下载 JDK 1.8 的 RPM 安装包：WGCLOUDbash 复制编辑 wget --no-check-certificate --no-cookies \ --header "Cookie: oraclelicense=accept-securebackup-cookie" \ http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.rpm注意：下载链接可能会因 Oracle 政策变化而失效，请根据实际情况获取最新的下载链接。3. 安装 JDK为下载的 RPM 包添加执行权限并进行安装：bash 复制编辑 chmod +x jdk-8u131-linux-x64.rpm sudo rpm -ivh jdk-8u131-linux-x64.rpm4. 配置环境变量编辑系统环境变量配置文件 /etc/profile：bash 复制编辑 sudo vim /etc/profile在文件末尾添加以下内容：bash 复制编辑 export JAVA_HOME=/usr/java/jdk1.8.0_131 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH export PATH=$PATH:${JAVA_HOME}/bin:${JRE_HOME}/bin保存并退出编辑器后，使环境变量配置立即生效：bash 复制编辑 source /etc/profile5. 验证安装执行以下命令，检查 JDK 是否安装成功：bash 复制编辑 java -version如果安装成功，系统将显示类似以下的信息：scss 复制编辑 java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
来一个大的介绍：ElasticSearch的性能优化的几大方面本文借鉴尼恩博主，参考学习总的来说，ElasticSearch的性能优化，可以从以下方面的考虑：硬件层面的优化：机器分配，机器配置，机器内存，机器CPU，机器网络，机器磁盘性能系统层面的优化：文件句柄优化、swap关闭ElasticSearch集群层面的优化 重点合理分配节点，合理分配参加竞选Master的节点ElasticSearch 索引层面的优化 重点副本数量、索引数量、分片数量ElasticSearch查询层面的优化职责分离，全面监控1 硬件层面的优化1.1 为ES进行 硬件层面的 内存优化：Elasticsearch是一个内存密集型应用，合理分配内存对于Elasticsearch 提高性能至关重要:确保系统内存有足够的空间用于文件缓存，而不单单是JVM堆内存。建议将一半的内存分配给Elasticsearch的堆内存，另一半留给操作系统和其他进程。为了避免垃圾回收（GC）成为瓶颈，建议设置堆内存的最大值为30GB或更小。可以通过修改jvm.options文件来调整堆内存的大小。总之，ES是比较依赖内存的，并且对内存的消耗也很大，内存对ES的重要性甚至是高于CPU的，所以即使是数据量不大的业务，为了保证服务的稳定性，在满足业务需求的前提下，我们仍需考虑留有不少于20%的冗余性能。根据业务量不同，内存的需求也不同，一般生产建议总体 32内存，其中JVM 不要少于16G， 还有 16G给底层的Luence。为ES进行 系统层面的 磁盘优化对于ES来说，磁盘可能是最重要的了，因为数据都是存储在磁盘上的，当然这里说的磁盘指的是磁盘的性能。磁盘性能往往是硬件性能的瓶颈，木桶效应中的最短板。方式一：纵向扩展： 使用最高性能的 磁盘ES 磁盘优化的最为简单，最为粗暴的方式是 纵向扩展，就是使用最高性能的 磁盘，比如SSD 固态硬盘。ES应用可能要面临不间断的大量的数据读取和写入，数据规模非常庞大， 尼恩曾经架构过的一个30个节点ES集群，数据量非常庞大，达到1TB 。一般来说，速度最快的，也是最昂贵的。不可能都用SSD硬盘， 一般是 SSD和机械硬盘组合使用。所以，可以考虑把节点冷热分离：“热节点”使用SSD做存储，可以大幅提高系统性能；“冷数据“存储在机械硬盘中，降低成本。如果使用磁盘阵列，可以使用RAID 0。这里，尼恩给大家来点基础知识， 什么是RAID 0？RAID（Redundant Array of Independent Disks，独立磁盘冗余阵列）是一种将多个物理磁盘组合成一个逻辑单元的数据存储虚拟化技术，其目的是提高数据存储的性能、可靠性或两者兼顾。RAID 0 是RAID中一种配置方式，具体含义如下：RAID 0 定义：RAID 0 又称为条带化（Striping），它将数据分散存储在两个或多个硬盘上，每个硬盘只存储数据的一部分，从而提高数据的读写速度。工作原理：在RAID 0配置中，数据被分成多个块（称为“条带”），并按顺序存储在多个硬盘上。例如，如果有一个4KB的数据块，它会被分成4个1KB的块，分别存储在4个硬盘上。读取数据时，因为可以同时从多个硬盘读取，所以读取速度会加快。写入数据时也是同理，数据会被分成多个块并行写入多个硬盘。性能提升：RAID 0 的主要优势在于提高数据的读写速度。由于数据被分散在多个硬盘上，所以理论上，RAID 0 的读写速度可以达到单个硬盘速度的总和。没有冗余：RAID 0 的缺点是没有数据冗余。如果其中一个硬盘发生故障，整个阵列上的数据都会丢失，因为每个硬盘上只存储数据的一部分，没有副本。适用场景：RAID 0 适用于对性能要求高而对数据安全性要求不高的场景，如视频编辑、大型数据库的临时存储等。RAID 0 风险：由于没有数据冗余，RAID 0 的风险较高。一旦硬盘发生故障，整个RAID 0 阵列上的数据都会丢失，因此在使用RAID 0 时需要非常小心，并且定期备份数据。总结来说，RAID 0 是一种通过将数据分散存储在多个硬盘上来提高性能的配置方式，但它不提供任何数据冗余，因此使用时需要考虑到数据丢失的风险。这里，尼恩给大家再来点基础知识， 看看 RAID 0 和 RAID 1 的区别?RAID 0 和 RAID 1 是两种不同的RAID配置，RAID 0 和 RAID 1 在数据存储、性能和可靠性方面有显著的区别：RAID 0（条带化）： 多块磁盘并行读写数据存储：数据被分割成多个块（条带），然后分散存储在两个或多个硬盘上。这意味着每个硬盘上存储的是数据的不同部分，而不是数据的副本。性能：由于数据的读写操作可以并行进行，RAID 0 可以提供很高的数据传输速率，特别是在写入大量数据时。可靠性：RAID 0 没有数据冗余，任何一个硬盘的故障都会导致整个阵列上的数据丢失。因此，RAID 0 的可靠性最低。适用场景：适合对读写性能要求高、对数据安全性要求不高的应用，如视频处理、大型数据库的临时存储等。RAID 1（镜像）： 多块磁盘冗写，并行读数据存储：数据在两个硬盘上存储完全相同的副本，即镜像。这意味着数据在两个硬盘上都有备份。性能：RAID 1 的读取性能通常比写入性能要好，因为多个硬盘可以同时读取数据。但是，写入性能可能受到限制，因为数据需要同时写入两个硬盘。可靠性：RAID 1 提供了很好的数据冗余，即使一个硬盘发生故障，另一个硬盘上的数据副本仍然可以保证数据的完整性和可用性。适用场景：适合对数据安全性要求高的应用，如关键业务数据存储、服务器操作系统等。总结来说，RAID 0 提供了高性能但牺牲了数据安全性，而RAID 1 则提供了数据冗余和较高的可靠性，但性能相对较低。在选择RAID配置时，需要根据具体的应用需求和预算来决定使用哪种RAID级别。方式二：横向扩展：使用多块硬盘提高 硬盘的性能横向 扩展，就是 搞多块磁盘。Elasticsearch 通过多个path.data目录配置，把Elasticsearch数据条带化分配到使用多块硬盘上面，以提高I/O性能。要在Elasticsearch中使用多块硬盘，并通过配置多个path.data目录来实现数据的条带化分配，可以按照以下步骤操作：配置多个数据路径：在Elasticsearch的配置文件elasticsearch.yml中，可以指定多个数据存储路径（对应到多个磁盘），用逗号分隔。例如，如果你有三块硬盘，并且已经分别挂载在/mnt/disk1、/mnt/disk2和/mnt/disk3，你可以这样配置： path.data: /mnt/disk1,/mnt/disk2,/mnt/disk3这样配置后，Elasticsearch会尝试在这些路径上存储数据，类似于软件层面的RAID 0配置。ES的分片分配机制：Elasticsearch在选择存储分片的路径时，会根据一定的逻辑来选择最佳路径。Elasticsearch会优先选择磁盘使用率较低（/最为空闲）的磁盘来存储新的分片数据文件。如果多个路径可用，Elasticsearch会根据剩余空间和已存储的分片数量来决定将分片分配到哪个路径上。数据安全性问题：虽然使用多个path.data路径可以提高I/O性能，但这种做法类似于软件层面的RAID 0，没有在磁盘层面 做 数据冗余。如果其中一个硬盘发生故障，存储在该硬盘上的所有分片都会丢失。因此，官方建议，确保每个分片至少有一个副本分片，以防止数据丢失。系统层面的ES 磁盘优化 总结：ES应用可能要面临不间断的大量的数据读取和写入，磁盘关系到 ES的性能，建议使用最高速的磁盘设备（如SSD）可以显著提高Elasticsearch的性能。当然最高速也是最昂贵的，不可能都用SSD硬盘， 一般是 SSD和机械硬盘组合使用。合理规划磁盘布局，可以考虑把节点冷热分离， 将数据按照冷热的程度，存储在不同的磁盘上，可以避免磁盘I/O竞争。使用RAID 0可以提高磁盘I/O，但要注意数据的安全性。通过多个path.data目录配置，把Elasticsearch数据条带化分配到使用多块硬盘上面，以提高I/O性能。避免使用远程挂载的存储，比如NFS或者SMB/CIFS，因为这会引入延迟，影响性能。1.2 为ES进行 硬件层面的 CPU 调优CPU对其他的 应用程序而言，可谓是最重要的硬件，但对于ES来说，CPU不是他最依赖的硬件， 提升CPU配置可能不会像提升磁盘或者内存配置带来的性能收益更直接、显著。在成本预算一定的前提下，应该把更多的预算花在磁盘以及内存上面。当然也不是说CPU的性能就不重要，Elasticsearch在处理查询和索引操作时会消耗大量的CPU资源。服务器的CPU不需要太高的单核性能，更多的核心数和线程数意味着更高的并发处理能力。现在PC的配置8核都已经普及了，更不用说服务器了。对于ES来说，通常来说单节点cpu 4核起步，不同角色的节点对CPU的要求也不同。CPU优化的第一步：角色分离，合理分配节点角色与资源在 ElasticSearch 集群中，明确划分主节点（Master）、数据节点（Data）和协调节点（Coordinating）的角色。主节点（Master）主要负责集群的管理和元数据存储，其 CPU 负载主要来自于集群状态的维护和更新。数据节点（Data）负责存储和索引数据，在数据写入和查询操作时会占用较多 CPU 资源，这是CPU的大户。协调节点（Coordinating）负责接收用户查询请求并将其分发到合适的数据节点进行处理，也会消耗一定的 CPU 资源用于请求的转发和结果的合并。例如，在一个大型的ES集群中，大致的CPU配置如下：主节点（Master）配置在性能稳定、CPU 核心数适中（如 4 - 8 核）的服务器上，专门用于处理集群管理任务；数据节点（Data）则根据数据量和查询负载，分配具有较高 CPU 核心数（如 16 - 32 核）的服务器，以满足大量数据的索引和查询操作；协调节点（Coordinating）可以选择 CPU 核心数相对较少（如 8 - 16 核）但网络性能良好的服务器，用于高效地分发和接收查询请求。CPU优化的第二步：对线程池进行监控和动态调优ElasticSearch 线程池类型ElasticSearch 中有多种线程池，如搜索线程池（search）、索引线程池（index）、批量线程池（bulk）等。每个线程池负责不同类型的操作，并且其大小会影响 CPU 的利用率。例如:搜索线程池（search）大小决定了可以同时处理的搜索查询数量索引线程池（index）大小则影响数据索引的并发处理能力。优化线程池大小根据 CPU 核心数和集群的实际负载情况，合理调整线程池大小。一般来说，线程池大小不应超过 CPU 核心数的两倍，以避免过多的线程竞争 CPU 资源导致性能下降。例如，在一个具有 16 核 CPU 的数据节点上，可以将搜索线程池大小设置为 30 左右，索引线程池大小设置为 20 左右。同时，可以通过监控工具（如 ElasticSearch 自带的监控 API 或者第三方监控工具）观察线程池的活跃线程数、队列长度等指标，根据这些指标动态调整线程池大小。使用 ElasticSearch 自带监控 API获取监控数据ElasticSearch 提供了丰富的 REST API 用于监控集群状态。可以通过访问_cat/thread_pool端点来获取线程池相关的信息。例如，发送一个 HTTP GET 请求到http://<es-node-ip>:9200/_cat/thread_pool?vv参数用于以详细格式返回结果），会返回类似如下内容：pool_name active queue rejected bulk 0 0 0 fetch 0 0 0 flush 0 0 0 generic 0 0 0 get 0 0 0 index 0 0 0 listener 0 0 0 management 0 0 0 percolate 0 0 0 refresh 0 0 0 search 0 0 0 snapshot 0 0 0 suggest 0 0 0 war m 0 0 0 write 0 0 0其中，active列表示当前活跃的线程数，queue列表示线程池队列中的任务数，rejected列表示被拒绝的任务数。通过这些核心指标，可以解每个线程池的负载情况。动态调整线程池大小（通过 API 或配置文件）如果发现 线程池负载高，或者 \`queue 线程池队列中的任务数 积压多，可以进行动态调整线程池大小调整在 ElasticSearch 中，可以通过更新集群设置来动态调整线程池大小，可以使用下面的API，_cluster/settings发送一个 PUT类型的settings 请求，例如：{ "persistent": { "thread_pool.search.size": 30 } }这个请求将搜索线程池（search）的大小调整为 30。需要注意的是，这种调整方式需要谨慎操作，并且最好在低峰期进行，因为不当的调整可能会对集群性能产生暂时的负面影响。另外，也可以通过修改elasticsearch.yml配置文件来调整线程池大小，不过这种方式需要重启集群才能生效。CPU 的优化建议：确保服务器有足够的CPU核心，并合理调整Elasticsearch的线程池配置，可以提高系统的并发处理能力。推荐使用多核处理器（至少8核及以上）来提升处理能力。避免虚拟化过度使用，特别是在生产环境中，因为这可能会引入不必要的性能开销。对线程池进行监控和动态调优， 防止线程任务积压1.3 为ES进行 硬件层面的 网络优化：建议为ES 使用 低延迟的网络 ：ES是天生自带分布式属性的，并且ES的分布式系统是基于对等网络的，节点与节点之间的通信十分的频繁，延迟对于ES的用户体验是致命的，所以对于ES来说，低延迟的网络是非常有必要的。高延迟的，跨多个数据中心的ES集群方案，是不太可取的，虽然，ES可以容忍集群跨多个机房，可以有多个内网环境，支持跨AZ部署，但是不建议多个机房跨地域构建集群，一旦发生了网络故障，集群可能直接雪崩，即使能够保证服务正常运行，维护这样（跨地域单个集群）的集群带来的额外成本可能远小于它带来的额外收益。2 系统层面的优化2.1 为ES进行 系统层面的 文件句柄数 调优修改ES启动用户可使用的系统文件句柄数，以适应Elasticsearch的需求。Elasticsearch 需要大量 的文件句柄数， 主要 两大 原因：第一：ES集群有大量的TCP /http 连接需求大量的节点间通信连接：Elasticsearch 集群中节点之间， 需要进行频繁的通信。如数据同步、状态协调等，这会建立大量的 TCP 连接，每个 TCP 连接都需要占用一个文件句柄1。ES集群有大量的客户端连接：当有大量的 HTTP 客户端连接到 Elasticsearch 集群进行数据读写操作时，也会消耗大量的文件句柄。如果文件句柄数不足，可能导致新的连接无法建立，客户端出现连接超时等问题 。第二：ES集群有大量的索引与数据操作ES集群有大量的索引文件操作：Elasticsearch 在处理索引时，会涉及到大量的文件操作。例如，在索引创建、更新、删除过程中，需要打开和操作多个索引文件，包括索引元数据文件、数据文件、段文件等。每个文件都需要一个文件句柄，如果文件句柄数受限，可能会导致索引操作失败或异常。ES集群有大量的数据段合并：为了提高查询性能，Elasticsearch 会在后台进行数据段合并操作。在合并过程中，需要同时打开多个段文件进行读写，如果文件句柄数不足，会影响段合并的效率，甚至导致合并失败 。问题：文件句柄数的 系统限制，满足不了es的句柄需求系统默认文件句柄数限制过低：许多 Linux 发行版默认每个进程允许的文件句柄数通常只有 1024 或更低，这对于处理大量数据和高并发请求的 Elasticsearch 来说远远不够。避免资源耗尽风险：如果不调整文件句柄数，当 Elasticsearch 的文件句柄使用达到系统默认限制时，继续请求打开文件就会失败，可能导致数据丢失、节点故障等严重问题 。调优：如何解除 文件句柄数的 系统限制？step1：查看当前系统文件句柄数限制：可以使用ulimit -n命令查看当前文件描述符限制，默认通常是 1024，对于 ES 来说远远不够。step2：修改文件句柄数临时修改文件句柄数：可以 通过ulimit -n 65536命令将当前会话的文件描述符限制临时设置为 65536 。永久修改文件句柄数：使用vim /etc/security/limits.conf命令编辑该limits.conf 文件，添加或修改以下配置项：* soft nofile 65536 * hard nofile 65536上述配置中，*表示匹配所有用户，soft为警告值，hard为最大值。检查配置是否生效：重新登录系统后，使用ulimit -n命令查看文件句柄数限制是否已生效2.2 为ES进行 系统层面的 swap 调优这里就是简单粗暴，关掉swap。内存交换 到磁盘对服务器性能来说是 致命 的。如果内存交换到磁盘上，性能就会急剧下降， 一个 100 微秒的操作可能变成 10 毫秒，下降100倍以上。想想那么多 10 微秒的操作时延累加起来。不难看出 swapping 对于性能是多么可怕。所以，Elasticsearch建议禁用Swap分区，因为当物理内存不足时，操作系统会将一些内存页交换到磁盘上，这会导致性能急剧下降。在Linux系统中， 用以下命令关掉swap:sudo swapoff -a在Linux系统中使用ES，最好可以通过修改/etc/sysctl.conf文件来禁用Swap分区：# 在文件末尾添加以下行 vm.swappiness=1然后运行sudo sysctl -p使配置生效。除了 关掉swap ，还可以 使用 memlock ，为ES 锁定的物理内存。memlock 是一个与内存锁定相关的系统资源限制。它用于指定一个进程能够锁定的物理内存（RAM）的大小。内存锁定意味着将进程使用的内存固定在物理内存中，防止操作系统将其交换（swap）到磁盘上的虚拟内存（磁盘交换空间）。对于像 Elasticsearch 这样的应用程序，将内存锁定是很重要的。因为 Elasticsearch 大量使用内存来缓存索引数据，如果这些数据被交换到磁盘，会导致查询性能大幅下降。内存锁定可以确保关键的内存区域（如索引缓存）始终在物理内存中，提高数据访问速度。可以通过设置Elasticsearch的bootstrap.memory_lock选项来尝试锁定JVM内存，防止其被交换到磁盘上， 在 配置文件elasticsearch.yml中添加以下行 bootstrap.memory_lock: true注意：这需要用户 有memlock权限。可以通过ulimit -l命令查看当前用户的memlock限制，并通过ulimit -l unlimited命令设置无限制（但这通常需要root权限）。在生产环境中，更推荐的方式是通过修改/etc/security/limits.conf文件来永久设置 memlock 限制。通过修改/etc/security/limits.conf文件来永久设置memlock限制，可按以下步骤进行操作：用文本编辑器打开/etc/security/limits.conf文件， 在文件末尾添加或修改以下内容 ：* soft memlock [期望的软限制大小] * hard memlock [期望的硬限制大小]例如，如果想将memlock限制设置为 8GB（以字节为单位），则可写成：* soft memlock 8388608 * hard memlock 8388608这里的*表示匹配所有用户，你也可以根据实际情况指定具体的用户，如elastic soft memlock 8388608和elastic hard memlock 8388608，只对elastic用户生效 。3 JVM层面的优化3.1 为ES进行 JVM层面的 JVM堆大小调优Elasticsearch是运行在JVM上的，对其做JVM参数调优至关重要。最常见的调优是Java内存的分配。下面是JVM的内存模型，具体每块的作用，不在这里阐述。新生代和老年代分配的内存比例给多大？Jvm内存分为新生代和老年代。新生代（或者伊甸园）新实例化的对象分配的空间。新生代空间通常都非常小，一般在 100 MB–500 MB。新生代也包含两个 幸存 空间。老年代较老的对象存储的空间。这些对象预计将长期留存并持续上很长一段时间。老生代通常比新生代大很多。新生代、老生代的垃圾回收都有一个阶段会“stop the world”。给新生代和老年代分配多大的内存呢？他们的比例是多少呢？一般来说，老年代和新生代的内存比例为2：1是比较合适的。比如给堆内存分配3G，则新生代分配1G，其余都给老年代。在ElasticSearce的配置文件jvm.options文件配置：-Xms3g //配置堆初始化大小 -Xmx3g //配置堆的最大内存 -Xmn1g //配置新生代内存。一个Elasticesearch节点的两个内存大户内存对于 Elasticsearch 来说绝对是重要的，它可以被许多内存数据结构使用来提供更快的操作。一台机器装一个Elasticesearch节点，我们应该怎么分配机器的内存呢？官方给出了解决方案，把一半（少于）的内存分配给Luence，另外的内存分配给ElasticSearch.是一个Elasticesearch内存大户，底层的 Luence 另一个内存大户：第一个 内存大户Elasticsearch 缓存（1） 文档 缓存数据最重要的是缓存索引数据。首先 是 文档数据缓存。 当执行查询操作时，经常访问的文档数据会被缓存在堆内存中。例如：一个电商商品搜索系统使用 Elasticsearch，用户频繁搜索的热门商品信息（如商品名称、价格、品牌等）会被缓存，这样在后续查询时可以快速从内存中获取，减少磁盘 I/O 操作，提高查询响应速度。其次 是过滤器缓存， 也存储在堆内存中。当使用过滤器进行查询（如筛选出特定品牌的商品）时，过滤器的结果会被缓存，下次遇到相同的过滤条件时可以直接利用缓存结果，提高过滤效率。（2） 索引缓冲区在数据写入索引的过程中，会有一个索引缓冲区（Index Buffer）在堆内存中。新的数据首先进入这个缓冲区，当缓冲区满或者达到一定的阈值时，数据会被刷新到磁盘上的索引文件中。（3） 查询相关数据结构存储查询执行过程中的临时数据结构，如用于排序的字段值集合。例如，对商品按照价格进行排序的查询，排序过程中的价格数据会暂存在堆内存中。还有聚合操作的中间结果，比如在统计不同品牌商品数量的聚合查询中，每个品牌的计数中间结果会存储在堆内存。（4） 集群状态 缓存Elasticsearch 主节点需要在堆内存中存储集群状态信息，包括索引元数据（如索引名称、分片数量、副本数量等）、节点信息（如节点角色、节点健康状态等）。这些信息对于集群的管理和协调至关重要，如在进行分片分配、故障转移等操作时会使用到这些信息。第二个 内存大户 Lucene 非堆内存（off - heap）（1） 索引文件数据Lucene 是 Elasticsearch 的底层搜索引擎库，大部分索引数据实际上存储在非堆内存（磁盘文件）中。索引文件包含了倒排索引，倒排索引是 Lucene 的核心数据结构。例如，对于文档中的每个单词，倒排索引记录了包含该单词的文档列表。在文本搜索场景下，如新闻网站的文章搜索，文章中的词汇和对应的文档编号存储在非堆内存的索引文件中，用于快速定位包含特定词汇的文章。（2） 段文件（Segment Files）存储在非堆内存中。Lucene 会将索引划分为多个段，每个段是一个独立的索引部分。这些段文件包含了文档数据、词向量等信息，并且会根据一定的策略进行合并和优化。例如，在数据更新过程中，新的数据可能会形成新的段，然后通过段合并操作来优化索引结构，这些段文件存储在非堆内存中。（3）文件系统缓存操作系统会对磁盘上的索引文件进行缓存，这部分缓存数据虽然不在 Java 堆内存中， 也是（off - heap）非堆内存的一部分。当 Lucene 读取索引文件时，操作系统会将最近访问的文件块缓存在内存中，提高文件的访问速度。例如，频繁被查询的索引段文件部分会被操作系统缓存，下次访问时可以直接从缓存中读取，减少磁盘读取时间。Lucene 使用 内存映射文件（Memory - Mapped Files）数据，将磁盘上的索引文件映射到内存中，使得文件访问就像访问内存一样高效。这部分内存映射的数据也属于非堆内存。通过这种方式，Lucene 可以直接在内存中操作索引文件的部分内容，而不需要进行大量的文件 I/O 操作，提高了索引数据的读写效率。该分配多大的内存给Elasticesearch？如果 把所有的内存都分配给 Elasticsearch 的堆内存，那将不会有剩余的内存交给 Lucene。这将严重地影响全文检索的性能。标准的建议是把 50％ 的可用内存作为 Elasticsearch 的堆内存，保留剩下的 50％，Lucene 会很乐意利用起余下的内存，也就是把 50％ 的可用内存作为 Lucene 的非堆内存（off - heap）。Lucene 被设计为可以利用操作系统底层机制来缓存内存数据结构，所以这部分Lucene 的非堆内存（off - heap），预留就OK了，不需要专门设置。所以，在使用Elasticesearch的时候，按照实际的系统内存大小，一般进行Elasticesearch堆大小进行对应的配置，从最小规格的8G（如果 os内存 16g） 到了中等规模16G内存（如果 os内存 32g），然后最大规格32G内存（如果 os内存>64g）。分配的建议如下：如果 os内存 16g ，jvm 8G如果 os内存 32g ，jvm 16g如果 os内存>64g ，jvm 32gXms和Xmx设置：合理设置堆内存大小， 一般建议将堆内存设置为物理内存的 50 - 70%。通过-Xms（初始堆大小）和-Xmx（最大堆大小）参数来设置初始堆大小、最大堆大小 相同。例如：-Xms30g -Xmx30g确保-Xms和-Xmx的值相同，这样可以避免在堆内存扩展或收缩过程中产生性能开销。建议将JVM堆内存的初始大小(-Xms)和最大大小(-Xmx)设置为相同的值，以避免运行过程中动态调整堆内存大小，这是一个损耗性能的操作。可以通过修改jvm.options文件来调整堆内存的大小。例如，将堆内存设置为16GB的配置如下：-Xms16g -Xmx16g这里将堆内存的初始大小和最大大小都设置为16GB，你可以根据实际情况调整分配给Elasticesearch 最大内存应该小于 32766 mb（\~31.99 gb）为啥呢？ JVM 在内存小于 32 GB 的时候会采用一个内存对象指针压缩技术。对于 32 位的系统，意味着堆内存大小最大为 4 GB。对于 64 位的系统， 可以使用更大的内存，但是 64 位的指针意味着更大的浪费，因为你的指针本身大了。更糟糕的是， 更大的指针在主内存和各级缓存（例如 LLC，L1 等）之间移动数据的时候，会占用更多的带宽。所以， 为了节省 64 位的指针 占用内存的大小， Java 使用一个叫作 内存指针压缩（compressed oops）的技术来解决这个问题。使用 内存指针压缩（compressed oops）的技术， 对象的指针不再表示对象在内存中的精确位置，而是表示 偏移量 。这意味着 32 位的指针可以引用 40 亿个 对象 ， 而不是 40 亿个字节。从极端角度来说，假设一个对象一个字节，使用 内存指针压缩（compressed oops）的技术后， ， 32 位的指针表示对象的指针理解为，最大增长到 32 GB 的物理内存。一旦你越过那个神奇的 \~32 GB 的边界，指针就会切回普通对象的指针。切回普通对象的64位指针，每个对象的指针都变长了，就会使用更多的 CPU 内存带宽，也就是说你实际上失去了更多的内存。事实上，哪怕是 40–50 GB 内存，和使用了内存对象指针压缩技术时候的 32 GB 内存的 效果是一样的， 10多个G白白的给普通对象的64位指针了 。总之：即便你有足够的内存，JVM 也尽量不要 超过 32 GB。因为它浪费了内存，降低了 CPU 的性能，还要让 GC 应对大内存。3.2 垃圾回收器的优化3.2.1：JDK8 + G1 垃圾回收器很多公司生产环境使用的是老的es 6 系列版本（比较稳定），如JDK：JDK1.8\_171-b11 (64位)ES集群：一般由3台16核32G的虚拟机部署 ES 集群，每个节点分配16G堆内存ELK版本：6.3.0垃圾回收器：ES 默认指定的老年代（CMS）+ 新生代（ParNew）操作系统：CentOS Linux release 7.4.1708(Core)Elasticsearch 默认的垃圾回收器（ GC ）是 CMS。官方建议使用 CMS。这个垃圾回收器可以和应用并行处理，以便它可以最小化停顿。然而，它有两个 stop-the-world 阶段，但是 CMS处理大内存也有点吃力。启用 G1 垃圾回收器在 Elasticsearch 的 jvm.options文件中，将默认的垃圾回收器设置为 G1。找到以下行（如果不存在可以添加）：-XX:+UseG1GC这会告诉 JVM 使用 G1 垃圾回收器来管理内存。设置 G1堆内存 参数根据堆内存大小来调整 G1 的相关参数。第一：确定 G1 垃圾回收器的堆内存区域（Region）大小：例如，-XX:G1HeapRegionSize参数用于确定 G1 垃圾回收器的堆内存区域（Region）大小。其默认值会根据堆内存大小自动调整，但在某些情况下可能需要手动优化。计算公式为：1M <= G1HeapRegionSize <= 32M，并且堆内存大小 / G1HeapRegionSize = Region数量应该是2^n。例如，对于 30GB 的堆内存，可以尝试将-XX:G1HeapRegionSize设置为16M，这样Region数量为30 * 1024 / 16 = 1920（近似值），是一个比较合适的数值。第二：优化停顿时间相关参数设置最大停顿时间目标，使用-XX:MaxGCPauseMillis参数来控制 G1 垃圾回收器的最大停顿时间目标。例如，设置为200表示希望垃圾回收的停顿时间尽量不超过 200 毫秒。但要注意，设置的值过小可能会导致 G1 频繁地进行垃圾回收以满足停顿时间要求，反而增加了总的垃圾回收时间。需要根据实际的业务场景和性能测试来调整这个参数。第三：调整混合垃圾回收（Mixed GC）的触发条件G1 会在一定条件下触发混合垃圾回收，即同时回收年轻代和部分老年代。可以通过-XX:InitiatingHeapOccupancyPercent参数来调整触发混合垃圾回收的堆内存占用比例。例如，将其设置为45，表示当堆内存占用达到 45% 时，G1 开始考虑触发混合垃圾回收。这个参数的调整需要综合考虑堆内存大小、对象分配速率和停顿时间要求等因素。3.2.2：jdk17 + ZGC垃圾回收器45岁老架构师尼恩建议，如果条件允许，可以升级jdk版本，提升性能。那为什么要升级到jdk17呢，主要是由于：1、jdk17的g1垃圾回收期相较于jdk11，提升到15%左右。2、在jdk17中，有向量化支持，同时在字符串处理等方面做了一些优化，相对于集群的写入压力会小很多。3、在jdk17中，对于ZGC的适配做了一些加强，这个ZGC的垃圾回收器比G1稳定性和性能有很大的提升。在考虑把jdk的版本升级到jdk17去，同时尽量把elasticsearch的版本更新到最新的版本去。在完成了jdk17的升级之后，下一步考虑的就是配置ZGC垃圾回收器。ZGC垃圾回收器是一个并发的、单代的、基于区域的、NUMA 感知的垃圾回收器，Stop-the-world 阶段仅限于根扫描，因此 GC 暂停时间不会随堆或 live set 的变大而增加。而且ZGC垃圾回收期在回收的过程中几乎是全程并发处理，如下图：所以对于短暂停顿的时间来说非常小，几乎可以忽略不计。下面我们来演示下配置ZGC的垃圾回收器方法。关于zgc的文章，尼恩后续写一个系统化的介绍， 帮助大家穿透zgc。在elasticsearch的config目录下有一个jvm.options文件打开后，找到如下部分：因为我们的jdk版本是jdk17，所以找到14-的地方把这里所有的东西注释掉，然后添加如下的代码：14-:-XX:+UseZGC 14-:-XX:+UnlockExperimentalVMOptions最后的样子如下图：此时我们就配制好了当前的elasticsearch的环境使用ZGC垃圾收集器。然后我们启动下elasticsearch，启动完成后在日志中查找下ZGC关键词，如下图：可以看到启动的时候使用的是ZGC垃圾收集器。以上就是ZGC垃圾收集器的配置全过程。1、zgc垃圾收集器从jdk11版本开始支持，所以只要是jdk11及以上的版本都支持zgc垃圾收集器。2、zgc垃圾收集器的由于是并行进行处理，所以生产环境中ZGC的垃圾收集停顿时间不超过10ms，可以放心大胆的使用起来。3、使用ZGC是并行的进行垃圾回收，所以有时候会发生查询耗时增加的情况，可以在jvm.options文件中添加如下的参数：-XX:+UseDynamicNumberOfGCThreads 代表动态配置GC线程，降低CPU的使用量，这个是JDK17的新特性，在jdk17以下是没有这个参数的。具体如下图：Elasticsearch的内存优化总结Elasticsearch的内存配置是其性能优化中非常重要的一部分。以下是一些关键点：Elasticsearch默认将JVM堆内存设置为1GB或系统物理内存的一半，取较小值。但是这个默认值可能不适合所有场景，因此需要根据实际情况进行调整。官方建议堆内存大小不要超过32GB，因为当堆大小超过32GB时，JVM会使用64位指针，这会导致更多的内存消耗和额外的开销。如果需要更大的堆内存，可以考虑增加节点数量而不是单个节点的堆内存大小。堆内存应该合理分配给Elasticsearch进程，同时要确保操作系统和其他进程有足够的内存可用。通常，将机器总内存的50%到60%分配给Elasticsearch是合理的，剩下的内存留给操作系统缓存和其他服务。对垃圾回收器进行优化，可以使用zgc4 ElasticSearch集群层面的优化Elasticsearch的核心架构，是围绕着节点（Node）和集群（Cluster）展开。节点是Elasticsearch的运行实例，每个节点都承担着特定的任务和职责。多个节点组成一个集群，通过分布式的方式存储和处理数据，实现了高可用性、可扩展性和高性能。4.1. Elasticsearch节点概述4.1.1 节点的基本概念在Elasticsearch中，节点（Node） 是一个运行中的Elasticsearch实例。它是集群的基本组成单元，负责存储数据、处理查询请求以及与其他节点进行通信。每个节点都有一个唯一的标识符，用于在集群中进行识别和通信。4.1.2 节点的类型划分Elasticsearch中的节点可以根据其功能和角色进行不同的划分，常见的节点类型包括：4.1.2.1 主节点（Master Node）主节点 负责管理整个集群的状态，包括节点的加入和离开、索引的创建和删除、分片的分配和迁移等。主节点并不负责处理数据的存储和查询，它的主要职责是维护集群的元数据和协调各个节点的工作。以下是一个简单的示例，展示如何在Elasticsearch的配置文件中设置节点为主节点：node.name: master-node node.master: true node.data: false在上述配置中，node.master 设置为 true 表示该节点可以作为候选主节点，node.data 设置为 false 表示该节点不存储数据。这里设置的是候选主节点，真正的主节点通过选举产生，45岁老架构师尼恩提示， es 集群只能有一个 真正主节点。4.1.2.2 数据节点（Data Node）数据节点 负责存储和检索数据。它是集群中最核心的节点类型，负责处理文档的索引和查询操作。数据节点会将数据按照一定的规则进行分片存储，以实现数据的分布式存储和并行处理。以下是一个设置数据节点的示例配置：node.name: data-node node.master: false node.data: true在这个配置中，node.master 设置为 false 表示该节点不作为主节点，node.data 设置为 true 表示该节点是数据节点。4.1.2.3 协调节点（Coordinating Node）协调节点 负责接收客户端的查询请求，并将请求转发到合适的数据节点进行处理。协调节点会收集各个数据节点的查询结果，并进行合并和排序，最终将结果返回给客户端。协调节点本身不存储数据，它的主要作用是协调和管理查询请求的处理过程。以下是一个设置协调节点的示例配置：node.name: coordinating-node node.master: false node.data: false node.ingest: false search.remote.connect: false在这个配置中，除了将 node.master 和 node.data 设置为 false 外，还将 node.ingest 和 search.remote.connect 设置为 false，以确保该节点只作为协调节点。4.1.3 节点的通信机制Elasticsearch中的节点通过 TCP 协议进行通信。节点之间会建立连接，通过发送和接收消息来进行数据的传输和交互。节点之间的通信是基于请求-响应模型的，一个节点发送请求，另一个节点接收请求并返回响应。为了提高通信效率和可靠性，Elasticsearch采用了一些优化措施，例如：连接池：节点会维护一个连接池，用于管理与其他节点的连接。连接池可以复用已建立的连接，减少连接的建立和销毁开销。消息压缩：在节点之间传输消息时，Elasticsearch会对消息进行压缩，以减少网络传输的数据量。异步通信：节点之间的通信采用异步方式，发送请求后可以继续执行其他操作，不需要等待响应返回。4.2 合理划分节点角色4.2.1 主节点（Master）：主节点负责集群的管理和元数据存储，如索引的创建、删除、分片分配等操作。为了确保集群的稳定性，应该选择性能稳定、网络连接良好的节点作为主节点。一般建议将主节点与数据节点分离，避免主节点因处理大量的数据存储和查询任务而负载过重。例如，在一个大规模的日志分析集群中，可以选择 3 - 5 个配置较高的节点作为master candidate 主节点候选节点，并且这些节点最好分布在不同的机架或数据中心，以防止单点故障。同时，合理设置最小主节点，通过minimum\_master\_nodes 完成。minimum\_master\_nodes 设置及其重要，为了防止集群脑裂，这个参数应该设置为法定个数就是 ( master 候选节点个数 / 2) + 1。minimum\_master\_nodes的定义与作用minimum_master_nodes是 Elasticsearch 集群中的一个重要设置，用于定义在集群中可以正常选举主节点（Master）的最少候选主节点数量。minimum\_master\_nodes 的主要目的是防止集群出现 “脑裂（split - brain）” 现象。脑裂是指在集群中，由于网络分区或其他原因，导致多个节点分别认为自己是主节点，从而使集群分裂成多个独立的部分，这会导致数据不一致和集群的混乱。通过设置minimum_master_nodes，可以确保在选举主节点时，只有当足够数量的候选主节点能够相互通信时，才能成功选举出主节点，从而避免脑裂情况的发生。计算minimum\_master\_nodes的合适值计算minimum_master_nodes的公式为：(master_eligible_nodes / 2) + 1，其中master_eligible_nodes是集群中具有主节点选举资格的节点数量。例如，在一个有 5 个具有主节点选举资格的节点的集群中，minimum_master_nodes的值应该是(5 / 2) + 1 = 3（向上取整）。这意味着在进行主节点选举时，至少需要 3 个候选主节点能够相互通信，才能成功选举出主节点。4.2.2 数据节点（Data）的配置：数据节点主要负责存储和索引数据，它们会占用大量的磁盘空间和 I/O 资源。根据数据量和查询负载来合理分配数据节点的数量。如果数据量增长迅速或者查询并发量较高，可以适当增加数据节点。同时，要注意数据节点的硬件配置，如配备足够的内存来缓存索引数据，使用高性能的磁盘（如 SSD）来提高数据读写速度。除此之前，数据节点（Data）的配置的配置，和数据规模有关。预估存储一个亿文档的 Elasticsearch 数据节点数量需要综合多方面因素考虑，以下是一些分析：第一，评估数据量与存储单个文档大小估算：假设每个文档平均大小为 1KB，那么一亿个文档大约需要 100GB 的存储空间。如果考虑副本以及索引的额外开销，实际所需空间会更大。节点存储容量规划：通常情况下，一个数据节点的磁盘容量建议不超过其总容量的 70% 至 80%，以预留一定空间用于数据的临时写入和系统文件存储等。如果使用普通机械硬盘，每个节点的存储容量可以控制在 2TB 至 3TB 左右；如果是 SSD 硬盘，可根据其性能和成本适当增加存储容量，但也不建议超过单节点 5TB 左右的存储量。第二，规划分片与副本分片数量：根据经验，分片大小保持在 10GB 至 50GB 之间较为合适。对于一亿个文档（ 100GB），若按每个分片 30GB 左右估算，大约需要 4 至 5 个主分片。同时，还需要考虑副本数量，通常副本数量可以设置为 1 至 2 个，以提高数据的可用性和查询性能。节点承载分片数：每个数据节点能够承载的分片数量也有限制，官方一般建议每 GB 堆内存对应少于 20 个分片。其实，官方的这个建议，这个其实性能很低的。为什么呢 ？按照官方建议，一个具有 32GB 堆内存的数据节点，最多可承载 640 个分片左右， 对应到的磁盘空间 640 *30G =19200G 。假设缓存16G，对19200G 的数据进行缓存，当然 这么少的内存，这么大规模的数据，这个内存的缓存的作用，可以说聊胜于无，没啥命中率的。如果估算数据节点数怎么估算较好呢？尼恩的经验， 可以按照 内存需求，反向计算节点数。一般来说， 最好有磁盘空间的30%以上的内存空间，作为 堆内存。当然，这个比例越高，性能越好。比如有磁盘空间的50%以上的内存空间， es的性能越高。那么， 一亿个文档（ 100GB），加速副本最少 200G， 最好有60个G的内存。假设一个节点 16G内存， 那么 需要 5个节点。当然，如果有10个节点， 性能会更好。4.2.3 协调节点（Coordinating）的配置：协调节点负责接收用户查询请求，并将请求分发到合适的数据节点进行处理，然后汇总结果返回给用户。协调节点需要有良好的网络和 CPU 性能，以快速地转发和处理查询。在集群中，可以根据查询流量来设置协调节点的数量，一般可以按照一定的比例（如每 10 个数据节点配置 2 - 3 个协调节点）来配置。4.3 ES 数据节点的写入调优ES 数据写入具有一定的延时性，这是为了减少频繁的索引文件产生。另外，在搜索引擎的业务场景下，用户一般并不需要那么高的写入实时性。比如 在网站发布一条征婚信息，或者二手交易平台发布一个商品信息。其他人并不是马上能搜索到的，这其实也是正常的处理逻辑。默认情况下 ES 每秒生成一个 segment 文件，当达到一定阈值的时候 会执行merge，merge 过程发生在 JVM中，频繁的生成 Segmen 文件可能会导致频繁的触发 FGC，导致 OOM。为了避免避免这种情况，通常采取的手段是降低 segment 文件的生成频率，手段有两个一个是 增加时间阈值，另一个是增大 Buffer的空间阈值，因为缓冲区写满也会生成 Segment 文件。生产经常面临的写入可以分为两种情况：高频低量：高频的创建或更新索引或文档一般发生在 处理 C 端业务的场景下。低频高量：一般情况为定期重建索引或批量更新文档数据。4.3.1 写入优化的目标写入调优均，常常以提升写入吞吐量和并发能力为目标，而非提升写入实时性。4.3.2.增加 flush 时间间隔，目的是减小数据写入磁盘的频率，减小磁盘IO频率。在 Elasticsearch 中，flush操作主要是将索引缓冲区（Index Buffer）中的数据刷新到磁盘上的索引文件中。当新的数据写入 Elasticsearch 时，会先存储在内存的索引缓冲区中。flush操作会将这些缓冲区中的数据持久化，以确保数据的安全性和可恢复性。同时，flush操作还会更新索引的相关元数据，如提交点（Commit Point）等。通过配置文件修改flush时间间隔在 Elasticsearch 的elasticsearch.yml配置文件中，可以找到与flush相关的参数。主要参数是index.translog.flush_threshold_period，它用于控制flush的时间间隔。这个参数的默认值是30s，表示每隔 30 秒会自动进行一次flush操作。修改时间间隔要增加flush时间间隔，例如将其设置为60s，可以在elasticsearch.yml配置文件中添加或修改以下内容：index.translog.flush_threshold_period: 60s修改完成后，保存配置文件并重启 Elasticsearch 服务，新的flush时间间隔就会生效。尼恩提示，增加flush时间间隔意味着数据在内存缓冲区中停留的时间更长。如果在这段时间内发生服务器故障或 Elasticsearch 进程意外终止，可能会导致缓冲区中的数据丢失。因此，在增加flush时间间隔的同时，需要权衡数据安全性和性能之间的关系。如果数据的可靠性要求极高，不建议过长地延长flush时间间隔。4.3.3 增加refresh_interval的参数值在 Elasticsearch 中，refresh_interval是一个用于控制索引数据可见性的重要参数。它定义了索引数据被刷新（Refresh）的时间间隔。当新的数据写入索引后，并不是立即可以被搜索到，而是要等待索引被刷新。刷新操作会使新写入的数据对搜索可见。具体来说，Elasticsearch 会在内存中维护一个数据结构来存储索引数据，这个数据结构在刷新之前是不断更新的。refresh 刷新操作， 会将这个内存中的数据结构更新到磁盘上的索引文件（倒排索引等）中，并且更新相关的搜索视图，使得新数据能够被查询到。增加refresh_interval的参数值 目的，是减少segment文件的创建，减少segment的merge次数，merge是发生在jvm中的，有可能导致full GC，增加refresh会降低搜索的实时性。ES的 refresh 行为非常昂贵，并且在正在进行的索引活动时经常调用，会降低索引速度。refresh\_interval的默认值和取值范围默认值：默认的refresh_interval是1s。这意味着每隔 1 秒，索引会被刷新一次，新写入的数据会在 1 秒后对搜索可见。取值范围：可以设置为一个时间值，如-1（表示禁用自动刷新，需要手动刷新）、30s、1m等。时间单位可以是秒（s）、分钟（m）、小时（h）等。默认情况下，Elasticsearch 每秒（1s ） 定期刷新索引，但仅在最近 30 秒内收到一个或多个搜索请求的索引上。如果没有搜索流量或搜索流量很少（例如每 5 分钟不到一个搜索请求）， 并且想要优化索引速度，这是最佳配置。此行为旨在在不执行搜索的默认情况下自动优化批量索引。建议显式配置此配置项，如 30秒。如何修改refresh\_interval？ 通过配置文件修改，在elasticsearch.yml配置文件中，可以通过添加或修改以下内容来设置refresh\_intervalindex.refresh_interval: 30s这会将所有新创建的索引的刷新间隔设置为 30 秒。如果只想对特定的现有索引进行设置，可以使用索引设置 API。发送一个 PUT 请求到http://<es - node - ip>:9200/<index - name>/_settings，其中是要修改的索引名称。请求体如下：{ "index": { "refresh_interval": "30s" } }这种方式可以在不重启 Elasticsearch 的情况下，动态地修改特定索引的刷新间隔。延长refresh_interval可以提高索引的写入性能。因为刷新操作是一个相对较重的操作，它涉及到内存数据结构到磁盘索引的更新以及搜索视图的更新。减少刷新频率可以减少磁盘 I/O 和 CPU 的消耗，使得更多的资源可以用于数据写入。例如，在大数据量的批量写入场景下，将refresh_interval延长到 30 秒或更长，可以显著提高写入速度。但是，延长refresh_interval会导致新数据对搜索可见的延迟增加。如果应用场景对数据的实时性要求很高，如实时搜索系统，可能需要谨慎考虑延长刷新间隔的影响。4.3.4 写入的时候， 增加Buffer大小本质也是减小refresh的时间间隔，因为导致segment文件创建的原因不仅有时间阈值，还有buffer空间大小，写满了也会创建。默认最小值 48MB< 默认值 JVM 空间的10% < 默认最大无限制4.3.5 写入的时候， 关闭副本当需要单次写入大量数据的时候，建议关闭副本，暂停搜索服务，或选择在检索请求量谷值区间时间段来完成。第一是减小读写之间的资源抢占，读写分离第二，当检索请求数量很少的时候，可以减少甚至完全删除副本分片，关闭segment的自动创建以达到高效利用内存的目的，因为副本的存在会导致主从之间频繁的进行数据同步，大大增加服务器的资源占用。具体可通过则设置index.number_of_replicas 为0以加快索引速度。没有副本意味着丢失单个节点可能会导致数据丢失，因此数据保存在其他地方很重要，以便在出现问题时可以重试初始加载。初始加载完成后，可以设置index.number_of_replicas改回其原始值。4.4 数据分片设置4.4.1 数据分片的预估ES一旦创建好索引后，就无法调整分片的设置，而在ES中，一个分片实际上对应一个lucene 索引，而lucene索引的读写会占用很多的系统资源，因此，分片数不能设置过大；所以，在创建索引时，合理配置分片数是非常重要的。一般来说，我们遵循一些原则：控制每个分片占用的硬盘容量，不超过ES的最大JVM的堆空间设置（一般设置不超过32G，参加上文的JVM设置原则），因此，如果索引的总容量在500G左右，那分片大小在16个左右即可。一般都设置分片数不超过节点数的3倍。考虑一下node数量，一般一个节点有时候就是一台物理机，如果分片数过多，大大超过了节点数，很可能会导致一个节点上存在多个分片，一旦该节点故障，即使保持了1个以上的副本，同样有可能会导致数据丢失，集群无法恢复。所以， 一般都设置分片数不超过节点数的3倍。4.4.2 分片分配策略ES使用数据分片（shard）来提高服务的可用性，将数据分散保存在不同的节点上以降低当单个节点发生故障时对数据完整性的影响，同时使用副本（repiica）来保证数据的完整性。关于分片的默认分配策略，在7.x之前，默认5个primary shard，每个primary shard默认分配一个replica，即5主1副，而7.x之后，默认1主1副ES的 分片分配策略，大致如下：ES在分配单个索引的分片时会将每个分片尽可能分配到更多的节点上。但是，实际情况取决于集群拥有的分片和索引的数量以及它们的大小，不一定总是能均匀地分布。Paimary只能在索引创建时配置数量，而replica可以在任何时间分配，并且primary支持读和写操作，而replica只支持客户端的读取操作，数据由es自动管理，从primary同步。ES不允许Primary和它的Replica放在同一个节点中，并且同一个节点不接受完全相同的两个Replica同一个节点允许多个索引的分片同时存在。4.4.3 分片的数量避免分片过多：大多数搜索会命中多个分片。每个分片在单个 CPU 线程上运行搜索。虽然分片可以运行多个并发搜索，但跨大量分片的搜索会耗尽节点的搜索线程池。这会导致低吞吐量和缓慢的搜索速度。分片越少越好：每个分片都使用内存和 CPU 资源。在大多数情况下，一小组大分片比许多小分片使用更少的资源。4.4.4 分片的大小决策分片的合理容量：10GB-50GB。虽然不是硬性限制，但 10GB 到 50GB 之间的分片往往效果很好。根据网络和用例，也许可以使用更大的分片。在索引的生命周期管理中，一般设置50GB为单个索引的最大阈值。堆内存容量和分片数量的关联：小于20分片/每GB堆内存，一个节点可以容纳的分片数量与节点的堆内存成正比。例如，一个拥有 30GB 堆内存的节点最多应该有 600 个分片。如果节点超过每 GB 20 个分片，考虑添加另一个节点。
一、前言相信对于 Nginx 你一定并不陌生，它是一款轻量级的开源 Web 服务及代理程序，因其轻量化、支持高并发等特性，目前国内企业早已广泛的使用 Nginx。既然 Nginx 使用这么广泛，那么我们在性能测试工作中如何对其进行优化和配置便成了重中之重。二、配置优化2.1 并发处理架构优化2.1.1 工作进程配置现代 CPU 多核架构下，Nginx 的 worker 进程若在不同核心间频繁调度，会增加 CPU 上下文切换损耗。CPU 亲和性通过将 worker 进程固定到特定核心，避免跨核调度，提升缓存利用率。所以就需要配置 Nginx 的 CPU 亲和性来解决这个问题。那由于 Nginx CPU 亲和性配置本身有多套配置方案，这里推荐你直接将配置项设置成 auto（worker\_cpu\_affinity ），即采用了 Nginx 推荐的 CPU 绑核策略方式。另外的一个方式是手动绑定，将 worker 线程数量与 CPU 核心数一一绑定方式设置。我们设置成 auto Nginx 会自动识别并按照推荐策略来分配 worker 线程和 CPU。配置方案：自动绑定（推荐）：通过 worker_cpu_affinity auto 让 Nginx 自动识别 CPU 核心并按最优策略绑定。例如 8 核 CPU 会将 8 个 worker 进程一对一绑定到 CPU0\~CPU7，实现负载均衡。手动绑定：按核心数显式配置，如 worker_cpu_affinity 0001 0010 0100 1000（4 核场景）。配置示例如下：worker_cpu_affinity auto; # 自动将工作进程绑定到可用的 CPU 核心 worker_processes auto; # 基于CPU核心数动态分配工作进程，充分利用多核性能 worker_connections 1024; # 单进程最大并发连接数，支持高并发场景 worker_rlimit_nofile 65535; # 单进程文件描述符上限，突破系统资源限制 理论并发能力： 通过多进程模型实现水平扩展，总并发量为：worker\_processes × worker\_connections （例：4 核 CPU 配置下可达 4×1024=4096 并发连接）。需结合系统级限制（如 nofile）。若未调整 sysctl，实际并发可能受限于系统默认值（通常 1024）。# 系统级文件描述符限制 worker_rlimit_nofile 65535; # 需在系统中同步调整（/etc/security/limits.conf）2.1.2 事件驱动模型Linux 系统下一切皆文件，比如打开一个设备，它便会产生一个文件描述符。在产生一个进程时，这个进程便需要一个进程描述符，这个进程描述符也是一个文件。所以在 Nginx 处理请求的时候，每一个请求都会产生处理请求的描述符。其次，在 Nginx 处理大规模请求的时候，为了提高并发效率需要采用异步非阻塞模型，epoll 本身是以异步非阻塞模型来处理请求流中的事件流。这里还需要注意一点，并不是所有的 Linux 操作系统都可以使用 epoll，它是在 kernel 2.6 版本以后提出的，早期内核使用的 select\poll 模型，select 模型比 epoll 模型性能要低很多。模型对比：epoll（Kernel 2.6+）：异步非阻塞模型，基于事件驱动，通过 mmap 共享用户态与内核态空间，突破 select/poll 的 1024 文件描述符限制，支持百万级并发。select/poll：早期模型，需轮询扫描所有文件描述符，性能随并发量下降明显。配置示例如下：multi_accept on; # 一次性接受所有新连接 use epoll; # Linux高效IO多路复用，支持C10K级别并发 2.2 传输效率优化2.2.1 零拷贝技术Nginx 在处理文件时，会将文件传入操作系统内核态的 Buffer Cache，然后传递到操作系统上层的用户态，经用户态的 Buffer Cache 再传回内核态中，最后通过 Socket 将文件转发出去。通过 sendfile on 实现内核态直接传输，避免文件在用户态与内核态间的冗余拷贝。传统流程需 4 次拷贝（内核 Buffer→用户 Buffer→内核 Buffer→Socket），零拷贝仅需 2 次内核态数据搬运，尤其适合图片、视频等静态资源。配置示例如下：# I/O优化 - 减少带宽消耗和提高传输效率 sendfile on; # 开启零拷贝文件传输，减少CPU消耗和内核上下文切换 tcp_nopush on; # 优化数据包传输，减少网络报文数量，配合sendfile使用 tcp_nodelay on; # 禁用Nagle算法，降低小数据包传输延迟2.2.2 长连接复用配置示例如下：# 连接优化 - 减少TCP连接建立的开销 keepalive_timeout 65; # 保持客户端连接超时时间(秒)，减少重复的TCP握手 keepalive_requests 100; # 在一个keepalive连接上可处理的最大请求数2.3 缓存体系构建用到缓存优化主要期望提高网站服务访问效率，减少服务端的性能使用。这里缓存配置优化大致可以分为几个部分，分别是文件描述符缓存、代理缓存优化、静态资源缓存优化等。2.3.1 文件描述符缓存打开文件缓存设置在 Nginx 端，通常而言我们会把一些静态元素（如：JPG、CSS、GS）在代理端通过这种方式进行设置，这里 Nginx 缓存的是静态元素的元数据。元数据的作用就是缓存打开用户所请求的静态元素的文件路径等信息，那么如果在本地频繁地查找之前请求过的静态元素文件而没有缓存元数据时效率比较低，而如果我们把一部分索引数据缓存到 Nginx 的 Cache 下，这种频繁访问就可以很大地提高访问效率。配置示例如下：# 缓存优化 - 减少重复读取文件的开销 open_file_cache max=10000 inactive=20s; # 缓存最多10000个文件描述符，20秒不使用则关闭 open_file_cache_valid 30s; # 每30秒检查一次缓存中的文件描述符是否仍然有效 open_file_cache_min_uses 2; # 文件被访问至少2次后才会被缓存，降低低频访问文件的缓存 open_file_cache_errors on; # 同时缓存文件错误信息，避免重复访问不存在的文件2.3.2 代理缓存反向代理场景下缓存后端服务响应，支持 UWSGI、FastCGI 等协议。首先通过 Nginx 作代理，可以支持实现动静态的分离，静态元素直接交给 Nginx 来处理，再把后端动态数据适当作缓存。通常做这种代理+缓存的架构，是能有效地提高整体网站的访问并发性能。配置示例如下：# 代理缓存 - 减少对后端服务的请求 proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m location / { proxy_cache my_cache; … }参数说明：proxy\_cache\_path 表示在本地分配哪个路径来存储并缓存后台返回数据；cache levels 表示存放文件的分层方式；my\_cache:10m max\_size=10g 分别表示是开辟一个名为my\_cache共享块（用于统计访问次数）及缓存的单个文件的最大大小等。这些都是做对应的缓存在本地的文件目录相关属性的一些设置。另外一块的设置表示在proxy\_cache 的时候，通过 location 引用到 cache 的名称。2.3.3 静态资源缓存通常可以把静态元素，比如用户请求的图片、CSS 、JS 等元素缓存到客户端。这种缓存可以通过 Nginx配置中的 expires 配置项进行设置，expires 后面可以加具体的时间，也可以加对应的特定意义的数值，比如 -1 表示不缓存资源，max 设置最大周期缓存（默认缓存周期为 10 年），需要做具体的时间的设置可以写入具体的时间周期，比如一个小时或是一天。配置示例如下：# 静态资源缓存配置 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { # 匹配静态资源文件扩展名 expires 30d; # 客户端缓存30天 add_header Cache-Control "public"; # 允许所有缓存服务器缓存 access_log off; # 减少日志噪音 }在缓存整体使用中，虽缓存越多、越靠前、命中率越高越好，但需综合考量并注意实际问题：一是文件更新策略问题，配置文件后端缓存策略时，要考虑缓存删除与更新策略，确保后端数据更新能被前端用户及时感知；二是缓存命中率失败给后端造成的瞬间压力，前端缓存失效等情况会给后端带来瞬间压力，网站设计时需考虑如何避免缓存失效及缓存失效时保证后端服务高可用；三是多节点缓存一致性，做缓存设置时，当前端多节点保存相同内容，需考虑如何保证数据一致性，涉及缓存架构设计及前端缓存节点更新策略。2.4 协议层深度优化2.4.1 HTTP/2 支持HTTP/2 作用与优势：多路复用 (Multiplexing)：允许在单个 TCP 连接上并行传输多个请求/响应，彻底解决 HTTP/1.x 的队头阻塞问题，降低延迟并提升页面加载速度。头部压缩 (HPACK)；使用二进制格式和静态字典压缩 HTTP 头部，减少冗余数据传输（典型场景可节省 50% 的头部大小）。服务器推送 (Server Push)：可主动向客户端推送关键资源（如 CSS/JS），无需等待 HTML 解析，优化首次渲染时间。流量优先级控制：允许标记资源优先级（如优先加载首屏内容），提升用户体验。http2 on; # 启用HTTP/2多路复用 注意：HTTP/2 必须基于 HTTPS，需先配置 SSL 证书。主流浏览器（Chrome/Firefox/Edge）均支持，但旧客户端（如 IE11）自动回退到 HTTP/1.x。2.4.2 TLS优化建议禁用不安全协议：TLSv1.0 和 TLSv1.1 存在已知漏洞（如 POODLE、BEAST），必须禁用。 另外，TLSv1.3 优势：更少握手次数（1-RTT 或 0-RTT），降低延迟。支持现代加密算法（如 ChaCha20-Poly1305），安全性更高。AES-GCM：硬件加速支持好，适合现代 CPU。ssl_protocols TLSv1.2 TLSv1.3; # 仅允许安全协议版本 ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256"; # 高效加密套件 ssl_session_cache shared:SSL:10m; # 10MB共享内存缓存SSL会话 ssl_session_timeout 10m; # 会话超时时间 2.5 资源处理优化Nginx 基础配置优化的最后一项是文件压缩，我们希望做到 Nginx 服务端往客户端发送的数据越小，占用的延迟越低用户体验便会越好。所以往往在代理或 Nginx 中会设置文件压缩，我们通过 gzip 压缩文本类资源（HTML/CSS/JS），降低带宽占用。典型配置如下：2.5.1 压缩与大文件支持 # Gzip压缩配置 - 显著减少传输数据量，节省带宽 gzip on; # 启用gzip压缩，可减少传输数据量40%-70% gzip_comp_level 6; # 压缩级别(1-9)，一般来说推荐你设置成 6 就比较合适 gzip_min_length 256; # 最小压缩文件大小(字节)，小于此值的文件不压缩 gzip_proxied any; # 对所有代理请求进行压缩，包括反向代理的响应 gzip_vary on; # 添加Vary: Accept-Encoding响应头，正确处理缓存 gzip_http_version 1.1; # 使用HTTP 1.1版本，支持gzip压缩 gzip_types text/plain # 压缩纯文本 text/css # 压缩CSS text/xml # 压缩XML text/javascript # 压缩JS application/javascript # 压缩JS application/json # 压缩JSON application/xml # 压缩XML application/xml+rss # 压缩RSS image/svg+xml # 压缩SVG图片 font/eot # 压缩EOT字体 font/otf # 压缩OTF字体 font/ttf # 压缩TTF字体 application/octet-stream; # 压缩二进制文件流 gzip_disable "MSIE [1-6]\."; # 对IE6及以下浏览器禁用gzip，避免兼容性问题 gzip_static on; # 优先使用预压缩.gz文件，减少CPU使用率2.5.2 大文件支持若业务需要用户上传大文件（如视频、高清图片），若 API 接口接收 JSON/POST 数据（如大数据分析场景），需根据需求调整限制。client_max_body_size 500M; # 允许上传500MB文件 2.6 内存管理优化2.6.1 缓冲区与共享内存HTTPS 建连需多次加密协商，通过ssl\_session\_cache缓存会话密钥（SessionKey），减少重复握手。gzip_buffers 16 8k; # 128KB压缩缓冲区 proxy_buffering off; # 关闭代理缓冲，实时传输 ssl_session_cache shared:SSL:10m; # SSL会话共享内存 ssl_session_timeout 10m; # 设置 SSL 会话超时时间为 10 分钟通过在 Nginx 中添加 ssl\_session\_cache 配置，配置中分配 Nginx 在处理 SSL 会话所需要开辟的共享内存的空间，我这里这里设置值为 10 MB，第二个参数就是设置 SSL SessionKey 的超时时间，这里设置的为 10 分钟，也就是每隔 10 分钟需要重新再进行一次建联，这是一个在服务端的超时时间。三、总结通过以上配置，我们能达到以下目标：1.并发能力：支持 worker\_processes × worker\_connections 的并发连接（如4核CPU：4×1024=4096并发）。2.传输效率：通过HTTP/2多路复用、零拷贝、长连接复用，减少50%以上的网络延迟。3.缓存命中率：静态资源缓存30天，文件描述符缓存减少 80 %的磁盘IO。4.性能平衡：SSL会话复用降低 50 %握手开销。终极目录即构建毫秒级响应、C10K级别（万级并发）并发处理能力，适用于高并发、高安全、低带宽要求的Web服务场景。