Хранение данных

Поговорим о сохранении данных после обработки.

Варианты действий по завершению анализа:

  • Отбросить данные (по результатам анализа)
  • Передать данные потоковой платформе
  • Сохранить данные для использования в режиме реального времени
  • Сохранить данные для последующей пакетной обработки

Первый вариант не столь фантастичен, однако надо помнить, что в рассматриваемой архитектуре отброшенные данные пропадают необратимо.

Передача данных потоковой платформе – это вариант композиции, когда выход одной системы обработки становится входом для другой.

Долговременное хранилище

Долговременное хранилище прежде всего интересно, если необходимо хранение исторических данных для последующей обработки в пакетном режиме. В качестве примеров долговременного хранилища можно назвать HDFS, HBase, реляционные СУБД и т.п. Если возникает необходимость записывать данные в долговременное хранилище, есть три принципиальных подхода как это делать:

  1. Сохранять каждое сообщение отдельно
  2. Сохранять сообщения пакетами, формируемыми в звене анализа
  3. Сохранять сообщения пакетами, формируемыми очередью сообщений

Первые два варианта – варианты прямой записи. В первом варианте каждое сообщение записывается сразу после обработки, по сути, со скоростью потока. Во втором сообщения накапливаются звеном анализа и по достижении определённого порога или времени записываются в долговременное хранилище.

С этими вариантами связаны потенциальные проблемы и риски. Скажем, если хранилище не справляется со скоростью потока, то мы как минимум не сможем сохранить данные, а возможно затормозим и анализ. К тому же при крахе компонента анализа велик шанс потерять данные.

Третий вариант – вариант непрямой записи. Здесь связь между долговременным хранилищем и обработкой потока разрывается при помощи очереди. Можно использовать все те же подходы, что и с очередью сообщений, используемой для входных данных. При этом данные из очереди сообщений извлекаются специальным компонентом, называемым пакетным загрузчиком. Из инструментов пакетной загрузки можно назвать Gobblin (https://gobblin.readthedocs.io, sic!) компании LiknedIn и Secor (https://github.com/pinterest/secor) компании Pinterest. Оба реализуют сохранение данных из Kafka или другого источника в HDFS, Openstack Swift или другое решение распределённого хранения (напр. MS Azure Blob Storage, Amazon S3, etc).

Хранение данных в памяти

При создании системы потокового анализа, целью является обработка данных в режиме “почти” реального времени, по мере поступления. Кроме того, для анализа может быть необходим какой-то объём исторических данных, доступ к которым должен быть максимально быстрым (чтобы не замедлять обработку потока)

В общем, для максимально быстрого доступа к данным, эти данные должны находиться в оперативной памяти. Даже самые быстрые системы хранения на текущий момент в 1000 или более раз медленные, чем оперативная память. А в современных системах, объем доступной оперативной памяти одного узла может достигать нескольких терабайт, что делает хранение данных в оперативной памяти реальным и достаточно заманчивым подходом.

Рассмотрим какие есть варианты для таких систем.

Встраиваемые хранилища

Сначала рассмотрим вариант, который плохо подходит для систем потоковой обработки – решения для хранения, предназначенные для встраивания в программу. Такие решения рассчитаны на один узел и не предоставляют никаких средств распределённой обработки, которые обычно имеются в невстраиваемых системах.

По сути, при использовании встраиваемых решений, на каждом узле анализа оказывается своё, отдельное хранилище данных, что становится весьма проблематично, как только возникает необходимость получить данные, недоступные звену локально: неясно, как определить, какой обработчик владеет нужными данными, и что делать, если он стал недоступен.

Несколько примеров встраиваемых систем:

  • SQLite
  • RocksDB
  • LMDB
  • Perset

Это ни в коем случае не исчерпывающий список.

Системы кэширования

Решения этой категории могут называться по-разному: системы кэширования, хранилище в RAM, хранилище ключ-значение в RAM и т.п. В любом случае, речь о решениях, предназначенных для ускорения доступа к данным за счёт хранения в оперативной памяти.

Системы кэширования ставятся между потребителями данных и системой хранения (обычно долговременным хранилищем).

Здесь следует отдельно отметить, что долговременное или по крайней мере отказоустойчивое хранилище в любом случае необходимо.

Рассмотрим несколько возможных стратегий реализации кэширования.

Стратегии чтения

Сквозное чтение

Система кэширования читает данные из постоянного хранилища, если у неё запрашивают запись, отсутствующую в кэше. Это приводит к задержкам при первом запросе данных.

Опережающее обновление

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

Стратегии записи

Обходная запись
Идея этой стратегии в том, что постоянное хранилище, проксированное кэшем, обновляется помимо кэша. “Помимо” в данном случае означает, что система кэширования вообще может не знать о том, когда и как обновляется хранилище, и полагаться на то, что какой-то другой процесс обновит кэш при обновлении хранилища. На предыдущих схемах такая запись показана точечным пунктиром.
Сквозная запись

Здесь система кэширования проксирует не только чтение, но и запись. Однако, в данном случае, запись производится сразу, и подтверждение успешности записи поступает только когда запись в долговременное хранилище завершена. Это в свою очередь приводит к задержкам.

Отложенная запись

В случае отложенной записи, система кэширования записывает данные не сразу, а “рано или поздно”. В отличие от сквозной записи, запрос на запись возвращается, как только обновлены данные в кэше, а запись в хранилище производится в фоновом режиме. Преимуществом такой системы является отсутствие накладных расходов на ввод-вывод, но возникает проблема: пока данные не записаны в хранилище, есть вероятность их утери в связи с крахом узла кэша.

Таким образом, приходится выбирать между задержками на сохранение данных и шансом их утраты.

Несколько наиболее популярных продуктов:

  • Memcached – популярная система кэширования, но для записи в хранилище требуется обходная запись
  • EHCache – система кэширования, поддерживающая различные стратегии
  • Hazelcast – решение не только и не столько для кэширования, но в части кэширования поддерживает сквозное чтение и сквозную запись
  • Redis – поддерживает постоянное хранение с помощью собственного формата, однако любую из перечисленных выше стратегий по сути необходимо реализовывать вручную

Базы данных и сетки данных в памяти

Базы данных в памяти (IMDB) и сетки данных в памяти (IMDG) представляют более мощное решение, чем системы кэширования. В отличие от систем кэширования, для энергонезависимого хранения непосредственно используется диск. Однако в отличие от традиционных СУБД (включая многие NoSQL решения) в том, что данные в основном хранятся в памяти, а диск используется только для журналирования и периодического сохранения “снимков” данных, т.е. используется только чтобы не потерять данные в случае сбоя.

Исторически, IMDB и IMDG отличались в основном наличием в IMDG распределённой архитектуры, позволяющей выполнять обработку данных непосредственно “рядом с данными”, по аналогии с хранимыми процедурами в РСУБД. Кроме того, IMDB обычно предоставляют API на базе SQL, в то время как IMDG – нет. Но на практике эти границы крайне размыты. IMDB приближаются по функциональности к IMDG, а IMDG-продукты в свою очередь соревнуются с фреймворками обработки.

  • MemSQL – система, совместимая с MySQL, но хранящая данные в оперативной памяти с возможностью сохранения на диск.
  • VoldDB – высокопроизводительная SQL-совместимая БД, предлагающая вариант хранения в памяти
  • Aerospike – оптимизированный для SSD-накопителей движок NoSQL
  • Apache Geode – движок для хранения в памяти. Предлагает SQL-подобный язык запросов OQL
  • Couchbase – документно-ориентированная база данных, появившаяся как гибрид Memcached и CouchDB. Поддерживает SQL-подобный язык запросов N1QL.
  • Apache Ignite – IMDG, с развитой структурой распределённых вычислений, поддержкой SQL и интеграцией с Hadoop и Spark.
  • Hazelcast – IMDG с поддержкой собственного языка запросов, распределённого обобщения, MapReduce и т.д.
  • Infinispan – IMDG, предлагающая интеграцию со Spark