Disk IO Perfomance, hi load
«in-memory» — это сейчас звучит часто и со всех сторон.
Но что, если с диском НАДО работать в вашем ПО ? Какие либы использовать? Какие движки? Быстрее какого предела не подняться в принципе ? Какие основные проблемы здесь имеет программист?
Я попробую приподнять звесу и поделиться своим опытом.
Производительность IPC
Сначала надо начать с самой ОС и ее ввода-вывода. И даже с оверхеда на системный вызов.
Внутри самого компа (box) используются IPC методы для ввода-вывода, чаще всего они проходят, не затрагивая дисковый ввод-вывод.
IPC — inter process communication; ru «методы межпроцессного обмена»
Тут сразу приведу странные факты о влиянии железа. Два теста: первый проходил в старые времена, второй — гораздо свежее.
- первый 1996 год http://www.gerhardmueller.de/docs/UnixCommunicationFacilities/ip/node10.html
- второй May 12, 2014 http://www.programering.com/a/MTO0AzMwATI.html
Оба они показывают задержки по работе с IPC. И !
Как была базовая линия задержки (в целом) 64-200 мкс, так и осталась!
Пропускная способность возросла, но задержка, считай, никак не поменялась за эти годы. Уже ядра другие, ОС развивались, железо лучше, а вот так — и это заставляет задуматься(было от 64-200, или 300, стало 40-60 — что очень не существенно).
Что характерно, в тестах 96 года задержка на системный вызов — до 3-х мкс, а это по сравнению с 64 или 512 сильно меньше. Понятно, что эта разница как раз скрыта в работе ядра ОС, а это переключения контекстов в том числе, работа планировщиков… Планировщики — отдельный разговор.
Пропускная же способность сильно возросла! Но слабый рост по задержке уже тогда, а сейчас тем более, создает главную и большую проблему. При рабте с диском умножай задержку на 100, 1000, 10000 и это зависит от загрузки диска, его очереди, размера и скорости кеша, числа головок, и даже хваленый SSD требует большой коэффициент для домножения. Batch mode помогает, но где вы его реально сами-то используете?
Как только нужен random access, сразу эта задержка бьёт по пальцам. В наше время с этим стали бороться словосочетанием «in-memory» или методами memory mapping, как это делает LMDB, например. В итоге, работа с диском сильно страдает.
Обсуждение тестов
Обсуждений здесь не будет.
Делайте сами свои тесты. Не доверяйте чужим картинкам, запускайте сами тесты и получайте эти картинки на своих тачках.
Прочих тестов полно и можно почитать. Там все разжевано. Да, не очень ясны причины получаемых цифр, можно спорить о методах тестирования — мне сейчас на это наплевать.
Практике мало помогает смотреть чужие цифры — все склоняют, что их бд или движок быстрее. Давайте рассматривать решения, которые улучшат Вашу архитектуру ПО. Поэтому этот раздел пуст.
Advanced IO
Я рекомендую всем ИТ-специалистам хотябы прочитать этот раздел -Linux Programming. Ссылка: https://www.safaribooksonline.com/library/view/linux-system-programming/9781449341527/ch04.html
Эти методы надо знать и понимать:
- Scatter/gather I/O
- Epoll
- Memory-mapped I/O
- File advice
- Asynchronous I/O
Но их использование и даже изучение на уровне C API — тяжелая задача. Пользоваться этим в таком виде будете, когда будете писать движок БД, например. Кроме того, возможности эти очень низкого уровня.
Планировщик ввода-вывода на уровне приложения тут, как бы, напрашивается методика работы со структурами блоков данных и т.д.
Чтобы проблема встала перед вашими глазами, четче приведу пример задержки доступа при random access (случйном порядке доступа) к диску.
Хорошей скоростью считается результат, достигаемый движками Sophia и RocksDB. Чистый результат всего 120-200~ чтений в секунду и 5-8 мс задержка на одну операцию. Это очень близко к аппаратному seek time диска.
Данные результаты замерялись на таком железе:
cpu | Intel(R) Xeon(R) CPU E5-2609 0 @ 2.40GHz cpu |
ram | 96Gb |
storage | SATA hard disk drive WD4000FYYZ-01UL1B0 4Tb |
file system | XFS |
Тест | |
number of operations | 2 billion |
operations | set, iterate, get |
key order | random |
key size | 16 bytes |
value size | 32 bytes |
data compression | off |
Чтобы достигать реальных результатов, наверное, не стоит начинать с Advanced IO — уже есть эффективные движки для IO, которые я хочу рассмотреть.
LevelDB и клоны
Мы использовали LevelDB и его «клоны» (RocksDB, HyperLevelDB) в своих проектах.
Считается очень хорошей штукой, учитывая, что он сделан внутри как дерево, производительность удивляет, но и она не так хороша. Упомянутые «клоны» работают внутри чуток по-другому, иначе оттюнены, но API то же. Детали этих реализаций таковы, что я не могу дать рецепт, как выбрать среди них (клонов).
Но рекомендую сделать приложение-тест для вашей задачи и погонять, меняя бекенд хранения.
Замеры надо делать для больлших записей и малых, для большого числа записей и малого. Когда данные влезают в память и когда уже не влезают, разная интенсифность доступа. И посмотреть, какой из бекендов больше всего Вас устраивает для вашей конкретной задачи. Обязтельно сделайте это — разница может оказаться большой.
TC — Tokyo Cabinet
Эта матрешка собирается так:
HDB (K|V HASH) <<<< BTREE (K/V ordered tree) <<<<< TABLE with columns and query)
Самым элементарным считается Hash. Работает и правда очень быстро, хорошая штука.
BTree — подвижный, но можно лучше. Можете использовать. Разрешаю 😉 , только следите за скоростью все время.
Table — вообще не пользуйтесь, даже если он соблазняет. Провал в скорости огромен (хотя при разработке не заметен), тормоза есть, считайте, что его нет в TC.
В доп. преимущества скажу, что есть еще TC Dystopia и префиксные деревья с запросами, которые можо использовать для эффективных индексов, тегирования, поиска…
Есть биндинги к языкам, я пользовался lua биндингом. Если длаете продукт с этой штукой, то учтите, что собирать ее на машине придется каждый раз gcc надо, либу собрать, make install для либы, для биндинга, поставить либы с зависимостями, само оно нигде с полпинка не стоит и apt-get не поможет вам.
Но я запомнил для себя их дисковый хеш, насчет остального — сомнения.
BDB — Berkeley DB
BDB я использовал из Python биндинга для индексирования большой кучи xml связанными ссылками.
Когда я вместо него попробовал TC, понял, что TC быстрее, а BDB — тормоз (в том варианте как было у меня).
Однако, BDB умеет мимикрировать под API SQLITE http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#berkeleydb-backend под который есть ORM, а сам он умеет хорошо скелится (scale) по машинам… Вам это может очень в жизни пригодится 😉
WiredTiger
Создатели движка гордятся включением его в проект MongoDB.
Его правомерно сравнивать как с key-value движками, так и с, например, InnoDB или Percona MySQL(MariaDB).
Как так ? Да потому что там накручено абстракций из коробоки.
Вот, к примеру, о схеме данных http://source.wiredtiger.com/2.6.1/schema.html
Я так и не стал этим пользоваться — сложно и у меня мало времени, если так, то уж лучше разобраться с InnoDB, там и API дружелюбнее.
Но, если у Вас времени хватает, лучше разобраться ней, ведь она стоит в одном ряду с InnoDB и XtraDB, а это говорит о многом.
Его производительность кроется в методике LSMTrees, хорошо расписанной, кстати, как раз у них (хотя она общеизвестна) ссылка https://github.com/wiredtiger/wiredtiger/wiki/LSMTrees
В оригиналде LSMTree описан тут http://www.cs.umb.edu/~poneil/lsmtree.pdf
Sophia
Встраиваемая K/V хранилка http://sphia.org/index.html с транзакциями!
Но Append-Only MVCC.
Надо посмотреть графики, чтобы определить случай, когда она вам нужна.
Коротко: стабильно малая задержка 1) при вставке 2) при итерировании (обход значений)
Концептуально
Хранилиша локальные мы имеем. Движки для использования ресурсов ОС — тоже.
Можно выбрать планировщик IO, тип файловой системы тоже.
Но все равно не получается в 21 веке использовать все это правильно и вот почему:
Представьте как бы вы действовали, если бы вам нужно было сделать «Instagramm»?
В команде yandex для хранения картинок тоже используют не тупо ФС и кучу мелких файликов.
Задача, в которой надо много хранить мелких кусочков данных, которые могут быть востребованы в любом порядке не известно когда, не решается ни «быстрыми file API», ни кешированиями (т.к. не понятно, когда картинка будет нужна — нет предсказания, нет смысла все держать в кеше).
Тут ограничителем встает disk seek. Кусочки данных лежат в разных местах диска, все что можно сделать «вычитать файл картинки целиком за один seek», тогда скорось не будет превышать disk seek per second — а это очень мало чтений в секунду. Тут бесполезно знать, сколько памяти у компа, какие у него процы, и даже скорость ввода-вывода по сети мало влияет. Лучше «размылить» запросы к большому числу несильных машин, у которых по несколько дисков.
Представьте: картинка стала популярной! Беда — все ее запрашивают и вычитывают (если мы решили не кешировать) — диск дохнет, просто сгорает.
Надо периодически делать немного реплик каждого файла на живые диски и проверять, не стали ли пропадать реплики каких-то файлов вместе с мертвыми дисками.
Надо кешировать популярные файлы на какое-то время в памяти, чтобы не делать для них seek каждый раз.
Представьте: а если нет реплик этой картинки в сети? А она популярная, то сеть ограничит скорость отдачи. Решения два:
- проводить супер сеть — а это очень избыточно, т.к. мала вероятность пупулярности файла, (не реально)
- реплицировать картинку на 100500 компов — очень дорого, значит надо реплицировать только популярные картинки динамически намного компов, делать много реплик.
Все эти задачи — это всё задачи хранилища. А проблема в том, что просто из коробки выбрать годное решение даже для такой ерунды сегодня невозможно.
Для построения хранилищ есть компоненты, но даже развернуть подобное — большая работа.
handlersocket
Когда я впервые это увидел, я подумал: «Ну наконец-то догадались». Еще в институте я бловался с InnoDB и удивлялся получаемым скоростям.
Но удивило меня не сама эта поделка, а способ ее работы по сети — напоминает шизу, почему так??? Остается сказать только: «Хрен сним, главное, что оно работает»
Ссылка на хабр http://habrahabr.ru/post/239637/
Ссылка на percona https://www.percona.com/doc/percona-server/5.5/performance/handlersocket.html
Остается только пожелать, чтобы была вменяемая версия embedded, и она есть.
haildb
Встраиваемый InnoDB, правда, слишком сишный(они все уж очень сишные)
Ссылка http://www.haildb.com/tag/embedded-innodb/
Читать лучше тут http://www.haildb.com/doc/latex/refman.pdf
Расстраивает, что биндингов, считай, нет. Для питона есть, но не Hail, а просто InnoDB https://github.com/vpelletier/python-innodb
Стоит отметить странность-«нет оберток высокого прикладного уровня для InnoDB с простым API key-value, tree, table» — тема для диплома ?
Было бы неплохо иметь для питона, java, с репликами эксперимент поставить…
tarantool , nodejs, libuv
С точки зрения работы с данными tarantool можно считать хорошей штукой или даже платформой. http://tarantool.org/
Он использует внутри ассинхронщину sophia db для работы с диском и свою надстройку над ней. Там есть пакеты работы с файлами, все многопоточно, асинхронно и без блокирования. tarantool использует lua, а точнее luajit. Тот самый-самый, быстрый скиптовый.
Есть сборки подобного рода на JS, например, nodejs. К ним написано много пакетов и биндингов. Про это можно почитать отдельно.
Мое мнение: использование таких динамических платформ — это стрельба по ногам. Допустимо использование для отдельных задач, но писать на такой платформе большую систему на lua или JS я бы не рекомендовал.
http://libuv.org/
Множество асинхронных неблокирующихся hi load систем строятся поверх одной и той же либы libuv (это системы Luvit, Julia, pyuv и ДРУГИЕ https://github.com/libuv/libuv/wiki/Projects-that-use-libuv)
nodejs тоже работает поверх нее. Упрощенно и по сути Nodejs- это libuv, к которой приделали JS.
Биндинги к ней есть, наверное, для всех основных языков.
Сегодня libuv развивается, как библиотека-платформа для создания нагруженных вводом-выводом компонентов или звеньев программных систем.
«на этом мы остановимся», но Ваш путь только начинется 😉
Михаил Павлов
14.04.2016 в 23:31Добавлю : sophia + Python =
http://charlesleifer.com/blog/announcing-sophy-fast-python-bindings-for-sophia-database/
python AIO https://github.com/felipecruz/pyaio