当从PG数据库访问数据时, shared buffer中没有被访问的数据, 需要从数据文件所在的存储(例如磁盘)中读取数据块, 加载到shared buffer中, 以便下一次加速访问.
数据库运行一段时间后, 通常shared_buffer里面已经存在大量热数据(经常被访问的数据), 性能会比较平稳.
但是, 一旦数据库重启, shared_buffer将被清空, 如果业务高峰期发生主动重启或被动重启(例如oom、HA等原因), 可能出现严重的性能下降/抖动, 甚至触发数据库雪崩效应.
为了解决这个问题, PG提供了数据预热插件pg_prewarm, 结合pg_buffercache(可通过这个插件定期将buffer 内的数据块ID存到表中), 在应用程序访问数据库之前, 先根据存储的数据块ID预热到shared buffer中. 杜绝抖动问题.
向量数据库呢?
向量索引也可以预热, 但是向量索引通常都比较大, 容易把shared buffer打满.
实际上没有必要把整个索引都预热进来, 也能保障性能. 为什么呢?
因为向量索引通常是层次结构的, 例如rabitq+ivf的向量索引, 通常是有多层的. hnsw向量索引也是多层结构(越靠近搜索开端的层, 越稀疏, 就像地图, 最稀疏层可能只包含国家级别的数据, 然后逐层细化.).
vectorchord提供了向量索引预热接口函数, 同时考虑到向量索引的层级结构, 只需要把靠近搜索端的某些层级加热到shared buffer即可提升访问性能.
对于向量索引,索引大小通常远大于内存大小。向量索引的设计使得索引数据被分成两部分:一小部分是热数据,另一部分是冷数据。向量索引只需要内存中的热数据即可实现合理的性能。因此,存在一个专门的预热函数,它只将索引的热数据加载到内存中。
rabitq+ivf的例子
SELECT vchordrq_prewarm('items_embedding_idx');
即使索引大小远大于内存大小,它也能很好地运行。
vchordrq_prewarm和vchordg_prewarm是VectorChord中两种不同索引访问方法的预热函数,它们通过将索引的热数据部分加载到内存中来提升查询性能。
两个预热函数在PostgreSQL中的接口定义有所不同 :
vchordrq_prewarm(regclass, integer default 0):接受索引关系和高度参数vchordg_prewarm(regclass):只接受索引关系参数两个函数都遵循相同的验证和调用模式 :
AccessShareLock打开索引关系vchordrq_prewarm根据向量类型和距离函数分发到不同的算法实现 :
VectorKind::Vecf32和VectorKind::Vecf16两种向量类型DistanceKind::L2S和DistanceKind::Dot两种距离函数MakeH0PlainPrefetcher作为预取器vchordrq::prewarm()函数,传入索引、高度参数和预取器vchordg_prewarm使用类似的分发模式但不需要高度参数 :
vchordg::prewarm()函数,只传入索引参数基于背景信息中提到的热/冷数据分离设计,两种索引方法采用不同的预热策略:
两个预热函数都通过crates/vchordg/src/lib.rs和对应的vchordrq crate中的prewarm模块实现具体的预热逻辑 。这些函数在基准测试脚本中被调用以优化查询性能 。
在vchordrq索引中,高度(height)是层次化IVF结构中的核心概念,定义在MetaTuple的存储结构中 。
高度信息存储在索引的元数据结构中:
height_of_root字段定义了整个索引树的根节点高度MetaTupleHeader结构体中作为u32类型存储vchordrq使用多层聚类结构,每一层都有对应的高度值:

在索引构建时,高度通过层次化聚类过程确定 :
在搜索和预热过程中,高度参数控制遍历深度:
height_of_root与探针数量的匹配step函数逐层遍历,从根节点向下到指定高度预热过程中,高度参数决定了哪些层级被视为"热数据":
高度概念在vchordrq中是层次化IVF结构的核心,与传统B树索引的高度概念类似但应用于向量聚类场景。外部构建模式还支持从用户提供的层次结构表中构建索引,此时高度通过树遍历算法计算得出。
https://docs.vectorchord.ai/vectorchord/usage/prewarm.html
https://deepwiki.com/search/vchordrqprewarmvchordgprewarm_f3958251-a8d8-41b8-b8ea-96c27128f5ff