Pull to refresh

Comments 49

Осталось привести список мониторингов, где подобная база будет быстрее.
В таких базах есть еще интересный вопрос поддержания retentions разного разрешения (типа аггрегация ежеминутных данных за час в одно среднее значение за час). Это делается автоматом в хранилищах типа RRD или whisper, не знаю насчет InfluxDB.

И еще, возможно пропустил — как удаляются старые данные?
  1. Я считаю, что такие вещи, как запись статистических данных (среднее за час/минуту) не задача самого движка хранения. Да, в dariadb реализован метод stat, который выдаст вам для определенного измерения среднее значение, мин/максы. Но вот ведение параллельного временного ряда, со статистикой, это не задача движка. Я бы это вынес в серверную часть. Что я и сделаю в будущих версиях.
  2. Что касается удаления. Сейчас есть два способа удалить:
    • метод erase — который просто удалит страницы из интервала.
    • метод compact (в ветке dev) — с его помощью можно не только удалить выборочно измерения, но и заменить группу замеров на 1.

Да, можно сделать сейчас и более элегантное удаление, как в leveldb (запись специального значения в базу, которое говорит, что у нас удалена какая то часть), но этого пока у меня нет в планах. Если найдется гурппа желающих, то сделаю.

feather-файлы можно писать со скоростью 800 Мбайт/с (и даже быстрее, если ваше хранилище обеспечит нужную производительность)
blosc'ом можно сжимать данные очень быстро и просто одновременно, причем это может быть даже быстрее чем mem-to-mem копирование.


Зачем нужна ваша DariaDB?

Осмелюсь предположить, что бы впечатлить девушку. Даже осмелюсь угадать имя… Впрочем, вы сами всё понимаете.

Почти угадали. Дарья — моя дочь.

Уточните, пожалуйста, свое понимание слова «хобби» в выражении «хобби проект».

Зачем нужен ваш комментарий?
Feather это exchange формат данных для R датафреймов (чтобы их можно было передавать между R и Pandas), у датафрейма совсем другая модель данных (ряды должны быть выровнены, количество рядов доложно быть известно заранее и тд), использование отдельного feather файла на каждый временной ряд totally defeats the purpose этого формата, к тому же у них в README жирным шрифтом выделено — «Do not use Feather for long-term data storage».

Blosc — компрессор, который не подходит для TSDB, которая держит открытыми очень много рядов (например миллион), каждый ряд нужно уметь читать независимо, поэтому ему нужен свой отдельный контекст компрессора, а этот контекст обычно занимает десятки килобайт. Ну и еще он медленный.
Вопрос по коду примеров. А почему вы создаете объект Callback через new:
  std::unique_ptr<Callback> callback_ptr{new Callback()};
  storage->foreach (qp, callback_ptr.get());
  callback_ptr->wait();

В этом есть какой-то тайный смысл? Или это можно переписать так:
Callback callback;
storage->foreach(qp, &callback);
callback.wait();
да. Вы правы. Тут нет необходимости создания объекта на куче.

А почему вы используете 4 байта для флага? Простите если читал невнимательно.

А это эмпирически. дело в том, что я работаю с одной такой большой SCADA системой, так по опыту могу сказать, что 2 байт уже слишком мало, а 64 слишком много.
Все хорошо, полезно в любом случае, но почему не выбрали LMDB изначально для своих задач, к примеру? Он ведь уделывает вас по производительности в разы, да и функциональность значительно выше.
1. как я понял, LMDB — key-value база, а это совсем не то, что база данных для timeseries. Я сильно не вчитывался, но если в lmdb можно сделать запрос интервала и среза, то ее можно использовать как timeseries.
2. Dariadb — хобби проект. Он не рассчитан (во всяком случае пока) на продакшн. Делался для себя, чтобы понять как оно там внутри.
LMDB действительно KVS, оно действительно не предоставляет из коробки того высокоуровневного интерфейса, который ждешь от timeseries dbs (кстати, InfluxDB может использовать под капотом LMDB), но сама задача работы с timeseries абсолютно спокойно укладываются в парадигму KV (а тут не только LMDB, но и родственники LevelDB идеально впишутся), все сведется к простейшей обертке и range scans.
Эта задача не ложится на обычные KV-stores, предоставляющие oredered map inteface (LMDB, LevelDB). Чтобы понять почему, подумайте, что у вас будет ключем.
Да хотя бы конкатенация время + имя ряда
ОК, допустим вы хотите выбрать конкретный временной ряд чтобы нарисовать график, делаете full scan между двумя метками времени, на один байт полезных данных LevelDB читает несколько KB мусора, значение read amplification просто неприличное, пользователь ждет суточный график несколько минут.
«хотя бы» означает один возможных вариантов решения. Безусловно выбор ключа зависит от того, какие у вас данные, как вы по ним будете искать. Если рядов очень много и между 2 метками времени ожидается «неприличное» количество мусора, то имена рядов и метку времени можно просто поменять местами в ключе.
Можно поменять, тогда вы будете писать ключи в случайном порядке и получите кучу других проблем (долгие compaction паузы, например). Проблема в том, что данные приходят упорядоченными по метке времени, а читаются в совершенно другом порядке. Помимо обычных point запросов есть еще aggregations, joins, group by запросы, в каждом из которых разные ключи будут давать разные результаты. Я уже промолчу о том что KV-stores не могут делать join-ы и агрегировать данные, т.е. вам как-то вручную придется написать свою БД. Чуваки из influxdb попытались, в итоге отказались от LevelDB, а потом и от BoltDB (который является клоном LMBD), т.к. на типичный KV это все не ложится вообще. Более или менее нормально это ложится на колумнарные БД.
Ветка обсуждения наша началась с комментария о том, можно ли применить один из существующих KV стораджей, для реализации DariaDB. Внутри DariaDB — LSM c блум-фильтрами. Что похоже к примеру на RocksDB или LevelDB. Соответственно, вопрос «почему нельзя было взять готовый LSM в виде существующего KV-store» вполне резонный. Ну а вопросы про «read amplification», aggregations, joins, group by запросы в KV-стораджах здесь не совсем уместны, т.к. сравнивать нужно не KV-стораджи и аналитические колумнарные БД, а KV-стораджи и DariaDB. Как в DariaDB обстоят дела со всем этим?

Если брать готовое решение (абстрактный KV-сторадж или leveldb для конкретности), то их можно использовать, если они дают построить в памяти (ну или на диске) индекс таким образом, чтобы мы знали какие временные ряды имеются в хранилище, где они лежат на диске, чтобы не заниматься сканированием всего файла, а сразу сделать seek к нужным данным, а также уметь эффективно упаковывать значения временного ряда, потому что самое поле Value может почти не меняться для рядом стоящих замеров. Если абстрактный kv-сторадж это умеет, то его можно использовать вместо dariadb.


В dariadb заголовки индексных файлов лежат в памяти, по этим заголовкам мы определяем нужные нам файлы (индексные), проходим по ним и находим смещения нужных нам чанков, которые попали в запрос. Сами индексные файлы небольшие, для страницы в 1 мегабайт размером, индексный файл будет примерно 30 килобайт, при желании их вообще можно полностью держать в памяти. Причем сами индексы содержат статистику, поэтому для запроса среднего на интервале, или минимума за период, читать и распаковывать страницу не надо.


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


А вот с group by хотелось бы пример реального использования. Мне оно не встречалось, поэтому интересно зачем оно и как использовать.

Как в DariaDB обстоят дела со всем этим?
Мой вопрос был скорее адресован автору комента выше :)
Если абстрактный kv-сторадж это умеет, то его можно использовать вместо dariadb.
Я думаю, что хороший kv-сторадж умеет эффективно искать по ключу, а также умеет сжимать данные. Можно почитать, к примеру, как устроена RocksDB.
Причем сами индексы содержат статистику
Вот этого в kv-сторадже вероятно нет. Но до определенных пределов это может компенсироваться быстрым чтением или каким-нибудь кэшем сбоку.
А может специализированный движок будет лучше работать? Вы же предлагаете написать часть БД вприложении, кэш сбоку, потом окажется что нужен обработчик запросов свой и тд. Путем, который вы предлагаете многие уже пошли, может кому-нибудь стоит попробовать специализированное решение?
Безусловно специализированный движок в перспективе будет работать лучше Но для этого нужно приложить немало усилий.
«почти» ложится на «почти» KV. Напрмер, на кассандре, с некоторыми приседаниями, получается.
При этом тот же InfluxDB (который в плане производительности TS держится в топе) в качестве storage использует LMDB / LevelDB и его наследников, да? Ключом timestamp'а будет достаточно. Я действительно могу чего-то не улавливать, буду признателен за объяснение.
ключем может быть, к примеру, и час суток, а значением — отсортированные минутные замеры (с таймстампами).
Да без вопросов, я к тому, что нет проблем в реализации TS на KV, более того, все так и делают, по сути, оно абсолютно спокойно укладывается в идеологию, а как организовывать это все — уже индивидуально.
InfluxDB не держится в топе, КМК (что это за топ такой вообще?). Он на очень толстой машине может выдать чуть больше 0.5М операций записи в секунду. И они начали это делать только после того, как выкинули LevelDB, RocksDB и LMDB (там были plugable движки когда-то) из проекта и начали использовать TSM-tree — специализированный движок для хранения временных рядов (это еще в v0.9 было, года два назад).
Есть хорошее объяснение того, почему KV не годится для временных баз данных — это та статья, которую вы, собственно, комментируете :)
Ну, и мне кажется, что реальный «топ» таких баз данных — это закрытые энтерпрайз-решения
Мне довелось работать с базами замерных данных по объектам (суть те же временные ряды).
Наиболее актуальными видами запросов были следующие (как для одного ряда, так и для группы рядов):
  • выборка последнего значения по состоянию на момент времени, но не ранее заданного момента времени (фактически, выборка последнего значения в интервале);
  • выборка значений за временной период + последнего значения по состоянию на начало периода, но не ранее заданной даты.
Упомянутое дополнительное ограничение по времени при выборке последнего значения имеет смысл, когда слишком старые данные не актуальны и не нужны, и помогает базе не делать поиск глубоко в истории, что положительно сказывается на производительности.

У автора есть размышления не сей счёт или таких нюансов пока не касались?

"выборка последнего значения по состоянию на момент времени, но не ранее заданного момента времени (фактически, выборка последнего значения в интервале);"


А это чем отличается от среза на момент времени? Вам фактически возвращается значение, которое не позднее заданного времени.

При обычном срезе давность последних значений ничем не ограничена, а часто нужны последние, но не слишком старые данные.
Например, значения телеметрического замера тока электродвигателя, работающего с изменяющейся загрузкой, быстро теряют актуальность (замер суточной давности имеет мало смысла).
Я к тому, что обычно востребовано последнее значение, находящееся между двух заданных дат, а не просто по состоянию на одну дату.

Это все специфические запросы, которые нужны только в вашем проекте. Это все решается через запрос данных за интервал. БД не должна делать предположений о данных, а вы, фактически, хотите чтобы БД знала о том, что измерение является актуальным N секунд, поэтому последнее значение нужно искать не дальше N секунд в прошлом. У всех разные предпосылки.
По опыту согласен с комментарием, кроме того по опыту, обработка параметров происходит поочередно.

Два следствия для производительности для последовательного чтения записи:
  • хранить лучше каждый параметр отдельным хранилищем/файлом
  • это даст возможность предсоздавать файлы определенного размера, в том числе циклические

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

Перечень time-series dbms, возможно из них можно найти еще идеи для реализации

"хранить лучше каждый параметр отдельным хранилищем/файлом"


это смотря сколько значений. Знаю "скаду", где 100к временных рядов с разным шагом записи.

у нас в три сервера graphite собирается порядка 800к метрик с разрешением 1 минута(хрянятся 2 суток), из которых делаются ряды с разрешением пол-часа, два часа и т.д. Хранилище — carbon/whisper. Каждая метрика там хранится в отдельном файле вместе со всеми ретеншнами.

Есть тестовый стенд со сбором порядка 40к метрик в кассандру с time window compaction strategy.

Такие данные в любом случае нужно хранить на нескольких сервреах с прицелом на автоматический файловер и избыточность — на случай выходя из строя, апгрейда и т.д. Кассандра в этом смысле идеальна — все механизмы у неё встроенные. Графит с карборонм — хуже.
Как уже заметили, это не так уж и много. Кроме того, можно при желании иерархически разложить по каталогам, или при нехватке мощностей, по серверам.

Мой пойнт был в том, чтобы при необходимости например, построить 15 графиков из 100к по архивным данным, не пришлось 15 раз перечитать данные с диска от всех 100к параметров.

а в dariadb эти данные считаются за один проход с диска, потом уже в памяти будут выдаваться последовательно для каждого временного ряда.

Не годится. Сначала вы в пример ставите систему со 100к параметров, а теперь утверждаете, что всё поместится в память?

Не поместится, возьмем суточный график.

Даже без накладных данных это ~400 Тб, если параметр пишется с 500мс интервалом и если попытаться прочитать 100к параметров нахрапом.
Рейтинг составлен разработчиками DalmatinerDB на основе данных из интернета (сами они ничего не тестировали). Это все что нужно знать.
Sign up to leave a comment.

Articles