SSD как кэш, HDD как постоянное хранилище: экспериментируем с кэшированием в bcachefs
Вы когда-нибудь задумывались над смыслом названия этой файловой системы? А между тем ключевая фича, с которой все начиналось, — это как раз кэширование. Изначально был bcache — прототип, на основе которого впоследствии вырос bcachefs. Вот как об этом рассказывает сам разработчик файловой системы Kent Overstreet:
Enter bcache, the prototype for bcachefs. When SSDs were first being introduced, they were from the beginning orders of magnitude faster than rotating disk - but expensive, so block layer caching was an obvious approach for using them effectively.
В этой статье мы наверстаем упущенное — познакомимся с этой фичей и узнаем, есть ли от нее профит.
Немного теории
Концепция кэширования не нова и уже реализована в RAID-контроллерах. Есть три режима работы кэша записи:
Write Back. Пишем в быстрый кэш и сразу отдаем сигнал ОС о завершенной операции записи. Не ждем завершения записи на медленный storage, находящийся за кэшем. Не все дается бесплатно: есть риск потери данных, если пропадет питание, питающее кэш. Для защиты от этого часто используются специальные аккумуляторы - BBU (Battery Backup Unit).
Write Through. Пишем в быстрый кэш, но отдаем сигнал о завершении операции записи только после того, как запись пройдет и в кэш, и в медленный storage.
Write Around. Пишем в медленный storage, без кэша.
В статье мы соберем в bcachefs конфигурацию, похожую на Write Back.
Подготовка окружения
Тестировать будем на виртуальной машине в облаке.
Кстати, повторять этот стенд у себя необязательно на голом железе — всю конфигурацию из четырех дисков с разными характеристиками можно собрать на ВМ любого облачного провайдера за несколько минут.
Если тоже хотите погонять caching или что-то еще на живом железе, а не только читать чужие бенчмарки — у нас в К2 Cloud сейчас действует грант до 30 000 ₽ на тестирование инфраструктуры для новых корпоративных клиентов: срок теста согласовывается индивидуально, от 3 недель до 60 дней, а бонус можно потратить на виртуальные машины, SSD/NVMe-хранилище и другие сервисы платформы.
Если подбирать диск под роль кэша (т.е. под --foreground_target), логично брать самый шустрый и с минимальной задержкой. У K2 Cloud это nv1 — выделенный физический NVMe SSD с производительностью до 256 тыс. IOPS и пропускной способностью до 1000 МиБ/с. За счет отказа от репликации вся производительность диска уходит на скорость, а не на избыточность — для кэша это то, что доктор прописал.
А пока вернемся к подготовке окружения.
CPU | 4 vCPU | ||||||
RAM | 8 GiB | ||||||
Объем | Тип диска | IOPS | Throughput | Блочное устройство | Разделы | mountpoint | |
Disk 1 | 20 GiB | st3 | 500 | 8 MiB/s | /dev/vda | ||
/dev/vda1 | / | ||||||
Disk 2 | 256 GiB | st3 | 500 | 64 MiB/s | /dev/vdb | ||
/dev/vdb1 | /mnt/storage | ||||||
Disk 3 | 64 GiB | io2 | 3200 | 500 MiB/s | /dev/vdc | ||
/dev/vdc1 | /mnt/storage | ||||||
Disk 4 | 256 GiB | st3 | 500 | 64 MiB/s | /dev/vdd | ||
/dev/vdd1 | /mnt/storage_noncached_vdd | ||||||
Здесь все просто: операционная система на отдельном диске (/dev/vda), чтобы не мешать тестам. Для хранения и тестов — один медленный диск (/dev/vdb) большого объема. Для кэша — быстрый (/dev/vdc), но небольшой. И еще один медленный диск (/dev/vdd) без кэша, чтобы сравнить производительность с первой парой.
Возьмем Ubuntu 26.04 с ядром 7.0.0-31-generic и установим на нее модуль bcachefs.
# стягиваем ключ разработчика
sudo install -d -m 0755 /etc/apt/keyrings
wget -qO- https://apt.bcachefs.org/apt.bcachefs.org.asc | sudo tee /etc/apt/keyrings/apt.bcachefs.org.asc > /dev/null
sudo chmod 0644 /etc/apt/keyrings/apt.bcachefs.org.asc
# Fingerprint: EA483B991020C72A8A5035ADA0620B5E0E01C1DD
# ставим его репу себе в apt
sudo tee /etc/apt/sources.list.d/apt.bcachefs.org.sources > /dev/null <<SOURCES
Types: deb deb-src
URIs: https://apt.bcachefs.org/$(. /etc/os-release && echo ${VERSION_CODENAME})/
Suites: bcachefs-tools-release
Components: main
Signed-By: /etc/apt/keyrings/apt.bcachefs.org.asc
SOURCES
# обновляем кэш и ставим нужный пакет
sudo apt update
sudo apt install bcachefs-toolsПоставим также утилиту для замера производительности.
sudo apt install fioОсталось только подготовить файловые системы. Разбиваем диски.
sudo parted /dev/vdb mklabel gpt
sudo parted /dev/vdb mkpart ext2 1MiB 100%
sudo parted /dev/vdc mklabel gpt
sudo parted /dev/vdc mkpart ext2 1MiB 100%
sudo parted /dev/vdd mklabel gpt
sudo parted /dev/vdd mkpart ext2 1MiB 100%Проверяем.
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 20G 0 disk
└─vda1 253:1 0 20G 0 part /
vdb 253:16 0 256G 0 disk
└─vdb1 253:17 0 256G 0 part
vdc 253:32 0 64G 0 disk
└─vdc1 253:33 0 64G 0 part
vdd 253:48 0 256G 0 disk
└─vdd1 253:49 0 256G 0 part И наконец, форматируем.
sudo bcachefs format /dev/vd[bc]1 \
--foreground_target /dev/vdc1 \
--background_target /dev/vdb1 \
--promote_target /dev/vdc1
sudo bcachefs format /dev/vdd1Опция --foreground_target задает устройство для кэша записи, а --background_target — устройство для постоянного хранения, а --promote_target тоже указывает на кэш, но уже для чтения.
Интуитивно кажется, что таргеты логичнее задавать при монтировании файловой системы, но если попробовать это сделать, получим ошибку в dmesg:
sudo mount -t bcachefs -o foreground_target=/dev/vdc1,background_target=/dev/vdb1,promote_target=/dev/vdc1 /dev/vdb1:/dev/vdc1 /mnt/storage/
dmesg -T | tail -n 10
...
[Tue Sep 22 22:51:50 2026] bcachefs: bch2_parse_one_mount_opt() option foreground_target may no longer be specified at mount time; set via sysfs opts dir
[Tue Sep 22 22:51:50 2026] bcachefs: bch2_parse_one_mount_opt() option background_target may no longer be specified at mount time; set via sysfs opts dir
[Tue Sep 22 22:51:50 2026] bcachefs: bch2_parse_one_mount_opt() option promote_targetmay no longer be specified at mount time; set via sysfs opts dirПохоже, раньше это так и было, но теперь таргеты сохраняются в superblock файловой системы прямо на диске и применяются автоматически при каждом следующем монтировании. В этом примере используется версия модуля 1.39.6.
Монтируем.
sudo mount -t bcachefs /dev/vdb1:/dev/vdc1 /mnt/storage/
sudo mount -t bcachefs /dev/vdd1 /mnt/storage_noncached_vdd
sudo lsblk -fs
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
vda1 ext4 1.0 c248f645-8426-47a9-a343-3cfbe3c20c38 13.7G 25% /
└─vda
vdb1 bcachefs 1.39 6aa33294-d16e-4104-80b9-53592f8df9f7 279.9G 3% /mnt/storage
└─vdb
vdc1 bcachefs 1.39 6aa33294-d16e-4104-80b9-53592f8df9f7
└─vdc
vdd1 bcachefs 1.39 6ef11c96-2209-44d6-b093-5af2e88411ea 222.4G 3% /mnt/storage_noncached_vdd
└─vdd sudo bcachefs fs usage /mnt/storage/
Filesystem: 6aa33294-d16e-4104-80b9-53592f8df9f7
Size: 313615366656
Used: 8428191744
Online reserved: 0
undegraded
1x: 8428191744
cached: 8388608000
Device label Device State Size Used Use%
(no label) (device 0): vdb1 rw 272926502912 8388608000 3%
(no label) (device 1): vdc1 rw 68178407424 8428191744 15% Получившаяся конфигурация соответствует режиму Write Back во многих железных RAID-контроллерах: данные сначала пишутся на быстрое хранилище, а сигнал об успешной записи ОС получает сразу, не дожидаясь записи на низлежащее медленное хранилище.
Небольшим расхождением здесь является наличие опции --promote_target, которая не относится к политике Write Back (как следует из ее названия, она про запись). По поведению эта опция больше подходит на используемую в RAID-контроллерах Dell (PERC) фичу CacheCade, которая используется для повышения производительности случайных операций чтения для горячих (часто запрашиваемых) данных. Если верить документации Dell, она поддерживается только с сертифицированными SSD и только на контроллерах PERC H710P, H800, H810. Здесь же мы получаем ее без каких-либо сертифицированных дисков и проприетарных контроллеров — почти бесплатно.
Тестирование производительности
Для тестирования используем утилиту fio.
Подготовим некоторые данные, пусть это будет файл случайного содержимого размером 4 GiB.
sudo dd if=/dev/urandom of=/mnt/storage/file.raw bs=4k count=1000kСразу же посмотрим, как применяется кэширование.
sudo bcachefs fs usage -h /mnt/storage/
Filesystem: 6aa33294-d16e-4104-80b9-53592f8df9f7
Size: 292G
Used: 7.85G
Online reserved: 0
undegraded
1x: 7.85G
cached: 4.64G
Pending reconcile: data metadata
target: 3.17G 0
Device label Device State Size Used Use% Leaving
(no label) (device 0): vdb1 rw 254G 4.64G 2%
(no label) (device 1): vdc1 rw 63.5G 7.85G 15% 3.17G Данные сразу записались в кэш. К моменту, когда запустили команду, в фоне уже шел reconcile (отправка данных из кэша в постоянное хранилище) — из 4 GiB отправлено уже 830 MiB (см. столбец 'Leaving').
Подождем немного и запустим команду снова.
Filesystem: 6aa33294-d16e-4104-80b9-53592f8df9f7
Size: 292G
Used: 7.85G
Online reserved: 0
undegraded
1x: 7.85G
cached: 7.51G
Pending reconcile: data metadata
target: 311M 0
Device label Device State Size Used Use% Leaving
(no label) (device 0): vdb1 rw 254G 7.51G 3%
(no label) (device 1): vdc1 rw 63.5G 7.85G 15% 311M Как видно из вывода, объем кэшированных данных вырос до 7.51 GiB, а процесс reconcile почти завершил свою работу — из кэша в постоянное хранилище осталось отправить только 311 MiB. К началу тестирования весь файл уже будет и в кэше, и в постоянном хранилище.
Тест на последовательное чтение, IOPS
sudo touch /mnt/storage/file.raw
sudo fio --filename=/mnt/storage/file.raw --direct=1 --rw=read --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1 --readonlyiops-test-job: (groupid=0, jobs=4): err= 0: pid=2826: Tue Sep 22 23:23:27 2026
read: IOPS=2262, BW=9048KiB/s (9266kB/s)(181MiB/20461msec)Для сравнения, такой же тест проведем на файловой системе без кэша.
sudo fio --filename=/mnt/storage_noncached_vdd/file.raw --direct=1 --rw=read --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1 --readonlyiops-test-job: (groupid=0, jobs=4): err= 0: pid=3505: Wed Sep 23 00:09:32 2026
read: IOPS=250, BW=1003KiB/s (1027kB/s)(23.5MiB/24013msec)Тест на последовательное чтение, throughput
sudo fio --filename=/mnt/storage/file.raw --direct=1 --rw=read --bs=256k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1 --readonlythroughput-test-job: (groupid=0, jobs=4): err= 0: pid=2856: Tu e Sep 22 23:28:37 2026
read: IOPS=2243, BW=561MiB/s (588MB/s)(11.0GiB/20165msec)Такой же тест на файловой системе без кэша:
sudo fio --filename=/mnt/storage_noncached_vdd/file.raw --direct=1 --rw=read --bs=256k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1 --readonlythroughput-test-job: (groupid=0, jobs=4): err= 0: pid=3530: Wed Sep 23 00:12:13 2026
read: IOPS=257, BW=64.3MiB/s (67.4MB/s)(1350MiB/21003msec)Тест на случайное чтение, IOPS
sudo fio --filename=/mnt/storage/file.raw --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1 --readonlyiops-test-job: (groupid=0, jobs=4): err= 0: pid=3169: Tue Sep 22 23:50:23 2026
read: IOPS=2128, BW=8514KiB/s (8718kB/s)(173MiB/20803msec)Без кэша:
sudo fio --filename=/mnt/storage_noncached_vdd/file.raw --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1 --readonlyiops-test-job: (groupid=0, jobs=4): err= 0: pid=3551: Wed Sep 23 00:14:58 2026
read: IOPS=264, BW=1060KiB/s (1085kB/s)(21.7MiB/20977msec)Тест на случайное чтение, throughput
sudo fio --filename=/mnt/storage/file.raw --direct=1 --rw=randread --bs=256k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1 --readonlythroughput-test-job: (groupid=0, jobs=4): err= 0: pid=2920: Tue Sep 22 23:36:03 2026
read: IOPS=1958, BW=490MiB/s (513MB/s)(9.87GiB/20638msec)Без кэша:
sudo fio --filename=/mnt/storage_noncached_vdd/file.raw --direct=1 --rw=randread --bs=256k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1 --readonlythroughput-test-job: (groupid=0, jobs=4): err= 0: pid=3571: Wed Sep 23 00:16:09 2026
read: IOPS=221, BW=55.3MiB/s (58.0MB/s)(1169MiB/21129msec)Тест на случайное чтение-запись, IOPS
sudo touch /mnt/storage/file_rw.raw
sudo fio --filename=/mnt/storage/file_rw.raw --size=4GiB --direct=1 --rw=randrw --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1iops-test-job: (groupid=0, jobs=4): err= 0: pid=3288: Tue Sep 22 23:55:16 2026
read: IOPS=3048, BW=11.9MiB/s (12.5MB/s)(241MiB/20198msec)
write: IOPS=3066, BW=12.0MiB/s (12.6MB/s)(242MiB/20198msec); 0 zone resetsБез кэша:
sudo fio --filename=/mnt/storage_noncached_vdd/file_rw.raw --size=4GiB --direct=1 --rw=randrw --bs=4k --ioengine=libaio --iodepth=256 --runtime=20 --numjobs=4 --time_based --group_reporting --name=iops-test-job --eta-newline=1iops-test-job: (groupid=0, jobs=4): err= 0: pid=3626: Wed Sep 23 00:20:37 2026
read: IOPS=266, BW=1064KiB/s (1090kB/s)(21.8MiB/20944msec)
write: IOPS=278, BW=1114KiB/s (1141kB/s)(22.8MiB/20944msec); 0 zone resetsТест на случайное чтение-запись, throughput
sudo fio --filename=/mnt/storage/file_rw.raw --size=4GiB --direct=1 --rw=randrw --bs=64k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1throughput-test-job: (groupid=0, jobs=4): err= 0: pid=3327: Tue Sep 22 23:58:07 2026
read: IOPS=1376, BW=86.0MiB/s (90.2MB/s)(1727MiB/20074msec)
write: IOPS=1393, BW=87.1MiB/s (91.3MB/s)(1749MiB/20074msec); 0 zone resetsБез кэша:
sudo fio --filename=/mnt/storage_noncached_vdd/file_rw.raw --size=4GiB --direct=1 --rw=randrw --bs=64k --ioengine=libaio --iodepth=64 --runtime=20 --numjobs=4 --time_based --group_reporting --name=throughput-test-job --eta-newline=1throughput-test-job: (groupid=0, jobs=4): err= 0: pid=3651: Wed Sep 23 00:23:04 2026
read: IOPS=199, BW=12.5MiB/s (13.1MB/s)(258MiB/20700msec)
write: IOPS=210, BW=13.1MiB/s (13.8MB/s)(272MiB/20700msec); 0 zone resetsСводка по результатам тестирования
Тест | /dev/vdb в качестве постоянного хранилища | /dev/vdd как постоянное хранилище, без кэша |
последовательное чтение, IOPS | 2262 | 250 |
последовательное чтение, throughput | 561 MiB/s | 64 MiB/s |
случайное чтение, IOPS | 2128 | 264 |
случайное чтение, throughput | 490 MiB/s | 55 MiB/s |
случайное чтение-запись, IOPS | 3048 read, | 266 read, |
случайное чтение-запись, throughput | 86 MiB/s read, | 12.5 MiB/s read, |
Во всех тестах побеждает вариант с кэшированием. Разумеется, в реальных условиях ситуация, когда все данные, в которые идет IO от приложения, находятся полностью в кэше (как у нас в тесте) в каждый момент времени, будет возникать редко. Итоговые цифры напрямую зависят от хранилища, используемого под кэш — его размера и производительности. Чем он больше, тем больше данных поместится в кэш целиком и тем быстрее они будут отдаваться, и наоборот. Поэтому воспринимать эти результаты следует как наглядную демонстрацию пользы кэширования в bcachefs, а не как референс.
Тестирование на прикладной нагрузке
Для более наглядной демонстрации посмотрим на показатели производительности в хранилище ключ-значение - RocksDB. Этот k/v store хранит в каталоге на файловой системе в виде множества файлов .SST. И в составе этого хранилища есть утилита бенчмарка - db_bench.
Проведем с помощью нее три сравнительных теста:
первичное заполнение базы случайными данными на ~70 GiB;
чтение заполненной базы;
обновление заполненной базы.
Подготовка окружения
В виде готовых бинарей найти не удалось, поэтому собираем вручную.
git clone https://github.com/facebook/rocksdb.git ; cd rocksdb/
sudo apt-get install libgflags-dev
sudo apt-get install libsnappy-dev
sudo apt-get install zlib1g-dev
sudo apt-get install libbz2-dev
sudo apt-get install liblz4-dev
sudo apt-get install libzstd-dev
make releaseПодготовим директории для заполнения базы.
sudo mkdir -p /mnt/storage/rocksdb-bench
sudo mkdir -p /mnt/storage_noncached_vdd/rocksdb-benchЗаполнение базы случайными данными
Используем метод fillrandom для заполнения базы 25 млн. ключами. Размер ключа - 16 байт, размер значения ключа - 4096 байт. Такое заполнение даст нам примерно 98 GiB данных. В качестве показателя производительности будем использовать ops/sec - количество операций записи в секунду, где одна операция это запись одного ключа в RocksDB.
Без кэша
sudo ./db_bench --db="/mnt/storage_noncached_vdd/rocksdb-bench/" --benchmarks=fillrandom --num=25000000 --key_size=16 --value_size=4096 --compression_type=none --compression_ratio=1 --cache_size=268435456 --seed=17 --open_files=512
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Tue Sep 29 22:44:47 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (4096 bytes after compression)
Entries: 25000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 98037.7 MB (estimated)
FileSize: 98037.7 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: NoCompression
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
DB path: [/mnt/storage_noncached_vdd/rocksdb-bench/]
fillrandom : 896.214 micros/op 1115 ops/sec 22405.356 seconds 25000000 operations; 4.4 MB/sС кэшем
sudo ./db_bench --db="/mnt/storage/rocksdb-bench/" --benchmarks=fillrandom --num=25000000 --key_size=16 --value_size=4096 --compression_type=none --compression_ratio=1 --cache_size=268435456 --seed=17 --open_files=512
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Wed Sep 30 11:05:48 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (4096 bytes after compression)
Entries: 25000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 98037.7 MB (estimated)
FileSize: 98037.7 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: NoCompression
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
DB path: [/mnt/storage/rocksdb-bench/]
fillrandom : 239.129 micros/op 4181 ops/sec 5978.235 seconds 25000000 operations; 16.4 MB/sРезультирующий объем данных на файловой системе немного отличается от рассчитанного db_bench, но все равно превышает размер кэша ( > 64 GiB ):
sudo du -sch /mnt/storage/rocksdb-bench/
69G /mnt/storage/rocksdb-bench/
69G total
sudo du -sch /mnt/storage_noncached_vdd/rocksdb-bench/
70G /mnt/storage_noncached_vdd/rocksdb-bench/
70G totalКак видно из результатов, заполнение RocksDB с кэшем bcachefs завершилось в 3.75 раз быстрее:
fillrandom: 896.214 micros/op 1115 ops/sec 22405.356 seconds 25000000 operations; 4.4 MB/s
fillrandom: 239.129 micros/op 4181 ops/sec 5978.235 seconds 25000000 operations; 16.4 MB/sПри этом, в отличие от предыдущего теста fio, мы намеренно использовали объем базы данных больше кэша, чтобы продемонстрировать более реалистичный сценарий, когда часть данных помещается в кэш, а часть не помещается.
Чтение заполненной базы данных
Используем метод readrandom, с 100000 случайных операций чтений. Минуем page cache (опция --use_direct_reads).
Без кэша:
sudo ./db_bench --db="/mnt/storage_noncached_vdd/rocksdb-bench/" --benchmarks=readrandom --use_existing_db=1 --use_existing_keys=1 --reads=100000 --threads=4 --key_size=16 --value_size=4096 --cache_size=268435456 --open_files=512 --use_direct_reads=1 --seed=17
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Wed Sep 30 17:51:37 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (2048 bytes after compression)
Entries: 1000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 3921.5 MB (estimated)
FileSize: 1968.4 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: Snappy
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
DB path: [/mnt/storage_noncached_vdd/rocksdb-bench/]
readrandom : 41835.943 micros/op 95 ops/sec 4188.298 seconds 400000 operations; 0.4 MB/s (100000 of 100000 found)
Microseconds per read:
Count: 400000 Average: 41835.9452 StdDev: 17700.56
Min: 13 Median: 40913.7095 Max: 276074
Percentiles: P50: 40913.71 P75: 55302.70 P99: 100871.02 P99.9: 118833.33 P99.99: 217111.11С кэшем:
sudo ./db_bench --db="/mnt/storage/rocksdb-bench/" --benchmarks=readrandom --use_existing_db=1 --use_existing_keys=1 --reads=100000 --threads=4 --key_size=16 --value_size=4096 --cache_size=268435456 --open_files=512 --use_direct_reads=1 --seed=17
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Wed Sep 30 16:05:43 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (2048 bytes after compression)
Entries: 1000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 3921.5 MB (estimated)
FileSize: 1968.4 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: Snappy
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
DB path: [/mnt/storage/rocksdb-bench/]
readrandom : 5396.803 micros/op 736 ops/sec 542.883 seconds 400000 operations; 2.9 MB/s (100000 of 100000 found)
Microseconds per read:
Count: 400000 Average: 5396.8041 StdDev: 5316.71
Min: 8 Median: 3729.7822 Max: 192307
Percentiles: P50: 3729.78 P75: 6959.66 P99: 24398.90 P99.9: 51431.30 P99.99: 115142.86Случайное чтение с кэшем bcachefs показывает прирост производительности в 7.74 раза (736 ops/sec против 95 ops/sec).
Обновление заполненной базы
Используем метод overwrite, с 50 000 операций перезаписи.
Без кэша
sudo ./db_bench --db="/mnt/storage_noncached_vdd/rocksdb-bench/" --benchmarks=overwrite --use_existing_db=1 --use_existing_keys=1 --writes=50000 --threads=1 --key_size=16 --value_size=4096 --compression_type=none --compression_ratio=1 --cache_size=268435456 --open_files=512 --sync=1 --seed=17
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Wed Sep 30 19:58:26 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (4096 bytes after compression)
Entries: 1000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 3921.5 MB (estimated)
FileSize: 3921.5 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: NoCompression
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
DB path: [/mnt/storage_noncached_vdd/rocksdb-bench/]
overwrite : 4098.578 micros/op 243 ops/sec 204.929 seconds 50000 operations; 1.0 MB/s
Microseconds per write:
Count: 50000 Average: 4098.5796 StdDev: 6168.40
Min: 760 Median: 3722.2553 Max: 855061
Percentiles: P50: 3722.26 P75: 4138.38 P99: 6467.21 P99.9: 9437.38 P99.99: 170000.00С кэшем
sudo ./db_bench --db="/mnt/storage/rocksdb-bench/" --benchmarks=overwrite --use_existing_db=1 --use_existing_keys=1 --writes=50000 --threads=1 --key_size=16 --value_size=4096 --compression_type=none --compression_ratio=1 --cache_size=268435456 --open_files=512 --sync=1 --seed=17
Initializing RocksDB Options from the specified file
Initializing RocksDB Options from command-line flags
Integrated BlobDB: blob cache disabled
RocksDB: version 11.12.0
Date: Wed Sep 30 20:50:38 2026
CPU: 4 * Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
CPUCache: 16384 KB
Keys: 16 bytes each (+ 0 bytes user-defined timestamp)
Values: 4096 bytes each (4096 bytes after compression)
Entries: 1000000
Prefix: 0 bytes
Keys per prefix: 0
RawSize: 3921.5 MB (estimated)
FileSize: 3921.5 MB (estimated)
Write rate: 0 bytes/second
Read rate: 0 ops/second
Compression: NoCompression
Compression sampling rate: 0
Memtablerep: SkipListFactory
Perf Level: 1
------------------------------------------------
DB path: [/mnt/storage/rocksdb-bench/]
overwrite : 1033.723 micros/op 967 ops/sec 51.686 seconds 50000 operations; 3.8 MB/s
Microseconds per write:
Count: 50000 Average: 1033.7239 StdDev: 326.13
Min: 710 Median: 1065.4883 Max: 29081
Percentiles: P50: 1065.49 P75: 1202.16 P99: 1934.40 P99.9: 3770.00 P99.99: 12881.82967 ops/sec с кэшем bcachefs против 243 ops/sec без кэша - разница составляет почти 4 раза.
Итоги
Если у вас уже где-то работает bcachefs на медленном хранилище (например, HDD), но вы по какой-то причине не используете кэширование, самое время это исправить — даже небольшой SSD способен дать огромную прибавку к производительности.
Поскольку реализация полностью программная, вам не нужно завязываться на аппаратные возможности storage-контроллера.
В прикладной нагрузке (RocksDB с объемом базы больше объема кэша) использование кэша bcachefs показало существенный рост производительности: примерно в 3.75 раз быстрее запись случайных пар ключ-значение и до 7.74 раз быстрее чтение случайных пар ключ-значение.
А если тема производительности зашла, 20 октября у нас будет онлайн-митап по теме – сравним nv1 с gp2 и io2 в синтетических тестах на fio (IOPS, latency, throughput), покажем, как устроен сетевой нереплицируемый диск изнутри, поднимем PostgreSQL под нагрузкой и разберем, что происходит при отказе диска или узла. О том, как вписаться на этот движ — по ссылке. Присоединяйтесь!
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.