• 文章
  • 服务一百万向量所需的最低内存
返回 Qdrant 内部原理

服务一百万向量所需的最低内存

Andrei Vasnetsov

·

2022年12月07日

Minimal RAM you need to serve a million vectors

在衡量进程的内存消耗时,我们经常依赖于诸如 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)。

内存请求/秒
1512mb774.38
1256mb760.63
1200mb794.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** 内存时,就会发生内存不足的情况。

实验详情
内存请求/秒
1200mb759.94
1100mb687.00
1000mb10

— 使用稍快的磁盘 —

内存请求/秒
1000mb25 rps
750mb5 rps
625mb2.5 rps
600mb内存不足

此时,我们必须从网络挂载存储切换到更快的磁盘,因为基于网络的存储对于处理系统服务查询所需的顺序读取量来说太慢了。

但首先让我们看看服务 100 万个向量需要多少内存,然后再讨论速度优化。

使用 MMAP 存储向量和 HNSW 图

在第三个实验中,我们测试了当使用内存映射文件存储向量和 HNSW 图时,我们的系统性能如何。使用以下方式创建集合:

PUT /collections/benchmark 
{
  "vectors": {
    ...
    "on_disk": true
  },
  "hnsw_config": {
    "on_disk": true
  },
  ...
}

通过此配置,我们只需 **135mb 内存**即可服务 100 万个向量!

实验详情
内存请求/秒
600mb5 rps
300mb0.9 rps / 每个查询 1.1 秒
150mb0.4 rps / 每个查询 2.5 秒
135mb0.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 时的 RPSIOPS=183k 时的 RPS
600mb550
300mb0.913
200mb0.58
150mb0.47

如您所见,磁盘速度对搜索速度有显著影响。使用本地 SSD,我们将搜索速度提高了 10 倍!

使用生产级磁盘,搜索速度可能更高。某些配置的 SSD 可以达到 1M IOPS 甚至更多。

这可能是 Qdrant 中以低搜索延迟服务大型数据集的一个有趣选项。

结论

在本文中,我们展示了 Qdrant 在内存使用方面具有灵活性,可用于服务大型数据集。它提供了内存使用和搜索速度之间的可配置权衡。如果您有兴趣了解更多关于 Qdrant 的信息,请立即预订演示

我们渴望了解您如何在项目中使用 Qdrant,面临哪些挑战,以及我们如何帮助您解决这些问题。请随时加入我们的 Discord 并与我们分享您的经验!

此页面有帮助吗?

感谢您的反馈! 🙏

听到这个消息我们很抱歉。😔 您可以在 GitHub 上编辑此页面,或创建一个 GitHub Issue。