Производительность BTRFS по сравнению с LVM + EXT4 с учетом рабочих нагрузок базы данных
Введение
Во многих сборках баз данных резервные копии представляют собой очень большую проблему. Большинство систем резервного копирования требуют эксклюзивной блокировки таблиц и не имеют поддержки для инкрементных резервных копий; они требуют полной резервной копии каждый раз. Когда размер базы данных увеличивается до нескольких терабайт, это становится огромной проблемой. Обычным решением для этого является использование снимков. В облаке это довольно просто, так как облачная платформа может принимать моментальные снимки, сохраняя при этом определенный уровень производительности. В центре обработки данных существует несколько хороших решений. Одним из часто используемых методов является использование LVM в Linux для выполнения моментального снимка на уровне блочного устройства.
Снимки LVM
LVM - это технология Linux, которая позволяет осуществлять расширенные манипуляции с блочными устройствами, включая разделение блочных устройств на многие более мелкие, и объединять более мелкие блочные устройства с более крупными с помощью методов конкатенации или чередования, которые включают избыточное чередование, обычно называемое RAID. В дополнение к этому, технология также поддерживает функцию копирования на запись (CoW), которая позволяет делать снимки. Метод, используемый для реализации этого, состоит в том, чтобы выделить раздел базовых физических томов, на которые копируются исходные данные, перед обновлением основного логического тома.
BTRFS
Согласно wiki: «Btrfs - это современная файловая система Copy-on-Write (CoW) для Linux, нацеленная на внедрение расширенных функций, а также внимание к отказоустойчивости, ремонту и простому администрированию». Это по своей сути файловая система CoW, что означает, что она поддерживает моментальный снимок на уровне файловой системы в дополнение ко многим более продвинутым функциям.
Эксперимент 1: Простой контрольный показатель
Гипотеза
Поскольку и LVM с моментальными снимками, и Btrfs являются CoW, было бы разумно, что решение, предоставляющее функции на более высоком уровне, будет более производительным и обеспечит большую гибкость по сравнению с тем, что на более низком уровне, для которого есть меньше информации для оптимизации. Из-за этого Btrfs должен работать лучше или, по крайней мере, аналогично, и обеспечивать большую гибкость и упрощать управление.
Эксперимент
Эксперимент состоял из специально написанного сценария, который мог бы выделять большой блок данных, приостанавливать, чтобы можно было сделать снимок, а затем случайным образом обновлять разделы большого блока данных. Пользовательский сценарий был выбран потому, что существует несколько тестов, которые позволяют сделать паузу между этапами инициализации и тестирования. На LVM была создана файловая система EXT4, созданная с использованием следующих флагов: -E lazy_itable_init=0,lazy_journal_init=0. Btrfs был создан с использованием параметров по умолчанию. Сценарий производится следующим образом:
import multiprocessing
import datetime
import random
EXTENT_SIZE = 4000
EXTENTS = 100000000000 / EXTENT_SIZE
THREADS = 8
FRAGMENT_EXTENTS = 250000
def thread_setup(file):
global urandom
global output
urandom = open(‘/dev/urandom’, ‘rb’)
output = open(file, ‘w+b’)
def fill_random(args):
output.seek(args[‘start’] * EXTENT_SIZE)
for i in range(args[‘size’]):
output.write(urandom.read(EXTENT_SIZE))
output.flush()
def fill_random_list(extents):
for extent in extents:
output.seek(extent * EXTENT_SIZE)
output.write(urandom.read(EXTENT_SIZE))
output.flush()
if __name__ == ‘__main__’:
p = multiprocessing.Pool(THREADS, thread_setup(‘test’))
args = []
for i in range(THREADS):
args.append({‘start’: int((EXTENTS/THREADS)*i), ‘size’: int(EXTENTS/THREADS)})
start = datetime.datetime.now()
# Fill a test file
p.map(fill_random, args, chunksize=1)
end = datetime.datetime.now()
print(end – start)
print(“File made, please make a snapshot now.”)
input(“Press enter when snapshot made.”)
# Randomly fragment X pages
extents = list(range(EXTENTS))
random.shuffle(extents)
extents = extents[:FRAGMENT_EXTENTS]
start = datetime.datetime.now()
p.map(fill_random_list, extents)
end = datetime.datetime.now()
print(end – start)
# Finally, a big linear seek
start = datetime.datetime.now()
with open(‘test’, ‘rb’) as f:
for i in range(EXTENTS):
f.read(EXTENT_SIZE)
end = datetime.datetime.now()
Это было протестировано на выделенном сервере, чтобы удалить столько абстракций, которые могли бы привести к ошибкам измерений, сколько возможно. Он также выполнялся на одном вращающемся диске, что означает, что случайный поиск, вызванный CoW, должен быть усилен по сравнению с SSD. Кроме того, фрагментация была собрана с помощью утилиты filefrag как до теста обновления, так и после.
Результаты
Результаты приведены ниже:
Значение |
LVM |
BTRFS |
Ratio |
Время первоначальногосоздания |
0:22:09.089155 |
0:28:43.236595 |
0.7712749130655504 |
Время для случайного обновления |
0:03:22.869733 |
0:01:55.728375 |
1.7529817816935562 |
Линейное чтение после обновления |
0:16:46.113980 |
0:04:54.382375 |
3.4177113354697273 |
Фрагментация перед обновлением |
69 экстентов |
100 экстентов |
0.69 |
Фрагментация после обновления |
70576 экстентов |
63848 экстентов найдено |
1.1053752662573613 |
Btrfs занял немного больше времени, чтобы сделать первоначальное создание, что ожидаемо, так как CoW не было на месте в настоящее время для LVM, то есть Btrfs имеет больше накладных расходов. Для основной части гипотезы Btrfs был на 75% быстрее, чем LVM в рабочей нагрузке обновления, что согласуется с гипотезой. Что более удивительно, так это то, что Btrfs был на 342% быстрее, чем LVM на однопоточном линейном считывании после теста обновления. Это можно объяснить тем, что Btrfs имеет более агрессивную политику чтения, чем EXT4 или LVM. Еще одна удивительная находка заключалась в том, что после обновления Btrfs имело на 9,5% меньше фрагментированных экстентов, чем EXT4, что может объяснить часть замедления. Если фрагментация несет исключительную ответственность за замедление, то, используя закон Ахмдаля, операция по фрагментированному экстенду должна быть в среднем на 3600% медленнее, чем по нефрагментированному.
Эксперимент 2: Тест в реальных условиях
С успехом предыдущего эксперимента был сделан более реальный тест.
Гипотеза
Гипотеза заключается в том, что предыдущие результаты будут поддерживаться с использованием более приближенного к реалиям инструмента сравнения.
Эксперимент
Был выбран blogbench в качестве тестовой платформы, поскольку он обеспечивает хорошее сочетание как линейных, так и случайных операций записи и чтения. Было использовано 10 ГБ пространства, которое было равно 136 итерациям. Для теста был использован Blogbench 1.1. Для автоматизации процесса тестирования использовался следующий сценарий:
#!/bin/sh iterations=30 # Do BTRFS mkfs.btrfs /dev/sdb mount /dev/sdb /mnt cd /root/blogbench-1.1/src ./blogbench -d /mnt -i $iterations | tee ~/btrfs.blogbench.initial btrfs subvolume snapshot /mnt/ /mnt/snapshot ./blogbench -d /mnt -i $iterations | tee ~/btrfs.blogbench.snapshot umount /mnt wipefs -a /dev/sdb # Do LVM pvcreate /dev/sdb vgcreate vg0 /dev/sdb lvcreate -l 75%FREE -n lv0 vg0 mkfs.ext4 -E lazy_itable_init=0,lazy_journal_init=0 /dev/vg0/lv0 mount /dev/vg0/lv0 /mnt cd /root/blogbench-1.1/src ./blogbench -d /mnt -i $iterations | tee ~/lvm.blogbench.initial lvcreate -l +100%FREE –snapshot -n lv0snap vg0/lv0 ./blogbench -d /mnt -i $iterations | tee ~/lvm.blogbench.snapshot umount /mnt lvremove -f /dev/vg0/lv0snap lvremove -f /dev/vg0/lv0 vgremove /dev/vg0 wipefs -a /dev/sdb |
Результаты
Результаты приведены ниже:
Значение |
LVM |
BTRFS |
Ratio |
Начальный показатель чтения |
167695 |
346567 |
0.4838746908967097 |
Начальный показатель записи |
1155 |
1436 |
0.8043175487465181 |
Показатель чтения после снимка |
88398 |
233204 |
0.37905867823879524 |
Показатель записи после снимка |
848 |
964 |
0.8796680497925311 |
В этом тесте Btrfs превзошли LVM в каждом тестировании. Более высокие показатели лучше. Btrfs был на 107% быстрее в начальных показателях чтения и на 24% быстрее в начальных показателях записи. Это также было на 164% быстрее при чтении после снимка и на 17% быстрее при записи после снимка. Это коррелирует с предыдущим экспериментом и гипотезой. Еще одна вещь, которую следует отметить, заключается в том, что после моментального снимка LVM сильно пострадали от проблем с блокировкой, когда для нескольких итераций ничего не произошло, как показано здесь:
[root@btrfs-test ~]# cat lvm.blogbench.snapshot
Frequency = 10 secs
Scratch dir = [/mnt]
Spawning 3 writers…
Spawning 1 rewriters…
Spawning 5 commenters…
Spawning 100 readers…
Benchmarking for 30 iterations.
The test will run during 5 minutes.
Nb blogs R articles W articles R pictures W pictures R comments W comments
351 255030 17729 222611 19185 174246 354
519 38783 8539 32165 8203 20519 0
521 91712 195 75868 225 52156 486
524 265205 44 219897 61 147229 0
524 312 0 257 0 264 0
524 0 0 0 0 0 0
524 0 0 0 0 0 0
524 0 0 0 0 0 0
524 0 0 0 0 0 0
524 0 1 0 0 0 0
524 0 0 0 0 0 0
524 0 49 0 44 0 61
542 204263 869 170643 1062 113274 2803
576 263147 1805 218163 1715 142694 1409
601 223393 1474 186252 1326 120374 0
630 229142 1252 191061 1876 122406 0
658 230185 1437 191368 1241 117970 0
693 294852 2044 240333 1635 144919 488
737 330354 2093 272406 2153 174214 805
778 379635 1635 313989 1963 184188 0
812 302766 1697 248385 1608 151070 0
814 385820 97 316903 143 184704 0
814 275654 0 228639 0 132450 0
814 412152 0 340600 0 195353 0
814 276715 0 227402 0 131327 0
842 230882 1243 191560 1226 113133 1314
848 274873 209 226790 296 126418 257
848 355217 0 291825 0 168253 0
848 237893 0 196491 0 110130 0
848 396703 0 323357 0 179002 0
Final score for writes: 848
Final score for reads : 88398
Размышления и окончательные примечания
Оба этих теста показывают, что Btrfs превосходит LVM с точки зрения производительности при наличии снимков. Причина этого на самом деле довольно интуитивна и связана с методом внедрения CoW в системах. Причина этого на самом деле довольно интуитивна и связана с методом внедрения CoW в системах. В LVM CoW достигается путем первого копирования блока с основного логического тома на логический том моментального снимка, а затем обновления основного логического тома. Для этой операции требуется одно чтение и две записи для обновления одного блока! Btrfs делает это лучше, используя структуру данных, структурированную в логах для записи, что означает, что для обновления требуется только одна линейная запись. Это объясняет, почему первоначальное время создания в Эксперименте 1 было в целом так похоже: накладные расходы были не в CoW, а в контрольной сумме данных и других функциях. Это также объясняет, почему Btrfs был намного быстрее, чем LVM в режиме CoW. Использование системы CoW, когда это не обязательно, приводит к серьезной деградации производительности, особенно при работе с базами данных. Но если вы все равно будете внедрять CoW, то будет разумно использовать систему CoW, которая работает на уровне файловой системы или выше. Примером более высокой, чем файловая форма CoW, будет та, которая использует CoW в механизме базы данных для создания снимков. Некий тип имеющей имя постоянной транзакции, на которую можно ссылаться.