是时候用 ES 拯救发际线啦

那些年,我被 Elasticsearch 性能坑过的日子

默认分类 | 作者 search_engineer | 发布于2 小时前 | | 阅读数:41

那些年,我被 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 调优没有银弹,但有几个原则:

  1. 内存给够,但别超过 32G(不然半夜被叫起来)
  2. 分片别太多,也别太少(3-5 个一般够用)
  3. 深分页用 search_after(别用 from + size)
  4. 批量导入时关掉 refresh(速度提升 10 倍)
  5. 别用 wildcard,用 ngram(保住饭碗)

踩过这些坑,才算真正入门了 ES。

有问题评论区交流,我继续去调我的集群了。如果服务器又挂了,记得帮我打 120。


文 / 一个被 ES 坑过无数次的工程师 P.S. 我的头发还在,只是不多了


[尊重社区原创,转载请保留或注明出处]
本文地址:http://searchkit.cn/article/15687


0 个评论

要回复文章请先登录注册