Я хочу хранить данные таймсерий в mongodb. Я знаю, знаю, в сети есть много статей об этом, но ни одна не предлагала решение именно моей проблемы, и многие из них относятся к старому механизму хранения mmap, и я использую WiredTiger, и многие из них иметь дело с равноудаленными таймсериями. Цель состоит в том, чтобы достичь наилучшего компромисса между пропускной способностью чтения и записи, обеспечивая при этом максимальную масштабируемость (даже до терабайт данных временных рядов) и минимальный размер дискового хранилища / потребление оперативной памяти. Это также означает минимальное количество индексов. У историка должна быть возможность импортировать прошлые данные, поэтому новые данные не всегда добавляются с увеличивающейся временной меткой. Я уже разработал минималистичную схему:
{
"_id": "f061e95ae4dc281aea4df2f8",
"Values": [
{
"Time": "2018-05-02T07:56:40.545Z",
"Value": "529"
},
{
"Time": "2018-05-02T07:56:51.119Z",
"Value": "254"
},
...
]
}
Этот документ представляет один фрагмент данных, который принадлежит одному сигналу, и один один час по времени (данные будут иметь наименьшее, но неэквидистантное время выборки, составляющее около 1 секунды). Коллекция содержит только такие документы и имеет только один индекс (конечно, по _id). _Id документа генерируется на основе хэша уникального идентификатора этого сигнала и начала часа utc, которому принадлежит сегмент данных.
Как видите, для запроса данных я вычисляю набор идентификаторов на основе заданного временного диапазона и идентификатора сигнала. Первые 4 байта _id кодируют час utc, последние 8 байтов - хэш идентификатора сигнала. Следовательно, запрос, запрашивающий данные за один день для одного сигнала, приведет к запросу, пытающемуся получить 24 идентификатора. Проблема в том, что для больших временных диапазонов (например, начиная с эпохи до настоящего времени) мне приходится запрашивать множество идентификаторов (например, 400000), что, похоже, делает запрос довольно медленным. (Сокращенный) запрос mongodb db (на C#):
FilterDefinition<DBTimeSeries> f = Builders<DBTimeSeries>.Filter.Empty;
var filterbuilder = Builders<DBTimeSeries>.Filter;
IEnumerable ids = Helpers.GetIDs(start, end, tagname);
var f = Builders<DBTimeSeries>.Filter.Empty;
f = f & filterbuilder.In(x => x.ID, ids);
var chunks = await collection.FindAsync<DBTimeSeries>(f);
Даже при небольшом количестве данных в базе данных выполнение этого запроса занимает до одной минуты. Свойство ID имеет тип ObjectId из драйвера mongodb C#.
Я знаю что это возможно
f = f & filterbuilder.Where(x => x.ID > new ObjectId(DateTime.Now.AddDays(-1), 0, 0, 0) && x.ID < new ObjectId(DateTime.Now, 0, 0, 0))
Это означает, что я могу выполнять запросы временного диапазона по идентификаторам, но это не дает мне возможности запрашивать данные для одного конкретного сигнала без добавления дополнительного поля в документ, представленный выше. Мне даже пришлось бы проиндексировать это поле для лучшей производительности.
Поэтому мой вопрос: было бы оптимальным иметь возможность запрашивать _id с помощью такого запроса: «Дайте мне документы, первые 4 байта поля _id которых представляют uint32 между x и y, а последние 8 байтов поля _id - равный заданному unit64 ". Запрос должен использовать исключительно индексы, что должно быть возможно, поскольку вся информация хранится в поле _id. Я знаю о параметре $ bitsAllSet (https://docs.mongodb.com/manual/reference/operator/query/bitsAllSet/#op._S_bitsAllSet), но я могу каким-то образом передать только long в драйвере C#, и запросы, похоже, не могут использовать индекс для части запроса $ bitsAllSet ...
У кого-нибудь есть идея? Спасибо всем заранее.
Спасибо за помощь, Винс! Да, конечно, добавление поля индексированного идентификатора сигнала в документ корзины было бы простым решением, но теоретически все, что мне нужно, доступно в поле _id таким образом. Было бы здорово, если бы проблема могла быть решена с использованием только одного индекса, если бы я мог даже сделать запрос временного диапазона по _id (который в конечном итоге является запросом по первым 4 байтам _id), это также должно быть возможно включить в запрос гораздо менее сложный оператор «сопоставить последние 8 байтов _id с шаблоном, который я вам предоставляю».
ОК, имеет смысл. Я взял на себя смелость отредактировать заголовок вашего вопроса, чтобы попытаться зафиксировать важную часть того, что вы пытаетесь сделать. Помните, вам рекомендуется редактировать и при необходимости повторно редактировать вопросы, чтобы улучшить их в максимально возможной степени.
@heapoverflow несколько вопросов 1) Не могли бы вы опубликовать вывод объяснять()? 2) почему у вас значение _id
в строковом формате? 3) Будет ли ваше значение _id
последовательным? Я бы так предположил, но просто чтобы подтвердить. Спасибо.
Можно ли будет реструктурировать ваши данные? Использование поля _id для хранения идентификатора сигнала и метки времени корзины затрудняет их индексацию. Не могли бы вы сохранить идентификатор сигнала как одно поле, а временную метку корзины как отдельное поле даты, что позволит вам индексировать их как обычно?