在衡量进程的内存消耗时,我们经常依赖于诸如 htop
之类的工具来指示使用了多少内存。然而,这种方法可能会产生误导,并且不总是准确反映进程的实际内存使用情况。
htop
在许多方面可能不是衡量内存使用情况的可靠指标。例如,进程可能提前分配内存但未使用它,或者它可能不释放已释放的内存,导致内存消耗被夸大。进程可能被分叉(fork),这意味着它将拥有独立的内存空间,但会与父进程共享相同的代码和数据。这意味着子进程的内存消耗将被计算两次。此外,进程可能利用磁盘缓存,这在 htop
的测量中也算作常驻内存。
因此,即使 htop
显示一个进程使用了 10GB 内存,这并不一定意味着该进程实际需要 10GB 内存才能高效运行。在本文中,我们将探讨如何正确测量内存使用情况并优化 Qdrant 以实现最佳内存消耗。
如何衡量实际内存需求
我们需要知道内存消耗,以便估算运行程序所需的内存量。因此,为了确定这一点,我们可以进行一个简单的实验。让我们限制进程允许使用的内存,并观察它在哪个点停止工作。通过这种方式,我们可以确定程序运行所需的最小内存量。
一种方法是进行网格搜索,但更有效的方法是使用二分搜索快速找到所需的最小内存量。我们可以使用 docker 来限制进程的内存使用。
在每次运行基准测试之前,清除页面缓存非常重要,使用以下命令:
sudo bash -c 'sync; echo 1 > /proc/sys/vm/drop_caches'
这确保进程不使用之前运行的任何数据,从而提供更准确和一致的结果。
我们可以使用以下命令以 1GB 的内存限制运行 Qdrant:
docker run -it --rm \
--memory 1024mb \
--network=host \
-v "$(pwd)/data/storage:/qdrant/storage" \
qdrant/qdrant:latest
让我们进行一些基准测试
让我们运行一些基准测试,看看 Qdrant 需要多少内存来服务 100 万个向量。
我们可以使用 glove-100-angular
和来自 vector-db-benchmark 项目的脚本来上传和查询向量。第一次运行时,我们将使用 Qdrant 的默认配置,所有数据都存储在内存中。
# Upload vectors
python run.py --engines qdrant-all-in-ram --datasets glove-100-angular
上传向量后,我们将使用不同的内存限制重复相同的实验,以查看它们如何影响内存消耗和搜索速度。
# Search vectors
python run.py --engines qdrant-all-in-ram --datasets glove-100-angular --skip-upload
全部在内存中
在第一个实验中,我们测试了当所有向量都存储在内存中时,我们的系统性能如何。我们尝试使用不同内存量,范围从 1512mb 到 1024mb,并测量了系统能够处理的每秒请求数 (rps)。
内存 | 请求/秒 |
---|---|
1512mb | 774.38 |
1256mb | 760.63 |
1200mb | 794.72 |
1152mb | 内存不足 |
1024mb | 内存不足 |
我们发现 1152MB 的内存限制导致我们的系统内存不足,但使用 1512mb、1256mb 和 1200mb 内存时,我们的系统能够处理约 780 RPS。这表明服务约 100 万个向量需要约 1.2GB 内存,并且当内存使用量超过 1.2GB 时,速度没有降低。
使用 MMAP 存储向量
让我们更进一步!在第二个实验中,我们测试了当**使用内存映射文件 (mmap) 存储向量**时,我们的系统性能如何。使用以下方式创建集合:
PUT /collections/benchmark
{
"vectors": {
...
"on_disk": true
}
}
此配置告诉 Qdrant,如果段大小大于 20000Kb(约 4 万个 128 维向量),则对向量使用 mmap。
现在,当我们只允许使用 **600mb** 内存时,就会发生内存不足的情况。
实验详情
内存 | 请求/秒 |
---|---|
1200mb | 759.94 |
1100mb | 687.00 |
1000mb | 10 |
— 使用稍快的磁盘 —
内存 | 请求/秒 |
---|---|
1000mb | 25 rps |
750mb | 5 rps |
625mb | 2.5 rps |
600mb | 内存不足 |
此时,我们必须从网络挂载存储切换到更快的磁盘,因为基于网络的存储对于处理系统服务查询所需的顺序读取量来说太慢了。
但首先让我们看看服务 100 万个向量需要多少内存,然后再讨论速度优化。
使用 MMAP 存储向量和 HNSW 图
在第三个实验中,我们测试了当使用内存映射文件存储向量和 HNSW 图时,我们的系统性能如何。使用以下方式创建集合:
PUT /collections/benchmark
{
"vectors": {
...
"on_disk": true
},
"hnsw_config": {
"on_disk": true
},
...
}
通过此配置,我们只需 **135mb 内存**即可服务 100 万个向量!
实验详情
内存 | 请求/秒 |
---|---|
600mb | 5 rps |
300mb | 0.9 rps / 每个查询 1.1 秒 |
150mb | 0.4 rps / 每个查询 2.5 秒 |
135mb | 0.33 rps / 每个查询 3 秒 |
125mb | 内存不足 |
此时,磁盘速度的重要性变得至关重要。我们可以用 135mb 内存处理搜索请求,但请求速度使其无法在生产环境中使用该系统。
让我们看看如何提高速度。
如何加快搜索速度
为了测量磁盘参数对搜索速度的影响,我们使用 fio
工具测试了不同类型磁盘的速度。
# Install fio
sudo apt-get install fio
# Run fio to check the random reads speed
fio --randrepeat=1 \
--ioengine=libaio \
--direct=1 \
--gtod_reduce=1 \
--name=fiotest \
--filename=testfio \
--bs=4k \
--iodepth=64 \
--size=8G \
--readwrite=randread
最初,我们在网络挂载磁盘上进行了测试,但其性能太慢,读取 IOPS 为 6366,带宽为 24.9 MiB/s
read: IOPS=6366, BW=24.9MiB/s (26.1MB/s)(8192MiB/329424msec)
为了提高性能,我们切换到本地磁盘,结果显示快得多,读取 IOPS 为 63.2k,带宽为 247 MiB/s
read: IOPS=63.2k, BW=247MiB/s (259MB/s)(8192MiB/33207msec)
这带来了显著的速度提升,但我们想看看是否可以进一步提高性能。为此,我们切换到配备本地 SSD 的机器,结果显示性能甚至更好,读取 IOPS 为 183k,带宽为 716 MiB/s
read: IOPS=183k, BW=716MiB/s (751MB/s)(8192MiB/11438msec)
让我们看看这些结果如何转化为搜索速度
内存 | IOPS=63.2k 时的 RPS | IOPS=183k 时的 RPS |
---|---|---|
600mb | 5 | 50 |
300mb | 0.9 | 13 |
200mb | 0.5 | 8 |
150mb | 0.4 | 7 |
如您所见,磁盘速度对搜索速度有显著影响。使用本地 SSD,我们将搜索速度提高了 10 倍!
使用生产级磁盘,搜索速度可能更高。某些配置的 SSD 可以达到 1M IOPS 甚至更多。
这可能是 Qdrant 中以低搜索延迟服务大型数据集的一个有趣选项。
结论
在本文中,我们展示了 Qdrant 在内存使用方面具有灵活性,可用于服务大型数据集。它提供了内存使用和搜索速度之间的可配置权衡。如果您有兴趣了解更多关于 Qdrant 的信息,请立即预订演示!
我们渴望了解您如何在项目中使用 Qdrant,面临哪些挑战,以及我们如何帮助您解决这些问题。请随时加入我们的 Discord 并与我们分享您的经验!