WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
SQLite читает и пишет маленькие blob (например, изображения миниатюр)
на 35% быстрее, чем те же самые объекты
могут быть прочитаны или написаны с отдельными файлами на диске через
fread() или fwrite(). Кроме того, единственная база данных SQLite, содержащая 10-килобайтные
blob, использует приблизительно на 20% меньше дискового пространства,
чем хранение blob в отдельных файлах. Различие в производительности возникает (мы верим), потому что, работая от
базы данных SQLite, системные вызовы open() и close()
вызваны только однажды, тогда как open() и close()
вызваны однажды для каждого blob, сохраненных в отдельных файлах.
Кажется, что издержки запроса open() и close() больше, чем издержки
использования базы данных. Сокращение размера является результатом того, что
отдельные файлы увеличены к следующему кратному числу размера блока файловой
системы, тогда как blob упакованы более плотно в базу данных SQLite. Измерения в этой статье были сделаны в течение недели 2017-06-05
использования версий SQLite между 3.19.2 и 3.20.0. Можно ожидать, что будущие
версии SQLite выступят еще лучше. Число 35% выше приблизительно. Фактическое время варьируется в зависимости
от аппаратных средств, операционной системы и деталей эксперимента
и из-за случайных исполнительных колебаний на реальных аппаратных средствах.
См. текст ниже для большего количества деталей.
Попробуйте эксперименты сами. Сообщите о значительных отклонениях
на SQLite forum. 35% основано на запуске тестов на каждой машине, которую автор имеет под
рукой. Некоторые рецензенты этой статьи сообщают, что у SQLite есть более
высокое время ожидания, чем прямой I/O на их системах. Мы еще не понимаем
различия. Мы также видим признаки, что SQLite не использует прямой I/O, когда
экспериментами управляют, используя холодный кэш файловой системы. Время ожидания чтения-записи для SQLite конкурентоспособно по отношению ко
времени ожидания чтения-записи отдельных файлов на диске.
Часто SQLite быстрее. Иногда SQLite почти как быстро. Так или иначе эта
статья опровергает общее предположение, что реляционная база данных должна
быть медленнее, чем I/O прямой файловой системы.
2022 study (
альтернативная ссылка на GitHub) нашло, что SQLite примерно вдвое
более быстр при реальной рабочей нагрузке по сравнению с
Btrfs и Ext4 в Linux. Jim Gray
и другие изучили производительность чтения BLOB
против файлового ввода-вывода для Microsoft SQL Server и нашли, что чтение
BLOB из базы данных было быстрее для размеров BLOB меньше, чем между 250
250 KB и 1 MB (
здесь). В том исследовании база данных все еще хранит имя файла
содержания, даже если содержание проводится в отдельном файле. Таким образом,
с базой данных консультируются для каждого BLOB, даже если это только, чтобы
извлечь имя файла. В этой статье ключ для BLOB это имя файла, таким образом,
никакой предварительный доступ к базе данных не требуется.
Поскольку база данных никогда не используется вообще, читая содержание от
отдельных файлов в этой статье, порог где прямой доступ I/O к файлу
становится быстрее, меньше, чем в статье Грэя. Статья Internal Versus External BLOBs
на этом веб-сайте это более раннее исследование (приблизительно 2011),
которое использует тот же самый подход, как в статье Джима Грэя,
хранение имен файлов blob как записи в базе данных,
но для SQLite вместо SQL Server. Производительность I/O измерена, используя программу
kvtest.c из
исходного дерева SQLite. Чтобы собрать эту тестовую программу, сначала
поместите исходный файл kvtest.c в каталог с исходными файлами
объединения SQLite "sqlite3.c" и "sqlite3.h".
Затем в unix выполните: Или в Windows с MSVC: Инструкции для сборки в Android
даны ниже. Используйте получающуюся программу "kvtest", чтобы произвести
испытательную базу данных с 100000 случайных несжимаемых blob,
каждый со случайным размером между 8000 и 12000 байт, используя команду: При желании можно проверить новую базу данных, управляя этой командой: Затем, превратите копии всех blob в отдельные файлы в
каталоге, используя команду: В этом пункте можно измерить сумму дискового пространства,
используемого базой данных test1.db и каталогом test1.dir со
всем его содержанием. На стандартном Ubuntu Linux файл базы данных составил
1,024,512,000 байт, каталог test1.dir 1,228,800,000 байт (согласно
"du -k"), приблизительно на 20% больше, чем база данных. Каталог "test1.dir", созданный выше, помещает все blob
в единственную папку. Это было предугадано, что некоторые операционные
системы выступят плохо, когда единственный каталог
будет содержать 100000 объектов.
Чтобы проверить это, программа kvtest может также сохранить blob
в иерархии папок не больше, чем по 100 файлов и/или подкаталогов на папку.
Альтернативное представление на диске blob может быть создано, используя
параметр командной строки --tree для команды "export": Каталог test1.dir будет содержать 100000 файлов с именами
"000000", "000001", "000002" и т.д., но test1.tree
будет содержать те же самые файлы в подкаталогах
"00/00/00", "00/00/01" и т.д. Каталог test1.dir и test1.test
занимают приблизительно ту же самую сумму места, хотя test1.test
очень немного больше из-за дополнительных записей каталога. Все эксперименты, которые следуют, управляют тем же самым
"test1.dir" или "test1.tree".
Очень мало различия в производительности измерено в любом
случае, независимо от операционной системы. Измерьте производительность для чтения blob
от базы данных и от отдельных файлов, используя эти команды: В зависимости от вашей аппаратной и операционной системы необходимо
видеть, что это читает из файла базы данных test1.db
приблизительно на 35% быстрее, чем читает от отдельных файлов в test1.dir
или test1.tree. Результаты могут измениться значительно от одного пробега
до следующего из-за кэширования. Таким образом, желательно запустить тесты
многократно и взять среднее число или худший случай или лучший случай, в
зависимости от ваших требований. Опция --blob-api на тесте чтения базы данных заставляет kvtest
использовать sqlite3_blob_read()
в SQLite, чтобы загрузить содержание blob
вместо того, чтобы управлять чистыми SQL-операторами. Это помогает SQLite
работать немного быстрее на тестах чтения. Можно опустить тот выбор, чтобы
сравнить исполнение SQLite, управляющего SQL-операторами.
В этом случае SQLite все еще выигрывает прямое чтение, хотя не так, как
используя sqlite3_blob_read().
Опция --blob-api проигнорирована для тестов, которые читают файлы с диска. Измерьте производительность записи, добавив опцию --update.
Это вызывает перезапись blob на месте другим случайным blob
точно того же самого размера. Тест на запись выше не абсолютно справедлив, так как SQLite делает
безопасные по питанию транзакции, тогда как
запись прямо на диск этого не умеет.
Чтобы поместить тесты в более равные условия, добавьте опцию --nosync, чтобы
отключить запрос fsync() или FlushFileBuffers(),
чтобы вынудить сброс содержания к диску или используйте опцию --fsync
для тестов прямой записи на диск, чтобы вынудить их вызвать fsync() или
FlushFileBuffers(), обновляя дисковые файлы. По умолчанию kvtest управляет измерениями I/O базы данных в одной
транзакции. Используйте опцию --multitrans, чтобы каждое чтение или запись
выполнялась в отдельной транзакции. Опция --multitrans
делает SQLite намного медленнее и неконкурентоспособным по отношению к
прямому дисковому I/O. Этот выбор доказывает, что чтобы получить большую
производительность SQLite, необходимо сгруппировать как можно больше
взаимодействия базы данных в единственной транзакции. Есть много других вариантов тестирования, которые могут быть
найдены, управляя командой: Диаграмма ниже показывает данные, собранные, используя
kvtest.c
на пяти различных системах: Все машины используют SSD кроме Win7, у которой есть жесткий диск.
Испытательная база данных это 100K blob
с размерами, однородно распределенными между 8K и 12K для в общей
сложности приблизительно 1 гигабайта содержания.
Размер страницы базы данных составляет 4 KB. Выбор времени компиляции
-DSQLITE_DIRECT_OVERFLOW_READ использовался для всех этих тестов.
Тесты были запущены многократно. Первый показ использовался, чтобы нагреть
кэш, и от его времени отказались. Диаграмма ниже показывает среднее время, чтобы прочитать blob
непосредственно от файловой системы против времени, чтобы прочитать тот же
самый blob из БД SQLite.
Фактическое время варьируется значительно от одной системы до другой
(Ubuntu desktop намного быстрее, чем телефон Galaxy S3, например).
Эта диаграмма показывает отношение времени, чтобы прочитать данные из файла,
разделенное на время, чтобы прочитать от базы данных.
Крайний левый столбец в диаграмме это
нормализованное время, чтобы читать от базы данных для справки. В этой диаграмме, SQL-оператор ("SELECT v FROM kv WHERE k=?1")
подготовлен однажды. Затем для каждого blob, значение ключа blob связано с
параметром ?1, и запросы оценены, чтобы извлечь содержание blob. Диаграмма показывает, что на Windows10, содержание может быть прочитано из
базы данных SQLite приблизительно в 5 раз быстрее, чем это может быть
прочитано непосредственно с диска. На Android SQLite приблизительно только на
35% быстрее, чем чтение с диска. Работа может быть улучшена немного, обойдя слой SQL и читая содержание
непосредственно, используя sqlite3_blob_read()
, примерно вот так: Дальнейшая работа улучшается при помощи особенности
memory-mapped I/O SQLite.
В следующей диаграмме весь файл базы данных 1GB с отображенной памятью, и
blob прочитаны (в произвольном порядке) с использованием
sqlite3_blob_read().
С этой оптимизацией SQLite вдвое быстрей Android или MacOS X
и более чем в 10 раз быстрее, чем Windows. Третья диаграмма показывает, что чтение содержания из SQLite может
быть вдвое быстрей, чем читать от отдельных файлов на диске для Mac и
Android и в десять раз быстрее для Windows. Пишет медленнее. На всех системах, используя прямой I/O и SQLite,
производительность записи от 5 до 15 раз медленнее, чем чтение. Измерения производительности записи были сделаны, заменив (переписав)
каждый blob другим. Все blob случайны и несжимаемы.
Поскольку запись медленнее, только 10000 из этих 100000 blob
в базе данных заменяются. blob, которые будут заменены, отобраны наугад и
без определенного порядка. Прямые записи на диск достигаются, используя fopen()/fwrite()/fclose().
По умолчанию, и во всех результатах, показанных ниже, буфера файловой
системы OS никогда не сбрасываются к постоянному хранению, используя fsync()
или FlushFileBuffers(). Другими словами, нет никакой попытки сделать
прямую запись на диск транзакционной или безопасной по питанию.
Мы нашли, что вызывая fsync() или FlushFileBuffers()
на каждом файле запись будет
приблизительно в 10 или более раз медленнее, чем пишет SQLite. Следующая диаграмма сравнивает обновления базы данных SQLite в
режиме WAL против сырой прямой дисковой перезаписи
отдельных файлов на диске.
PRAGMA synchronous = NORMAL.
Вся запись базы данных в единственной транзакции.
Таймер для записи базы данных остановлен после того, как транзакция
коммитится, но прежде чем запущена контрольная точка
. Обратите внимание на то, что SQLite пишет, в отличие от прямой записи
на диск транзакционно и
безопасно, хотя
потому что синхронное урегулирование NORMAL вместо FULL,
транзакции не длительны. Показатели производительности android для опыта с записью
опущены, потому что тесты производительности на Galaxy S3
случайны. Два последовательных пробега того же самого эксперимента дали бы
дико различные времена. И честно говоря, исполнение SQLite на андроиде
немного медленнее, чем запись непосредственно на диск. Следующая диаграмма показывает исполнение SQLite против прямой записи
на диск, когда транзакции отключены (
PRAGMA journal_mode=OFF) и
PRAGMA synchronous = OFF.
Эти параметры настройки помещают SQLite в равные условия с прямой записью на
диск. Это делает данные подверженными повреждениям
из-за системных катастроф и перебоев в питании. Во всех тестах записи важно отключить антивирусное программное
обеспечение до управления тестами производительности прямой записи на диск.
Мы нашли, что антивирусное программное обеспечение замедляет
прямую запись на диск на порядок, тогда как оно влияет на записи
SQLite очень мало. Это, вероятно вследствие того, что изменения на диске
тысячи отдельных файлов, который все надо проверить антивирусом,
тогда как SQLite пишет изменения только в файл единой базы данных. Выбор времени компиляции
-DSQLITE_DIRECT_OVERFLOW_READ заставляет SQLite обходить свой кэш
страницы, читая содержание со страниц переполнения. Это помогает базе данных
читать 10K blob немного быстрее, но не все это намного быстрее.
SQLite все еще держит преимущество по скорости перед прямым чтением
файловой системы без выбора времени компиляции
SQLITE_DIRECT_OVERFLOW_READ. Другие варианты времени компиляции, такие как использование -O3 вместо
-Os или применение -DSQLITE_THREADSAFE=0
и/или некоторые из других
рекомендуемых вариантов времени компиляции
могли бы помочь SQLite работать еще быстрее относительно
прямой файловой системы. Размер blob в данных тестирования затрагивает производительность.
Файловая система обычно будет быстрее для больших blob,
так как издержки open() и close()
амортизируется по большему количеству байтов I/O, тогда как база данных будет
более эффективной в скорости, когда средний размер blob меньше. SQLite конкурентоспособен и обычно быстрее, чем blob,
сохраненные в отдельных файлах на диске, для чтения и записи. SQLite намного быстрее, чем прямая запись на диск в Windows,
когда антивирусная защита включена. Так как антивирусное программное
обеспечение должно быть включено по умолчанию в Windows, это
означает, что SQLite обычно намного быстрее, чем прямые записи
на диск в Windows. Чтение на порядок быстрее, чем запись для всех систем, как
для SQLite, так и для I/O прямо на диск. Производительность I/O значительно различается в зависимости от
операционной системы и аппаратных средств. Сделайте свои собственные
измерения прежде, чем делать выводы. Некоторые другие СУБД SQL советуют разработчикам хранить blob
в отдельных файлах и затем хранить имя файла в базе данных.
В этом случае нужно сначала консультироваться с базой данных, чтобы найти
имя файла прежде, чем открыть и прочитать файл, простое хранение blob
в базе данных дает намного более быстрое чтение
и производительность записи с SQLite. См. статью
здесь для подробностей. Программа kvtest собрана и выполнена на Android
следующим образом. Сначала установите Android SDK и NDK.
Затем подготовьте скрипт "android-gcc": Сделайте его исполняемым и поместите его в $PATH.
Теперь соберите программу kvtest следующим образом: Затем переместите получающийся исполняемый файл kvtest-android
в устройство на базе Android: Теперь примените "adb shell", чтобы получить приглашение оболочки на
устройстве на базе Android, перейдите в /data/local/tmp
и начните запускать тесты как с любым другим unix.
Choose any three.
1. Обзор
1.1. Протесты
1.2. Связанные исследования
2.
Как эти измерения сделаны
gcc -Os -I. -DSQLITE_DIRECT_OVERFLOW_READ \ kvtest.c sqlite3.c \
-o kvtest -ldl -lpthread
cl -I. -DSQLITE_DIRECT_OVERFLOW_READ kvtest.c sqlite3.c
./kvtest init test1.db --count 100k --size 10k --variance 2k
./kvtest stat test1.db
./kvtest export test1.db test1.dir
./kvtest export test1.db test1.tree --tree
./kvtest run test1.db --count 100k --blob-api
./kvtest run test1.dir --count 100k --blob-api
./kvtest run test1.tree --count 100k --blob-api
./kvtest run test1.db --count 100k --update
./kvtest run test1.dir --count 100k --update
./kvtest run test1.tree --count 100k --update
./kvtest help
2.1. Измерения производительности чтения
Диаграмма 1: Чтение SQLite, время ожидания относительно
прямой файловой системы.
100K blob, avg 10KB каждый, произвольный порядок, используя SQL.
Диаграмма 2: Чтение SQLite, время ожидания относительно
прямой файловой системы.
100K blob, avg 10KB, произвольный порядок через
sqlite3_blob_read().
Диаграмма 3: Чтение SQLite, время ожидания относительно
прямой файловой системы.
100K blob, avg 10KB, произвольный порядок
использование
sqlite3_blob_read() из memory-mapped БД.2.2.
Измерения производительности записи
Диаграмма 4: Запись SQLite, время ожидания относительно прямой
записи файловой системы.
10K blob, в среднем размер 10 КБ, произвольный порядок,
режим WAL с синхронным NORMAL,
исключено время контрольной точки.
Диаграмма 5: Запись SQLite, время ожидания относительно прямой
записи файловой системы.
10K blob, в среднем размер 10 КБ, произвольный порядок,
журналирование отключено, synchronous OFF.2.3. Изменения
3. Общие результаты
4. Дополнительные примечания
4.1.
Компилирование и тестирование на Android
#!/bin/sh
#
NDK=/home/drh/Android/Sdk/ndk-bundle
SYSROOT=$NDK/platforms/android-16/arch-arm
ABIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
GCC=$ABIN/arm-linux-androideabi-gcc
$GCC --sysroot=$SYSROOT -fPIC -pie $*
android-gcc -Os -I. kvtest.c sqlite3.c -o kvtest-android
adb push kvtest-android /data/local/tmp