四小时还没跑完的全量同步
读和查都稳了,老王迎来了写入大考:把 500 万条商品从 MySQL 全量同步进 ES,单条 index 请求一把梭,跑了四个小时进度条还在半山腰,集群 CPU 倒是不高——瓶颈根本不在算力,在请求方式。写入调优有三件套:bulk 批量、副本归零、刷新暂停。三招下去,同样的活跑进五十分钟。
第一件:bulk,把路费摊薄
单条 index 请求,每条都要走一遍网络往返、解析、路由。bulk 把几百上千条打成一个请求,路费一次付清。经验值:单批体积控制在 5 到 15 MB,条数几百到一两千,超过这个量级收益递减还容易把节点内存打爆。先拿一百条、五百条、一千条压一压,看吞吐曲线再定批大小,比抄任何博客都靠谱。
bulk 的返回值必须逐条检查:items 数组里每条有自己的 status,部分失败不影响其他条目。失败的捞出来单独重试,重试前想想第 4 篇的教训——带业务主键写入,重试天然幂等,不会产生重复文档。
第二件:导入期副本归零
第 3 篇说过副本是高可用的底气,但每写一条主分片,都要等副本同步完才返回——副本在导入期就是纯拖累。批量导入的正确姿势:
导入前:PUT /product/_settings
{ "number_of_replicas": 0 }
导入后:PUT /product/_settings
{ "number_of_replicas": 1 }副本数是动态设置(第 3 篇讲过主分片数才定死),改回去之后 ES 自动开始复制,yellow 转 green。同理 refresh:导入前把 refresh_interval 设 -1 暂停刷新(第 6 篇的原理正好用上),导完 POST _refresh 再恢复 30s 或 1s——一秒一次 refresh 意味着一秒一个小段,段多了后续合并也是负担,暂停刷新等于让数据憋一口气进大段。
translog 的取舍:快与不丢只能选一头
还有一道深水闸:index.translog.durability。默认 request,每条请求都 fsync,最安全但最慢;改成 async,后台按间隔刷盘,写入明显提速——代价是节点宕机时可能丢最近几秒的数据。场景决定选择:全量重建索引丢了就重来,async 随便开;增量同步实时数据,老老实实 request。老王的方案是重建期 async,建完恢复,两头都占。
写入调优清单
| 动作 | 收益 | 注意 |
|---|---|---|
| bulk 批量写 | 摊薄网络与解析开销 | 5-15MB 一批,逐条检查 errors |
| 导入期副本归零 | 少写一半以上的副本流量 | 导完恢复,等 green 再切流量 |
| 导入期关刷新 | 减少小段生成与合并 | 导完手动 _refresh 再恢复 |
| translog 改 async | 省每条请求的 fsync | 可能丢几秒数据,只用于可重跑场景 |
| 带业务主键写入 | 重试幂等,不产重复文档 | 比 ES 自动生成 id 好(随机 id 无点查加速) |
还有一条隐形纪律:大批量导入期间别让深度查询和重量级聚合同时跑,读写抢资源谁也快不了。老王的同步任务后来挪到了凌晨低峰,写入窗口独占集群。
小结
写入调优一句话:bulk 摊路费,副本先归零,刷新憋口气,translog 按场景松闸。三板斧都是动态设置,随时可调可回滚,比改架构便宜一万倍。
单机调优到头了,老王开始担心另一件事:三节点集群挂一个会怎样?分片怎么搬、主节点怎么选?下一篇讲集群:节点角色、主选举与 split-brain 的防线上。
评论 (0)