那些年,我被 Elasticsearch 性能坑过的日子(血泪史)
做搜索开发这几年,跟 ES 打交道的时间比跟女朋友还多(好吧,我承认我没有女朋友 😭)。今天分享几个被坑惨的经历,希望能帮大家少走弯路。
坑一:内存配错了,半夜三点被老板电话叫醒
刚开始用 ES,服务器 64G 内存,我想着:内存越大越好,直接给堆内存配了 56G!
当时还美滋滋地想:这下查询肯定飞快。
结果?查询慢得像蜗牛爬,还经常 OOM(内存溢出)。最惨的是,半夜三点服务器挂了,老板打电话来:"怎么回事?用户投诉搜不出东西了!"
我迷迷糊糊爬起来查日志,发现 GC 时间长得离谱,每次 GC 都要几秒。
后来查资料才明白:堆内存不能超过 32G,超过这个阈值,Java 的指针就不压缩了,反而更慢。
正确姿势:
-Xms30g
-Xmx30g
剩下的 34G 给 Lucene 做文件缓存,这才是亲儿子。改完之后,查询速度直接翻倍,我也能睡个好觉了。
坑二:分片数乱设,查询等到用户怀疑人生
有个项目,100G 数据,我设了 50 个分片,每个 2G。
心想:分片多,查询并行度高,肯定快!
上线后,用户反馈:搜个东西要 10 秒钟?
我:???
查了半天,发现 50 个分片要跨网络合并结果,开销爆炸。就像你问 50 个人问题,然后要把所有人的回答汇总,这能不慢吗?
后来改成 3 个分片,查询降到 200ms,用户终于不骂娘了。
经验公式:分片数 = 数据量(GB) / 30
- 100G 数据 → 3-4 个分片
- 1TB 数据 → 30-35 个分片
别整太多小分片,查询时会哭的。
坑三:深分页,把服务器查挂了(最贵的一课)
产品说要做"加载更多",我用 from + size 实现:
{
"from": 10000,
"size": 10
}
用户点了几十页后,服务器直接挂了。查日志发现,from=10000 时,ES 要扫描 10010 个文档,然后扔掉前 10000 个,只返回最后 10 个。
这操作太骚了!就像翻书,你不是记住看到哪了,而是每次都从第一页开始翻,翻到第 100 页,然后只看最后一行。
CPU 和内存直接爆炸,服务器说:"我不干了!"
正确做法是用 search_after:
{
"size": 10,
"sort": [{"date": "desc"}],
"search_after": ["2024-01-01"]
}
像翻书一样,记住上次看到哪了,下次从那里继续翻。改完之后,深分页查询从 10 秒降到 50ms。
坑四:刷新间隔没调,导入数据慢如龟速
批量导 1 亿条数据,预计 2 小时,结果跑了 2 天还没跑完。
我:???这什么鬼?
查监控发现,磁盘 IO 一直 100%,CPU 却没怎么动。原来是 refresh 的锅。
ES 默认 1 秒刷新一次,每次刷新都要生成新段(Segment),写磁盘。1 亿条数据,每秒刷新,这磁盘不得写废了?
导入数据前关掉 refresh:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "-1"
}
}
导完再开回来:
PUT /my_index/_settings
{
"index": {
"refresh_interval": "1s"
}
}
速度提升 10 倍,2 小时搞定!
坑五:wildcard 查询,把 CPU 打满(离职警告)
产品要支持模糊搜索,我直接用 wildcard:
{
"query": {
"wildcard": {
"title": "*手机*"
}
}
}
上线当天,CPU 直接 100%,服务挂了,用户群炸了。
我:完了,要提桶跑路了。
wildcard 是全表扫描啊兄弟们!几百万文档一个个匹配,就像你在图书馆找一本书,书名只记得一个字,然后你把所有书都翻一遍。
能不慢吗?
后来改成 ngram 分词,提前把词拆好:
- "手机" → "手"、"机"、"手机"
- "苹果手机" → "苹"、"果"、"手"、"机"、"苹果"、"果手"、"手机"、"苹果手机"
查询时直接匹配,性能提升 100 倍,我也保住了饭碗。
一些实用的监控命令(保命用)
# 看集群健康,绿色最好,黄色警告,红色完蛋
GET /_cluster/health
# 看节点负载,哪个节点在摸鱼
GET /_nodes/stats
# 看热点线程,谁在消耗 CPU
GET /_nodes/hot_threads
# 看慢查询,找出罪魁祸首
GET /_search?profile=true
慢查询日志一定要开:
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
不然出问题都不知道哪句查询慢的,只能抓瞎。
总结(血泪教训)
ES 调优没有银弹,但有几个原则:
- 内存给够,但别超过 32G(不然半夜被叫起来)
- 分片别太多,也别太少(3-5 个一般够用)
- 深分页用 search_after(别用 from + size)
- 批量导入时关掉 refresh(速度提升 10 倍)
- 别用 wildcard,用 ngram(保住饭碗)
踩过这些坑,才算真正入门了 ES。
有问题评论区交流,我继续去调我的集群了。如果服务器又挂了,记得帮我打 120。
文 / 一个被 ES 坑过无数次的工程师 P.S. 我的头发还在,只是不多了
本文地址:http://searchkit.cn/article/15687