在我之前的文章 “Elasticsearch:調(diào)整搜索速度”,我詳細(xì)地描述了如何調(diào)整正常的 BM25 的搜索速度。在今天的文章里,我們來進(jìn)一步探討如何提高近似 kNN 的搜索速度。希望對(duì)廣大的向量搜索開發(fā)者有一些啟示。
Elasticsearch 支持近似 k 最近鄰搜索,以有效查找與查詢向量最接近的 k 個(gè)向量。 由于近似 kNN 搜索的工作方式與其他查詢不同,因此對(duì)其性能有特殊的考慮。其中許多建議有助于提高搜索速度。 使用近似 kNN,索引算法在底層運(yùn)行搜索以創(chuàng)建向量索引結(jié)構(gòu)。 因此,這些相同的建議也有助于提高索引速度。
減少向量內(nèi)存占用
默認(rèn)的 element_type是 float。 但這可以通過 quantization 在索引時(shí)間時(shí)自動(dòng)進(jìn)行標(biāo)量量化。具體的介紹可以詳細(xì)閱讀文章 “Elasticsearch:dense vector 數(shù)據(jù)類型及標(biāo)量量化”。 量化會(huì)將所需的內(nèi)存減少 4 倍,但也會(huì)降低向量的精度并增加該字段的磁盤使用量(最多增加 25%)。 磁盤使用量增加是 Elasticsearch 存儲(chǔ)量化向量和未量化向量的結(jié)果。 例如,當(dāng)量化 40GB 浮點(diǎn)向量時(shí),將為量化向量存儲(chǔ)額外的 10GB 數(shù)據(jù)。 總磁盤使用量為50GB,但快速搜索的內(nèi)存使用量將減少到10GB。
對(duì)于 dim 大于或等于 384 的浮點(diǎn)向量,強(qiáng)烈建議使用量化索引。
降低向量維數(shù)
kNN 搜索的速度與向量維數(shù)成線性關(guān)系,因?yàn)槊總€(gè)相似度計(jì)算都會(huì)考慮兩個(gè)向量中的每個(gè)元素。 只要有可能,最好使用維度較低的向量。 一些嵌入模型有不同的維度大小,有更低和更高維度的選項(xiàng)。 你還可以嘗試使用 PCA 等降維技術(shù)。 在嘗試不同的方法時(shí),衡量對(duì)相關(guān)性的影響非常重要,以確保搜索質(zhì)量仍然可以接受。
從 _source 中排除向量字段
Elasticsearch 將在索引時(shí)傳遞的原始 JSON 文檔存儲(chǔ)在 _source 字段中。 默認(rèn)情況下,搜索結(jié)果中的每個(gè)命中都包含完整文檔 _source。 當(dāng)文檔包含 dense_vector 字段時(shí),_source 可能非常大且加載成本昂貴。 這可能會(huì)顯著降低 kNN 搜索的速度。
你可以通過 excludes 映射參數(shù)禁用在 _source 中存儲(chǔ) dense_vector 字段。 這可以防止在搜索期間加載和返回大向量,并且還可以減少索引大小。 _source 中省略的向量仍然可以在 kNN 搜索中使用,因?yàn)樗蕾囉趩为?dú)的數(shù)據(jù)結(jié)構(gòu)來執(zhí)行搜索。 在使用 excludes 參數(shù)之前,請(qǐng)確保查看從 _source 中省略字段的缺點(diǎn)。
另一種選擇是使用 synthetic_source(如果所有索引字段都支持)。
確保數(shù)據(jù)節(jié)點(diǎn)有足夠的內(nèi)存
Elasticsearch 使用 HNSW 算法進(jìn)行近似 kNN 搜索。 HNSW 是一種基于圖的算法,只有當(dāng)大多數(shù)向量數(shù)據(jù)保存在內(nèi)存中時(shí)才能有效地工作。 你應(yīng)該確保數(shù)據(jù)節(jié)點(diǎn)至少有足夠的 RAM 來保存向量數(shù)據(jù)和索引結(jié)構(gòu)。 要檢查向量數(shù)據(jù)的大小,你可以使用分析索引磁盤使用情況 API。 作為一個(gè)寬松的經(jīng)驗(yàn)法則,并假設(shè)默認(rèn)的 HNSW 選項(xiàng),使用的字節(jié)將為 num_vectors * 4 * (num_dimensions + 12)。 當(dāng)使用字節(jié) element_type 時(shí),所需的空間將更接近 num_vectors * (num_dimensions + 12)。 請(qǐng)注意,所需的 RAM 用于文件系統(tǒng)緩存,它與 Java 堆分開。
數(shù)據(jù)節(jié)點(diǎn)還應(yīng)該為其他需要 RAM 的方式留下緩沖區(qū)。 例如,你的索引可能還包括文本字段和數(shù)字,這也受益于使用文件系統(tǒng)緩存。 建議使用你的特定數(shù)據(jù)集運(yùn)行基準(zhǔn)測試,以確保有足夠的內(nèi)存來提供良好的搜索性能。 你可以在這里和這里找到我們用于夜間基準(zhǔn)測試的一些數(shù)據(jù)集和配置示例。
預(yù)熱文件系統(tǒng)緩存
如果運(yùn)行 Elasticsearch 的機(jī)器重新啟動(dòng),文件系統(tǒng)緩存將為空,因此操作系統(tǒng)需要一些時(shí)間才能將索引的熱區(qū)域加載到內(nèi)存中,以便搜索操作快速。 你可以使用 index.store.preload 設(shè)置顯式告訴操作系統(tǒng)哪些文件應(yīng)根據(jù)文件擴(kuò)展名立即加載到內(nèi)存中。
警告:如果文件系統(tǒng)緩存不夠大,無法容納所有數(shù)據(jù),則在太多索引或太多文件上急切地將數(shù)據(jù)加載到文件系統(tǒng)緩存中將使搜索速度變慢。 謹(jǐn)慎使用。
以下文件擴(kuò)展名用于近似 kNN 搜索:
- 向量值的 vec 和 veq
- HNSW 圖的 vex
- 用于元數(shù)據(jù)的 vem、vemf 和 vemq
減少索引段的數(shù)量
Elasticsearch 分片由段(segment)組成,段是索引中的內(nèi)部存儲(chǔ)元素。 對(duì)于近似 kNN 搜索,Elasticsearch 將每個(gè)段的向量值存儲(chǔ)為單獨(dú)的 HNSW 圖,因此 kNN 搜索必須檢查每個(gè)段。 最近的 kNN 搜索并行化使得跨多個(gè)片段的搜索速度大大加快,但如果片段較少,kNN 搜索的速度仍然可以提高數(shù)倍。 默認(rèn)情況下,Elasticsearch 通過后臺(tái)合并過程定期將較小的段合并為較大的段。 如果這還不夠,你可以采取明確的步驟來減少索引段的數(shù)量。
Lucene 合并,同時(shí)索引所有維基百科(英文)
強(qiáng)制合并到一個(gè)段
Force merge 操作強(qiáng)制進(jìn)行索引合并。 如果強(qiáng)制合并到一個(gè)段,kNN 搜索只需要檢查一個(gè)包含所有內(nèi)容的 HNSW 圖。 強(qiáng)制合并 dense_vector 字段是一項(xiàng)昂貴的操作,可能需要大量時(shí)間才能完成。
警告:我們建議僅強(qiáng)制合并只讀索引(意味著索引不再接收寫入)。 當(dāng)文檔被更新或刪除時(shí),舊版本不會(huì)立即刪除,而是軟刪除并標(biāo)記為 “墓碑”。 這些軟刪除文檔會(huì)在定期段合并期間自動(dòng)清除。 但強(qiáng)制合并可能會(huì)導(dǎo)致生成非常大(> 5GB)的段,這些段不符合常規(guī)合并的條件。 因此,軟刪除文檔的數(shù)量會(huì)迅速增長,從而導(dǎo)致更高的磁盤使用率和更差的搜索性能。 如果你定期強(qiáng)制合并接收寫入的索引,這也會(huì)使快照更加昂貴,因?yàn)樾挛臋n無法增量備份。
在批量索引期間創(chuàng)建大段
常見的模式是首先執(zhí)行初始批量上傳,然后使索引可用于搜索。 你可以調(diào)整索引設(shè)置以鼓勵(lì) Elasticsearch 創(chuàng)建更大的初始段,而不是強(qiáng)制合并:
- 確保批量上傳期間沒有搜索,并通過將其設(shè)置為 -1 來禁用 index.refresh_interval。 這可以防止刷新操作并避免創(chuàng)建額外的段。
- 為 Elasticsearch 提供一個(gè)較大的索引緩沖區(qū),以便它可以在刷新之前接受更多文檔。 默認(rèn)情況下,indices.memory.index_buffer_size 設(shè)置為堆大小的 10%。 對(duì)于像 32GB 這樣的大堆大小,這通常就足夠了。 為了允許使用完整的索引緩沖區(qū),你還應(yīng)該增加限制 index.translog.flush_threshold_size。
避免在搜索過程中建立大量索引
積極地索引文檔可能會(huì)對(duì)近似 kNN 搜索性能產(chǎn)生負(fù)面影響,因?yàn)樗饕€程會(huì)竊取搜索的計(jì)算資源。 當(dāng)同時(shí)索引和搜索時(shí),Elasticsearch 也會(huì)頻繁刷新,這會(huì)創(chuàng)建幾個(gè)小段。 這也會(huì)損害搜索性能,因?yàn)楫?dāng)分段較多時(shí),近似 kNN 搜索速度會(huì)變慢。
如果可能,最好在近似 kNN 搜索期間避免大量索引。 如果你需要重新索引所有數(shù)據(jù),可能是因?yàn)橄蛄壳度肽P桶l(fā)生了變化,那么最好將新文檔重新索引到單獨(dú)的索引中,而不是就地更新它們。 這有助于避免上述速度減慢,并防止由于頻繁的文檔更新而導(dǎo)致昂貴的合并操作。
在 Linux 上使用適度的預(yù)讀值來避免頁面緩存抖動(dòng)
搜索可能會(huì)導(dǎo)致大量隨機(jī)讀取 I/O。 當(dāng)?shù)讓訅K設(shè)備具有較高的預(yù)讀值時(shí),可能會(huì)執(zhí)行大量不必要的讀取 I/O,特別是當(dāng)使用內(nèi)存映射訪問文件時(shí)(請(qǐng)參閱存儲(chǔ)類型)。
大多數(shù) Linux 發(fā)行版對(duì)單個(gè)普通設(shè)備使用 128KiB 的合理預(yù)讀值,但是,當(dāng)使用軟件 raid、LVM 或 dm-crypt 時(shí),生成的塊設(shè)備(支持 Elasticsearch path.data)最終可能會(huì)具有非常大的預(yù)讀值(在 幾個(gè) MiB 的范圍)。 這通常會(huì)導(dǎo)致嚴(yán)重的頁面(文件系統(tǒng))緩存抖動(dòng),從而對(duì)搜索(或更新)性能產(chǎn)生不利影響。
你可以使用 lsblk -o NAME,RA,MOUNTPOINT,TYPE,SIZE 檢查當(dāng)前值(以 KiB 為單位)。 有關(guān)如何更改此值的信息,請(qǐng)參閱發(fā)行版的文檔(例如,使用 udev 規(guī)則在重新啟動(dòng)后保持不變,或通過 blockdev --setra 作為瞬態(tài)設(shè)置)。 我們建議預(yù)讀值為 128KiB。文章來源:http://www.zghlxwxcb.cn/news/detail-840278.html
在 Linux 上使用適度的預(yù)讀值 (readahead) 來避免頁面緩存抖動(dòng)
搜索可能會(huì)導(dǎo)致大量隨機(jī)讀取 I/O。 當(dāng)?shù)讓訅K設(shè)備具有較高的預(yù)讀值時(shí),可能會(huì)執(zhí)行大量不必要的讀取 I/O,特別是當(dāng)使用內(nèi)存映射訪問文件時(shí)(請(qǐng)參閱存儲(chǔ)類型)。
大多數(shù) Linux 發(fā)行版對(duì)單個(gè)普通設(shè)備使用 128KiB 的合理預(yù)讀值,但是,當(dāng)使用軟件 raid、LVM 或 dm-crypt 時(shí),生成的塊設(shè)備(支持 Elasticsearch path.data)最終可能會(huì)具有非常大的預(yù)讀值(在 幾個(gè) MiB 的范圍)。 這通常會(huì)導(dǎo)致嚴(yán)重的頁面(文件系統(tǒng))緩存抖動(dòng),從而對(duì)搜索(或更新)性能產(chǎn)生不利影響。
你可以使用 lsblk -o NAME,RA,MOUNTPOINT,TYPE,SIZE 檢查當(dāng)前值(以 KiB 為單位)。 有關(guān)如何更改此值的信息,請(qǐng)參閱發(fā)行版的文檔(例如,使用 udev 規(guī)則在重新啟動(dòng)后保持不變,或通過 blockdev --setra 作為瞬態(tài)設(shè)置)。 我們建議預(yù)讀值為 128KiB。
警告:blockdev 期望值以 512 字節(jié)扇區(qū)為單位,而 lsblk 報(bào)告值以 KiB 為單位。 例如,要將 /dev/nvme0n1 的預(yù)讀臨時(shí)設(shè)置為 128KiB,請(qǐng)指定 blockdev --setra 256 /dev/nvme0n1。文章來源地址http://www.zghlxwxcb.cn/news/detail-840278.html
到了這里,關(guān)于Elasticsearch:調(diào)整近似 kNN 搜索的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!