在衡量我们进程的内存消耗时,我们经常依赖诸如htop之类的工具来指示使用了多少RAM。然而,这种方法可能具有误导性,并不总是准确反映进程的真实内存使用情况。
htop在很多情况下可能无法作为内存使用情况的可靠指标。例如,一个进程可能会提前分配内存但不使用它,或者它可能不会释放已解除分配的内存,从而导致内存消耗被夸大。一个进程可能被分叉,这意味着它将拥有一个独立的内存空间,但它将与父进程共享相同的代码和数据。这意味着子进程的内存消耗将被计算两次。此外,一个进程可能会利用磁盘缓存,这在htop的测量中也被计为驻留内存。
因此,即使htop显示一个进程正在使用10GB内存,这并不一定意味着该进程实际需要10GB RAM才能高效运行。在本文中,我们将探讨如何正确测量RAM使用情况并优化Qdrant以实现最佳内存消耗。
如何测量实际RAM需求
我们需要了解内存消耗,以便估算运行程序所需的RAM量。因此,为了确定这一点,我们可以进行一个简单的实验。让我们限制进程允许的内存,并观察它何时停止运行。通过这种方式,我们可以确定程序运行所需的最小RAM量。
一种方法是进行网格搜索,但更有效的方法是使用二分搜索来快速找到所需的最小RAM量。我们可以使用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需要多少RAM来服务100万个向量。
我们可以使用来自vector-db-benchmark项目的glove-100-angular和脚本来上传和查询向量。第一次运行我们将使用Qdrant的默认配置,所有数据都存储在RAM中。
# Upload vectors
python run.py --engines qdrant-all-in-ram --datasets glove-100-angular
上传向量后,我们将用不同的RAM限制重复相同的实验,以查看它们如何影响内存消耗和搜索速度。
# 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。这表明大约需要1.2GB内存来服务大约100万个向量,并且当内存使用限制在1.2GB以上时,速度没有下降。
使用MMAP存储向量
让我们再进一步!在第二个实验中,我们测试了当**向量使用内存映射文件**(mmap)存储时,我们系统的性能如何。创建集合时使用
PUT /collections/benchmark
{
"vectors": {
...
"on_disk": true
}
}
此配置告诉 Qdrant,如果段大小大于 20000Kb(大约是 40K 个 128d 向量),则对向量使用 mmap。
现在,当只允许使用**600MB**内存时,就会发生内存不足
实验详情
| 内存 | 请求数/秒 |
|---|---|
| 1200MB | 759.94 |
| 1100MB | 687.00 |
| 1000MB | 10 |
— 使用稍快的磁盘 —
| 内存 | 请求数/秒 |
|---|---|
| 1000MB | 25 rps |
| 750MB | 5 rps |
| 625MB | 2.5 rps |
| 600MB | 内存不足 |
此时,我们不得不从网络挂载存储切换到更快的磁盘,因为基于网络的存储太慢,无法处理我们的系统需要服务查询的顺序读取量。
但我们首先来看看服务100万个向量需要多少RAM,然后我们也将讨论速度优化。
使用MMAP存储向量和HNSW图
在第三个实验中,我们测试了当向量和HNSW图都使用内存映射文件存储时,我们系统的性能如何。创建集合时使用
PUT /collections/benchmark
{
"vectors": {
...
"on_disk": true
},
"hnsw_config": {
"on_disk": true
},
...
}
通过此配置,我们能够仅使用**135MB的RAM**来服务100万个向量!
实验详情
| 内存 | 请求数/秒 |
|---|---|
| 600MB | 5 rps |
| 300MB | 0.9 rps / 每次查询1.1秒 |
| 150MB | 0.4 rps / 每次查询2.5秒 |
| 135MB | 0.33 rps / 每次查询3秒 |
| 125MB | 内存不足 |
此时,磁盘速度的重要性变得至关重要。我们可以用135MB的RAM来处理搜索请求,但请求速度使得该系统无法投入生产使用。
让我们看看如何提高速度。
如何加快搜索速度
为了衡量磁盘参数对搜索速度的影响,我们使用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在RAM使用方面具有灵活性,可用于服务大型数据集。它提供了RAM使用和搜索速度之间可配置的权衡。如果您有兴趣了解更多关于Qdrant的信息,请立即预订演示!
我们渴望了解您如何在项目中运用 Qdrant,面临哪些挑战,以及我们如何帮助您解决这些问题。欢迎加入我们的 Discord 并与我们分享您的经验!
