前言
ES的官方实验室曾发布过一篇博客,介绍了使ES向量检索性能获得显著提升的技术要点与展望:
- 多线程搜索能力的利用:Lucene 的分段架构允许实现多线程搜索能力。Elasticsearch 通过同时搜索多个段来提高性能,使用所有可用的 CPU 核心的计算能力显著减少了单个搜索的延迟。
- 加速多图向量搜索:通过在邻近图中平衡探索和利用,调整扩展匹配集的大小,控制运行时间和召回率之间的权衡,这对于在多个图中实现最佳搜索性能至关重要。
- 信息交换优化:在多图搜索场景中,通过智能地在搜索之间共享状态,使基于全局和局部竞争阈值的遍历决策更加明智,从而提高搜索效率。
- Java 最新进展的利用:通过 Project Panama Vector API,Java 代码现在可以无缝地与 SIMD 指令交互,解锁矢量引擎。
- 标量量化:Lucene 引入了标量量化技术,这是一种有损压缩技术,可以在不牺牲搜索性能的情况下显著减少内存需求。
- 压缩技术的改进:通过将每个维度从 7 位压缩到 4 位,进一步减少了数据大小,同时保持了搜索结果的准确性。
- 二进制量化的探索:未来可能会将二进制量化技术集成到 Lucene 中,这有可能彻底改变向量存储和检索。
- 多向量集成:Lucene 和 Elasticsearch 通过嵌套字段和连接功能,支持在顶级文档中管理多个嵌套文档,允许跨嵌套文档进行搜索,并随后与它们的父文档连接。
- 预连接优化:在搜索子向量段落时,Lucene 能够在搜索 HNSW 图的同时预先连接到父文档,确保返回的是文档而不是段落,这提高了效率。
这些技术如果得到集成,大部分是对用户透明的,我们今天要说的是在实践层面,对于向量检索有哪些调优考虑。
段合并只有20%的性能提升吗?
在之前的初步测试中,我们已经得出一个确定的结论(ESrally已专门为此设计了测试用例),在测试工具默认参数的情况下,段合并可以带来全面的 20%性能提升(也就是无论其他参数如何变换,QPS和latency性能表现提升趋势接近一致)。
我们观察数据量(约 260 万 96 维数据):
原始文档的大小解压缩后是 20 GB。
将数据写入ES后索引实际占用的存储:合并前约4.5GB,合并后1GB左右。
# 合并前:4.5GB
curl http://localhost:9200/_cat/indices/vectors?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open vectors f4iuM6vjTGiBwz8lofwqFw 2 0 2500000 0 4.5gb 4.5gb 4.5gb
## Running force-merge 100%- 合并后:1GB
curl http://localhost:9200/_cat/indices/vectors?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open vectors f4iuM6vjTGiBwz8lofwqFw 2 0 2500000 0 1gb 1gb 1gb
跟随后续的并发调优,我们可以看出段合并的巨大优势,不止 20%!
注:起初我们以为段合并仅仅会带来20%的提升,虽然稳定但是不高!
并发 QPS 调优
并发10的情况
这是问题的关键,也是长久以来踩坑的点。因为无法直接在命令行修改搜索并发,需要改动源码目录 .rally/benchmarks/tracks/default/dense_vector/challenges 的 default.json:
# 修改示例
{
"name": "knn-search-100-1000_multiple_segments",
"operation": "knn-search-100-1000",
"warmup-iterations": 100,
"clients": 10,
"iterations": 1000
}
测试指令:
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s2-c1-30g-d1-sc10-1.csv --report-format=csv --track-params="bulk_indexing_clients:1" --user-tags="shards:2,clients:1,mem:30g,disk:1,sc:10"
对比基线的全默认值,变量为 sc:10,也就是搜索并发度为 10,这时有10个客户端在同时发起搜索请求。
比较两次测试结果:
./esrally compare --baseline b337fb0a-39cf-4119-abe5-6dd4be745a32 --contender 576b29d8-98a0-458d-8af1-27830068df80 --report-file /home/elastic/dv-shards2-sc10_1.csv --report-format csv
官方比较工具会直接列出差值与百分比提升:
-
knn-search-10-100 +883.02%
-
knn-search-100-1000 +866.39%
-
knn-search-10-100_multiple_segments +496.02%
-
Knn-search-100-1000_multiple_segments +269.07%
在 10 搜索并发的情况下,对比结果显示 QPS 相比默认参数提高数倍。而且,段合并对性能的影响也放大了。
注意:CPU 使用率明显上升了:
此时使用top命令观察到 CPU 总体52.9%使用率, 且单独的 es 进程使用了 2538% 的 CPU。
注意,每轮测试需主动删除 ES 中的测试索引(vectors)!
curl -XDELETE http://localhost:9200/vectors
注:如果不删除的话,下一次测试开始时会打印诸多警告,而这些警告确实是影响测试结果的,一般会让你的结果更差!
继续测试
接下来继续测试搜索并发 20/30/40/50 的情况。
20 search
以下是使用官方对比工具结果的记录(与默认值比):
Mean Throughput,knn-search-10-100_multiple_segments,169.83212959330336,1223.2297240422738,+1053.39759,ops/s,+620.26%
90th percentile latency,knn-search-10-100_multiple_segments,4.8985810310114175,15.743541764095427,+10.84496,ms,+221.39%
99th percentile latency,knn-search-10-100_multiple_segments,6.255753734731111,61.14699723257214,+54.89124,ms,+877.45%
Mean Throughput,knn-search-100-1000_multiple_segments,71.8843832772858,286.788544718593,+214.90416,ops/s,+298.96%
99th percentile latency,knn-search-100-1000_multiple_segments,15.602529068710282,114.20343654463056,+98.60091,ms,+631.95%
Mean Throughput,knn-search-10-100,198.15221100022976,4285.480938609085,+4087.32873,ops/s,+2062.72%
99th percentile latency,knn-search-10-100,3.9556266699219123,3.956923596560939,+0.00130,ms,+0.03%
Mean Throughput,knn-search-100-1000,93.08795806647417,2109.4832627430874,+2016.39530,ops/s,+2166.12%
99th percentile latency,knn-search-100-1000,13.300802457379177,12.577712316997342,-0.72309,ms,-5.44%
30 search
以下是使用官方对比工具结果的记录(与默认值比),后续仅记录代表性的结果:
Mean Throughput,knn-search-10-100,198.15221100022976,5736.259111262101,+5538.10690,ops/s,+2794.88%
Mean Throughput,knn-search-10-100_multiple_segments,169.83212959330336,1253.008883159874,+1083.17675,ops/s,+637.79%
注意到,多段的检索性能已经达到瓶颈了,1223 -> 1253 ops。
40 search
以下是使用官方对比工具结果的记录(与默认值比),30倍提升就在此处:
# 合并段
Mean Throughput,knn-search-10-100,198.15221100022976,6327.666524710088,+6129.51431,ops/s,+3093.34%
# 多段
Mean Throughput,knn-search-10-100_multiple_segments,169.83212959330336,1307.2178700891045,+1137.38574,ops/s,+669.71%
## 延迟
99th percentile latency,knn-search-10-100,3.9556266699219123,8.661079301964493,+4.70545,ms,+118.96%
99th percentile latency,knn-search-10-100_multiple_segments,6.255753734731111,112.7571176411585,+106.50136,ms,+1702.45%
监控数据显示段合并前,cpu 使用率已达 100%,而段合并后的搜索也超过 90%,基本到瓶颈了。
合并后的搜索性能相比合并前提高了380%,远超第一章节测得初始的20%性能提升。
补测 HDD 环境并发
默认条件下的原始数据: Mean Throughput knn-search-10-100 140.1 ops/s,这个是之前进行测试所记录的结果,HDD环境与SSD环境并不是同一台物理机。
# 命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost2:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/hdd_dv_s10-c10-30g-d10-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:10" --user-tags="shards:10,mem:30g,disk:10,sc:40,bc:10"
## 40并发搜索结果摘录
Mean Throughput,knn-search-10-100_multiple_segments,706.64,ops/s
99th percentile latency,knn-search-10-100_multiple_segments,149.85001907974964,ms
Mean Throughput,knn-search-100-1000_multiple_segments,165.78,ops/s
99th percentile latency,knn-search-100-1000_multiple_segments,352.6353229774396,ms
Mean Throughput,knn-search-10-100,3468.38,ops/s
99th percentile latency,knn-search-10-100,19.097675213706708,ms
Mean Throughput,knn-search-100-1000,907.00,ops/s
99th percentile latency,knn-search-100-1000,67.89368528756322,ms
小结1
- 随着搜索并发提高 QPS 线性上升,同时搜索延迟也从个位数毫秒上升到百毫秒级别。
- 在 40 并发搜索的情况下 CPU 使用率已经超过/接近 100%,QPS 达到 6327, 较默认提高 3093.34%。
- 但生产环境中一般不能将 CPU 完全分配给 ES 服务,以免引起其他服务异常。
- 此外,HDD 环境的搜索并发改为 40 后比默认 QPS 提高了 23 倍。
- 段合并前后的性能差异从20%扩大到380%。
单纯 SDD 盘写入性能
之前测试的另一物理机单 HDD 盘的默认写入性能是 4647.58 docs/s。
当前 SSD 环境在默认配置下是 5909.12 docs/s,提高 27%。
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s2-c10-30g-d1-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:10" --user-tags="shards:2,clients:1,mem:30g,disk:1,sc:40,bc:10"
默认并发和多并发
使用了不同的写入并发数,测得单盘峰值:61478.90, docs/s
# 默认
Min Throughput,index-append,5180.31,docs/s
Mean Throughput,index-append,5909.12,docs/s
Median Throughput,index-append,5506.20,docs/s
Max Throughput,index-append,9594.47,docs/s
50th percentile latency,index-append,988.7585119868163,ms
90th percentile latency,index-append,1207.21985619748,ms
99th percentile latency,index-append,1269.2091078124936,ms
100th percentile latency,index-append,1677.7676600031555,ms
# 10 bulk clients
Min Throughput,index-append,36957.48,docs/s
Mean Throughput,index-append,43468.42,docs/s
Median Throughput,index-append,42163.38,docs/s
Max Throughput,index-append,52881.51,docs/s
50th percentile latency,index-append,1249.538094503805,ms
90th percentile latency,index-append,2999.254363036016,ms
99th percentile latency,index-append,6472.7708052354865,ms
100th percentile latency,index-append,6513.733912957832,ms
# 20 bulk clients
Min Throughput,index-append,56487.06,docs/s
Mean Throughput,index-append,61478.90,docs/s
Median Throughput,index-append,61104.85,docs/s
Max Throughput,index-append,68979.58,docs/s
50th percentile latency,index-append,1478.7163800210692,ms
90th percentile latency,index-append,2031.756117020268,ms
99th percentile latency,index-append,3127.242707742844,ms
100th percentile latency,index-append,3859.3457490205765,ms
# 40 bulk clients
Min Throughput,index-append,49535.45,docs/s
Mean Throughput,index-append,60714.67,docs/s
Median Throughput,index-append,58044.39,docs/s
Max Throughput,index-append,83740.28,docs/s
50th percentile latency,index-append,2543.7343135126866,ms
90th percentile latency,index-append,6296.8818135908805,ms
99th percentile latency,index-append,13231.077383063384,ms
100th percentile latency,index-append,13321.979588014074,ms
本机 HDD 数据盘对比
本机(即SSD机器,这台机器上也有HDD数据盘,这样对比是为了消除其他环境因素)的 HDD 盘写入性能是:38947.72 docs/s,而 SSD 是 60714.67 docs/s。
# 修改es配置指定了hdd路径,重启ES,执行命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/hdd_dv_s2-c40-30g-d1-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:40" --user-tags="shards:2,mem:30g,disk:1,sc:40,bc:40"
## 结果摘录
Min Throughput,index-append,30309.16,docs/s
Mean Throughput,index-append,38947.72,docs/s
Median Throughput,index-append,40289.51,docs/s
Max Throughput,index-append,44858.77,docs/s
50th percentile latency,index-append,3803.8593159872107,ms
90th percentile latency,index-append,5961.730462196283,ms
99th percentile latency,index-append,6552.596264737658,ms
100th percentile latency,index-append,9011.191075027455,ms
Mean Throughput,knn-search-10-100_multiple_segments,1202.54,ops/s
Mean Throughput,knn-search-100-1000_multiple_segments,277.04,ops/s
Mean Throughput,knn-search-10-100,6036.20,ops/s
Mean Throughput,knn-search-100-1000,2330.42,ops/s
HDD 环境的最佳数据
单盘:
dv_s2-c40-30g Mean Throughput index-append 43580.20 docs/s
小结2
- 本机一致性环境:SSD 单盘相比 HDD 单盘的写入性能约有 55% 的提高。
- 不同机器比较,单盘最佳写性能 SSD 比 HDD 提高约 40%。
加一块盘如何?
分片数不变,直接加一块数据盘对性能有什么样的影响。注意,测试工具所创建的索引默认就是2分片的,因此我认为,加一块数据盘,也就是2分片分配给2块SSD盘,会带来可观的性能提升。
# 修改为2块ssd盘,执行测试命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/hdd_dv_s2-c40-30g-d2-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:40" --user-tags="shards:2,mem:30g,disk:2,sc:40,bc:40"
# 结果摘录
## 写入
Min Throughput,index-append,50882.36,docs/s
Mean Throughput,index-append,59832.58,docs/s
Median Throughput,index-append,59232.83,docs/s
Max Throughput,index-append,77461.25,docs/s
50th percentile latency,index-append,2605.019590992015,ms
90th percentile latency,index-append,9014.553146681275,ms
99th percentile latency,index-append,10550.434804025572,ms
100th percentile latency,index-append,10557.958395977039,ms
## 检索
Mean Throughput,knn-search-10-100_multiple_segments,1198.33,ops/s
Mean Throughput,knn-search-100-1000_multiple_segments,278.20,ops/s
Mean Throughput,knn-search-10-100,6040.56,ops/s
Mean Throughput,knn-search-100-1000,2362.99,ops/s
由于默认是 2 个分片,加一块 SSD 盘之后理论上写入速度会快?
我们保持了同样的 40 个客户端并发写,然而平均写入速度并没有想当然地变快,相反,还略微下降了:
# 写入略慢
Mean Throughput,index-append,60714.66567557424,59832.581063638034,-882.08461,docs/s,-1.45%
99th percentile latency,index-append,13231.077383063384,10550.434804025572,-2680.64258,ms,-20.26%
# knn搜索几乎不变
Mean Throughput,knn-search-10-100,6026.645170540949,6040.563962284321,+13.91879,ops/s,+0.23%
99th percentile latency,knn-search-10-100,9.546809961320847,8.71230364718941,-0.83451,ms,-8.74%
可以发现,虽然 QPS 没有什么改善,但延迟都取得了一定的下降,尤其写入延迟降低了 20%,因此,我将写客户端数提高 50% 进行观察。
对比结果如下:
Mean Throughput,index-append,60714.66567557424,64879.02964420868,+4164.36397,docs/s,+6.86%
99th percentile latency,index-append,13231.077383063384,14159.762441150962,+928.68506,ms,+7.02%
Mean Throughput,knn-search-10-100,6026.645170540949,6449.086364596615,+422.44119,ops/s,+7.01%
99th percentile latency,knn-search-10-100,9.546809961320847,10.56021787808277,+1.01341,ms,+10.62%
小结3
- 分片数不变(shards = 2)的情况下,两块 SSD 的写入和搜索性能都略微提高(不足 10%)
- 如保持写并发不变,多一块盘使得写入延迟下降了 20%
满盘-SSD x 8
分片数与盘数对齐
资源情况一瞥
下图展示了测试期间cpu使用率几乎接近100%的情况:
并发写客户端数-60
## 命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c60-30g-d8-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:60" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:40,bc:60"
## 结果
Mean Throughput,index-append,60714.66567557424,79766.45483581861,+19051.78916,docs/s,+31.38%
99th percentile latency,index-append,13231.077383063384,3724.1466266277716,-9506.93076,ms,-71.85%
Mean Throughput,knn-search-10-100,6026.645170540949,3377.409548410251,-2649.23562,ops/s,-43.96%
99th percentile latency,knn-search-10-100,9.546809961320847,22.971102662268116,+13.42429,ms,+140.62%
小结 4
满配 8 块 SSD 数据盘 + 8 分片(1:1的完美配置),相比单盘配 2 分片的结果:
- 写入性能仅有 31% 的提升,有点得不偿失了(但延迟下降了大半)。
- 检索性能大幅下降,吞吐量与延迟双双断崖下跌近半(-43%和+140%)。
- 需要注意的是,检索性能的下降并非一无是处,一般这会使得召回率提高。因此分片数多了虽然导致搜索性能下降,在某些情况下,召回可能比延迟更重要。
提高并发写客户端数-80
本参数达到历史峰值:138452.61 docs/s,仅略高于 HDD 环境的 131409.25 docs/s。
## 命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c80-30g-d8-sc40-1.csv --report-format=csv --track-params="bulk_indexing_clients:80" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:40,bc:80"
## 结果
Min Throughput,index-append,49535.45076796103,131766.6760681999,+82231.22530,docs/s,+166.00%
Mean Throughput,index-append,60714.66567557424,138452.61400601084,+77737.94833,docs/s,+128.04%
Median Throughput,index-append,58044.39432179595,138627.72206634138,+80583.32774,docs/s,+138.83%
Max Throughput,index-append,83740.28288657573,144264.3729712853,+60524.09008,docs/s,+72.28%
50th percentile latency,index-append,2543.7343135126866,3871.0951925022528,+1327.36088,ms,+52.18%
90th percentile latency,index-append,6296.8818135908805,4181.484530790476,-2115.39728,ms,-33.59%
100th percentile latency,index-append,13321.979588014074,4343.633758020587,-8978.34583,ms,-67.39%
更高的 100 写并发也测试了,
index-append均值下降到 120729。 此外,HDD环境的实际盘数量会多2块。
80 写并发并提高 bulk_size
结合历史数据来看,bulk size(5000)提高到 8000 或者 10000 都对写入性能没有改善。
# 命令(并发搜索-60:提前修改配置文件)
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c80-30g-d8-sc60-bs8k-1.csv --report-format=csv --track-params="bulk_indexing_clients:80,bulk_size:8000" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:60,bc:80"
## 结果,写入性能下降!
Mean Throughput,index-append,138452.61400601084,130895.65255513952,-7556.96145,docs/s,-5.46%
## 当前搜索并发40->60,ops略高5%,远低于2分片的数据
Mean Throughput,knn-search-10-100,3401.566249965876,3574.8060117824484,+173.23976,ops/s,+5.09%
48 并发写
考虑之前的物理机为 10 块盘,盘均并发为 8,本机为 8 块盘,因此同比例设置为 48。
# 执行命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c48-30g-d8-sc60-2.csv --report-format=csv --track-params="bulk_indexing_clients:48" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:60,bc:48"
## 主要结果
Mean Throughput,index-append,112731.34,docs/s
99th percentile latency,index-append,3039.219787857728,ms
小结 5
- 客户端并发写入数 80 时达到写入峰值 13.8 万文档每秒。
- 无论是 SSD 还是 HDD 环境,提高 bulk size 对性能没有优势。
- SSD 盘均并发数与 HDD 一致时没有取得最高写入性能。
分片数翻倍
通过前面多轮测试,基本可以确定搜索性能随分片数(盘数)增大会下降,这一点可以跟段合并前后的性能表现规律对齐,因为分片多,那么段一定多!
本着拿数据说话的道理,我们还是测试了满盘双倍分片的性能(这也是对标最初单盘 2 分片的默认情况):
# 修改index分片设置为16后,执行:
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s16-c80-30g-d8-sc60-1.csv --report-format=csv --track-params="bulk_indexing_clients:80" --user-tags="shards:16,mem:30g,disk-ssd:8,sc:60,bc:80"
## 主要结果
Mean Throughput,index-append,126140.16,docs/s
99th percentile latency,index-append,3904.6728982008062,ms
Mean Throughput,knn-search-10-100,2324.94,ops/s
99th percentile latency,knn-search-10-100,38.022759984014584,ms
## 对比结果
./esrally compare --baseline 97eb0a41-4653-430a-a6cd-84554830fc87 --contender c988aa85-e28d-49ba-b8b5-68bc53e59e9f --report-file /home/elastic/dv-s8_16d8-sc60-bc80-1.csv --report-format csv
Mean Throughput,index-append,130895.65255513952,126140.15939120237,-4755.49316,docs/s,-3.63%
90th percentile latency,index-append,6992.081789008807,3814.373577013612,-3177.70821,ms,-45.45%
Mean Throughput,knn-search-10-100,3574.8060117824484,2324.937767315463,-1249.86824,ops/s,-34.96%
Mean Throughput,knn-search-100-1000,912.54751058795,544.3382852696755,-368.20923,ops/s,-40.35%
小结6
- 分片数翻倍后,检索性能几乎下降了一半,反映了 knn 检索性能与分片数的反比关系。
- 写入的吞吐量几乎不变,延迟下降了 45%,证明了多个分片处理的独立性。
- 基本可以确定搜索性能随分片数(盘数)增大下降,这可以跟段合并前后的性能表现规律对齐。
其他调优
SSD 的调度算法
官方文档曾注明 SSD 的 I/O 调度算法对性能影响很大,查询如下:
]# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq
这显示已经是合适的算法了,我们更改为另外两种进行测试(下面是Linux终端执行修改的命令)。
# echo cfq >/sys/block/sda/queue/scheduler
# echo cfq >/sys/block/sdb/queue/scheduler
# echo cfq >/sys/block/sdc/queue/scheduler
# echo cfq >/sys/block/sdd/queue/scheduler
# echo cfq >/sys/block/sde/queue/scheduler
# echo cfq >/sys/block/sdf/queue/scheduler
# echo cfq >/sys/block/sdg/queue/scheduler
# echo cfq >/sys/block/sdh/queue/scheduler
CFQ 完全公平队列
## 测试指令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c80-30g-d8-sc60-cfq-1.csv --report-format=csv --track-params="bulk_indexing_clients:80" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:60,bc:80,cfq:1"
## cfq对比deadline模式,指令
./esrally compare --baseline 97eb0a41-4653-430a-a6cd-84554830fc87 --contender a5c626d5-e7e1-4e3c-8e60-1b054c184d90 --report-file /home/elastic/dv-s8d8-sc60-bc80-cfq-1.csv --report-format csv
## 对比结果
Mean Throughput,index-append,130895.65255513952,121986.52869264976,-8909.12386,docs/s,-6.81%
90th percentile latency,index-append,6992.081789008807,5218.91500602942,-1773.16678,ms,-25.36%
Mean Throughput,knn-search-10-100,3574.8060117824484,3639.1262767493613,+64.32026,ops/s,+1.80%
99th percentile latency,knn-search-10-100,32.53638592606883,29.565102203632712,-2.97128,ms,-9.13%
FIFO 队列
## 测试指令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c80-30g-d8-sc60-none-1.csv --report-format=csv --track-params="bulk_indexing_clients:80" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:60,bc:80,none:1"
## noop对比deadline模式,指令
./esrally compare --baseline 97eb0a41-4653-430a-a6cd-84554830fc87 --contender 7aa1cda5-8594-4c2a-b7be-1c7f064fffcb --report-file /home/elastic/dv-s8d8-sc60-bc80-noop-1.csv --report-format csv
## 对比结果
Mean Throughput,index-append,130895.65255513952,135148.53372808752,+4252.88117,docs/s,+3.25%
90th percentile latency,index-append,6992.081789008807,5655.813492514426,-1336.26830,ms,-19.11%
Mean Throughput,knn-search-10-100,3574.8060117824484,3660.2126076340865,+85.40660,ops/s,+2.39%
99th percentile latency,knn-search-10-100,32.53638592606883,29.31073424639186,-3.22565,ms,-9.91%
小结7
- Cfq写入性能:吞吐量与延迟都有小幅度的下降,分别下降了 6.81% 和 25.36%,KNN搜索性能几乎不变
- Noop 写入和搜索性能小幅上升,写入延迟下降 19.11%
- Noop 相比 cfq 的写入吞吐量提高了+10.79%
增大Refresh 间隔
ES会将数据写入到一个缓冲区。但是注意了!此时,缓冲区的内容是无法被搜索到的,它还需要写入到segment里面才可以,也就是刷新到lucence索引里面,这就是refresh动作,默认1秒。
通过 index.refresh_interval 可以修改这个刷新间隔,我们测试改为 5 秒。
测试记录:
## 测试命令
./esrally race --track-path=/root/.rally/benchmarks/tracks/default/dense_vector --target-hosts=localhost:9200 --pipeline=benchmark-only --offline --report-file=/home/elastic/ssd_dv_s8-c80-30g-d8-sc60-r5-noop-1.csv --report-format=csv --track-params="bulk_indexing_clients:80" --user-tags="shards:8,mem:30g,disk-ssd:8,sc:60,bc:80,refresh:5"
## 对比命令(相比默认的刷新间隔1秒)
./esrally compare --baseline 7aa1cda5-8594-4c2a-b7be-1c7f064fffcb --contender a7dba82c-33c5-43ec-b2e3-14a03f9c125b --report-file /home/elastic/dv-s8d8-sc60-bc80-noop_r5-1.csv --report-format csv
## 对比结果
Mean Throughput,index-append,135148.53372808752,115390.67055064092,-19757.86318,docs/s,-14.62%
Mean Throughput,knn-search-10-100,3660.2126076340865,3657.5750445434032,-2.63756,ops/s,-0.07%
Mean Throughput,knn-search-10-100_multiple_segments,652.7557734459474,776.381731501024,+123.62596,ops/s,+18.94%
小结8
刷新间隔提高到 5 秒几乎没有什么收益(但段会减少,多段的检索性能有提高)。
环境信息摘要
CPU参数
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 48
On-line CPU(s) list: 0-47
Thread(s) per core: 2
Core(s) per socket: 12
Socket(s): 2
NUMA node(s): 2
Model name: Intel(R) Xeon(R) Gold 6126 CPU @ 2.60GHz
Stepping: 4
CPU MHz: 1499.976
CPU max MHz: 3700.0000
CPU min MHz: 1000.0000
BogoMIPS: 5200.00
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 19712K
NUMA node0 CPU(s): 0-11,24-35
NUMA node1 CPU(s): 12-23,36-47
内存
# free -g
total used free shared buff/cache available
Mem: 503 3 406 0 93 498
Swap: 3 0 3
存储
# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 3.2T 93G 3.1T 3% /
/dev/sdf1 1.8T 33M 1.8T 1% /es/data6
/dev/sde1 1.8T 33M 1.8T 1% /es/data5
/dev/sdh1 1.8T 33M 1.8T 1% /es/data8
/dev/sdb1 1.8T 33M 1.8T 1% /es/data2
/dev/sdg1 1.8T 33M 1.8T 1% /es/data7
/dev/sdd1 1.8T 33M 1.8T 1% /es/data4
/dev/sdc1 1.8T 33M 1.8T 1% /es/data3
/dev/sda1 1.8T 33M 1.8T 1% /es/data1
/dev/sdk1 3.7T 33M 3.7T 1% /es/data11
/dev/sdi1 3.7T 33M 3.7T 1% /es/data9
/dev/sdj1 3.7T 33M 3.7T 1% /es/data10
# smartctl -i /dev/sda
=== START OF INFORMATION SECTION ===
Device Model: SAMSUNG MZ7LH1T9HMLT-00005
Serial Number: S455NB0MC23058
LU WWN Device Id: 5 002538 e49c3f3a1
Firmware Version: HXT7404Q
User Capacity: 1,920,383,410,176 bytes [1.92 TB]
Sector Sizes: 512 bytes logical, 4096 bytes physical
Rotation Rate: Solid State Device
SATA Version is: SATA >3.1, 6.0 Gb/s (current: 6.0 Gb/s)
调优总结
在针对Elasticsearch(ES)的向量检索性能调优过程中,我们得出了一系列有意义的结论:
首先,随着搜索并发的提升,QPS(每秒查询率)实现了线性增长,但搜索延迟也相应地从毫秒级上升至百毫秒级。在40并发搜索的场景下,CPU使用率接近饱和,QPS高达6327,相较于默认设置有了显著的3093.34%提升。然而,实际生产环境中,需要谨慎分配CPU资源给ES服务,避免对其他服务造成不良影响。
其次,我们观察到在HDD环境下,将搜索并发提高至40后,QPS相较于默认设置提升了23倍。尤为显著的是,通过段合并策略,性能提升幅度从原本的20%扩大至惊人的380%,这充分展现了段合并技术的巨大优势。
在存储介质方面,SSD相比HDD展现出了明显的写入性能优势。无论是本机一致性环境还是跨机器比较,SSD的写入性能均优于HDD,其中本机环境下SSD的写入性能提升约55%,跨机器比较时最佳性能提升约40%。然而,在分片数保持不变(shards = 2)的情况下,增加SSD的数量对写入和搜索性能的提升并不显著,仅为个位数百分比的提升。
在扩展存储能力时,我们注意到增加数据盘数量(即分片数)对写入性能的影响有限,主要体现在延迟的降低上(约45%),而写入吞吐量几乎保持不变。然而,检索性能却随着分片数的增加而显著下降,这反映了KNN检索性能与分片数之间的反比关系。满配8块SSD数据盘和8个分片的情况下,虽然写入性能有31%的提升,但检索性能却大幅下降,吞吐量和延迟均受到严重影响(-43%和+140%)。不过,值得注意的是,这种检索性能的下降在某些情况下可能会带来召回率的提升,因此在实际应用中需要权衡搜索性能和召回率的需求。
在写入并发方面,客户端并发写入数达到80时,写入性能达到峰值,每秒可处理13.8万文档。然而,无论是SSD还是HDD环境,提高bulk size对性能并没有明显的提升效果(bulk size的默认值是5000)。
此外,官方调优手册提出磁盘调度算法有时候对性能影响巨大,我们还尝试了不同的I/O调度器(如CFQ和Noop)。CFQ在写入性能上表现出小幅度的下降(吞吐量下降6.81%,延迟下降25.36%),而KNN搜索性能几乎保持不变。Noop则在写入和搜索性能上均有所上升,写入延迟下降了19.11%,写入吞吐量提高了10.79%。
综上所述,针对Elasticsearch的向量检索性能调优需要综合考虑并发处理、存储选择、分片策略以及I/O调度器等多个方面。通过合理的配置和优化,可以显著提升ES的查询效率和响应速度,从而满足实际应用中的高性能需求。
附录
1. 向量存储与检索
ES 通过dense_vector字段类型来存储固定长度的浮点数数组,通过集成先进的向量相似度算法,如HNSW(Hierarchical Navigable Small World),ES能够在大规模数据集中快速找到与查询向量相似的结果。该技术将向量数据以索引形式存储,并支持多种向量相似度计算方式,如余弦相似度。
用户可轻松在ES中执行向量搜索,结合其他查询和分析功能,实现高效的向量数据检索和分析。
2. HNSW算法
这是目前 ES 唯一支持的向量索引算法。HNSW算法是ES向量检索中的关键技术之一,它通过构建分层可导航的小世界图结构,实现了高效的近似最近邻搜索。
- HNSW算法是一种基于图的ANN搜索算法,旨在从大量候选集中快速找到与查询向量最近的k个元素。
- 相较于传统方法(如计算查询向量与所有候选向量的距离),HNSW通过图连接的方式预先定义候选元素之间的关系,从而显著提高检索效率。
- 通过构建多层图结构,HNSW允许在不同层级间进行快速导航,从而在保持召回率的同时降低搜索成本。
3. 二进制量化技术
二进制量化技术是一种数据压缩方法,它通过将数据表示为更少的比特来减少数据的存储大小。在向量搜索和数据库的上下文中,这意味着可以将传统的浮点数(如 32 位的 float 或 64 位的 double)表示的向量,转换为二进制形式,从而显著减少每个向量所需的存储空间。 具体来说,二进制量化涉及将一个浮点数映射到一个二进制字符串,其中每个浮点数的值被转换为一个较短的二进制序列。例如,一个 32 位的浮点数可以被量化为几个二进制位(bits),这大大减少了表示每个向量所需的总位数。