Дизайн Kafka, устойчивость

Файловая система

Kafka в значительной степени полагается на файловую систему для хранения и кэширования сообщений. Существует общее мнение, что «диски работают медленно», что заставляет людей сомневаться в том, что постоянная структура может обеспечить конкурентоспособную производительность. На самом деле диски и намного медленнее, и намного быстрее, чем люди ожидают, в зависимости от того, как они используются; а правильно спроектированная структура диска часто может быть такой же быстрой, как сеть.

Ключевой факт о производительности дисков заключается в том, что пропускная способность жестких дисков отличается от задержки поиска диска в течение последнего десятилетия. В результате производительность линейной записи в конфигурации JBOD с шестью массивами 7200 об/мин SATA RAID-5 составляет около 600 МБ/с, но производительность произвольной записи составляет всего около 100 кбит/с - разница более 6000X. Эти линейные операции чтения и записи являются наиболее предсказуемыми из всех шаблонов использования и в значительной степени оптимизированы операционной системой. Современная операционная система предоставляет методы упреждающего чтения и обратной записи, которые осуществляют предварительную выборку данных в виде кратных больших блоков и группируют меньшие логические записи в большие физические записи.

Чтобы компенсировать это расхождение в производительности, современные операционные системы становятся все более агрессивными в использовании основной памяти для кэширования диска. Современная ОС с радостью направит всю свободную память на кэширование диска с небольшим снижением производительности, когда память будет освобождена. Все операции чтения и записи на диск будут проходить через этот единый кэш. Эту функцию нельзя легко отключить без использования прямого ввода-вывода, поэтому даже если процесс поддерживает внутрипроцессный кэш данных, эти данные, вероятно, будут дублироваться в кэше страниц ОС, эффективно сохраняя все дважды.

Кроме того, мы строим поверх JVM, и любой, кто хоть раз работал с использованием памяти Java, знает две вещи:

  1. Накладные расходы на память объектов очень высоки, часто удваивая размер хранимых данных (или хуже).
  2. Сборка мусора Java становится все труднее и медленнее по мере увеличения объема данных в куче.

В результате этих факторов использование файловой системы и использование кэша страниц лучше, чем поддержка кэша в памяти или другой структуры - мы, по крайней мере, удваиваем доступный кэш, имея автоматический доступ ко всей свободной памяти, и, вероятно, удваиваем снова, сохраняя компактную байтовую структуру, а не отдельные объекты. Это приведет к кэш-памяти размером до 28–30 ГБ на машине с 32 ГБ без штрафов за сборщик мусора. Кроме того, этот кэш будет оставаться прогретым, даже если служба будет перезапущена, тогда как внутрипроцессный кэш необходимо будет перестроить в памяти (что для кэш-памяти 10 ГБ может занять 10 минут), иначе его нужно будет запустить с полностью холодным кэшем (что, вероятно, означает ужасную начальную производительность). Это также значительно упрощает код, поскольку вся логика для поддержания согласованности между кэшем и файловой системой теперь находится в ОС, которая, как правило, делает это более эффективно и правильнее, чем разовые попытки внутри процесса. Если использование вашего диска способствует линейному чтению, то упреждающее чтение эффективно предварительно заполняет этот кэш полезными данными при каждом чтении с диска.

Это предполагает очень простую конструкцию: вместо того, чтобы поддерживать как можно больше в памяти и сбрасывать все это в файловую систему в панике, когда у нас заканчивается пространство, мы инвертируем это. Все данные немедленно записываются в постоянный журнал файловой системы без необходимости сбрасывать их на диск. Фактически это просто означает, что он передается в кэш страниц ядра.

Постоянное время достаточно

Постоянная структура данных, используемая в системах обмена сообщениями, часто представляет собой очередь для каждого потребителя со связанным BTree или другими структурами данных произвольного доступа общего назначения для хранения метаданных о сообщениях. BTree - это наиболее универсальная из доступных структур данных, позволяющая поддерживать широкий спектр транзакционной и нетранзакционной семантики в системе обмена сообщениями. Тем не менее, они имеют довольно высокую стоимость: операции Btree O(log N). Обычно O(log N) считается эквивалентным постоянному времени, но это неверно для дисковых операций. Поиск на диске происходит с задержкой 10 мс, и каждый диск может выполнять только один поиск за раз, поэтому параллелизм ограничен. Следовательно, даже небольшое количество поисков диска приводит к очень высоким накладным расходам. Поскольку в системах хранения сочетаются очень быстрые кэшированные операции с очень медленными операциями на физическом диске, наблюдаемая производительность древовидных структур часто оказывается сверхлинейной по мере увеличения объема данных с фиксированным кэшем, т.е. удвоение данных делает ситуацию намного хуже, чем вдвое медленнее.

Интуитивно понятно, что постоянная очередь может быть построена на простых операциях чтения и добавления файлов, как это обычно бывает с решениями для ведения журналов. Эта структура имеет то преимущество, что все операции выполняются за O(1) и чтения не блокируют записи или друг друга. Это дает очевидные преимущества в производительности, поскольку производительность полностью не зависит от размера данных - теперь один сервер может в полной мере использовать ряд дешевых дисков SATA емкостью 1+ТБ с низкой скоростью вращения. Несмотря на низкую производительность поиска, эти диски имеют приемлемую производительность при чтении и записи больших объемов данных и стоят 1/3 цены и в 3 раза больше емкости.

Доступ к практически неограниченному пространству на диске без потери производительности означает, что мы можем предоставить некоторые функции, которые обычно не встречаются в системе обмена сообщениями. Например, в Kafka вместо попытки удалить сообщения сразу после их использования мы можем сохранять сообщения в течение относительно длительного периода (скажем, недели). Это дает потребителям большую гибкость.


Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Нормальные формы, пример нормализации в базе данных

Хэш-таблица: разрешение коллизий