Хранилище ключей для данных временных рядов?
Я использую SQL Server для хранения исторических данных временных рядов за пару сотен тысяч объектов, наблюдаемых около 100 раз в день. Я нахожу, что запросы (дайте мне все значения для объекта XYZ между временем t1 и временем t2) слишком медленны (для моих нужд медленнее больше секунды). Я индексирую по метке времени и объекту.
Я размышлял над тем, что вместо этого использовал somethings хранилище для ключей, например MongoDB, но я не уверен, что это "подходящее" использование такого рода вещей, и я не мог найти никаких упоминаний о используя такую базу данных для данных временных рядов. В идеале я мог бы выполнить следующие запросы:
- получить все данные для объекта XYZ между временем t1 и временем t2
- выполните вышеуказанные действия, но верните одну точку в день (сначала, последний, закрытый до времени t...)
- получить все данные для всех объектов для определенной отметки времени
данные должны быть заказаны, и в идеале он должен быть быстрым для записи новых данных, а также для обновления существующих данных.
похоже, что мое желание запросить идентификатор объекта, а также временную метку может потребовать, чтобы две копии базы данных индексировались по-разному, чтобы получить оптимальную производительность... у кого-нибудь есть опыт создания такой системы, с ключом -value store, или HDF5, или что-то еще? или это полностью выполнимо в SQL Server, и я просто не делаю это правильно?
Ответы
Ответ 1
Похоже, что MongoDB будет очень хорошим. Обновления и вставки очень быстрые, поэтому вы можете создать документ для каждого события, например:
{
object: XYZ,
ts : new Date()
}
Затем вы можете проиндексировать поле ts, и запросы также будут быстрыми. (Кстати, вы можете создавать несколько индексов в одной базе данных.)
Как выполнить ваши три запроса:
получить все данные для объекта XYZ между временем t1 и временем t2
db.data.find({object : XYZ, ts : {$gt : t1, $lt : t2}})
выполните вышеуказанное, но верните одну дату точка в день (первая, последняя, закрытая до время t...)
// first
db.data.find({object : XYZ, ts : {$gt : new Date(/* start of day */)}}).sort({ts : 1}).limit(1)
// last
db.data.find({object : XYZ, ts : {$lt : new Date(/* end of day */)}}).sort({ts : -1}).limit(1)
В течение ближайшего времени вам, вероятно, потребуется специальная функция JavaScript, но это возможно.
получить все данные для всех объектов для конкретная временная метка
db.data.find({ts : timestamp})
Не стесняйтесь спрашивать в список пользователей, если у вас есть какие-либо вопросы, кто-то может подумать о более легком способе получения события, близкие к событиям.
Ответ 2
Вот почему существуют базы данных, специфичные для данных временных рядов - реляционные базы данных просто недостаточно быстры для больших временных рядов.
Я использовал Fame довольно много в инвестиционных банках. Это очень быстро, но я думаю, что это очень дорого. Однако, если ваше приложение требует скорости, возможно, стоит посмотреть его.
Ответ 3
Существует база данных с открытым исходным кодом для активной разработки (только для .NET), которую я написал. Он может хранить массивные суммы (террабайты) однородных данных в "двоичном плоском файле". Все использование ориентировано на потоки (вперед или назад). Мы активно используем его для хранения и анализа складских тиков в нашей компании.
Я не уверен, что это будет именно то, что вам нужно, но это позволит вам получить первые две точки - получить значения от t1 до t2 для любых серий (по одной серии для каждого файла) или просто взять одну точку данных.
https://code.google.com/p/timeseriesdb/
// Create a new file for MyStruct data.
// Use BinCompressedFile<,> for compressed storage of deltas
using (var file = new BinSeriesFile<UtcDateTime, MyStruct>("data.bts"))
{
file.UniqueIndexes = true; // enforces index uniqueness
file.InitializeNewFile(); // create file and write header
file.AppendData(data); // append data (stream of ArraySegment<>)
}
// Read needed data.
using (var file = (IEnumerableFeed<UtcDateTime, MyStrut>) BinaryFile.Open("data.bts", false))
{
// Enumerate one item at a time maxitum 10 items starting at 2011-1-1
// (can also get one segment at a time with StreamSegments)
foreach (var val in file.Stream(new UtcDateTime(2011,1,1), maxItemCount = 10)
Console.WriteLine(val);
}
Ответ 4
Недавно я попробовал что-то подобное в F #. Я начал с формата строки в 1 минуту для соответствующего символа в файле с разделителем пространства, который имеет примерно 80 000 минут в минуту. Код для загрузки и разбора с диска был меньше 1 мс. Код для вычисления 100-минутного SMA за каждый период в файле составил 530 мс. Я могу вытащить любой фрагмент, который я хочу из последовательности SMA, как только вы подсчитали менее 1 мс. Я просто изучаю F #, поэтому есть способы оптимизации. Обратите внимание, что это было после нескольких тестовых прогонов, поэтому оно уже было в кэше Windows, но даже при загрузке с диска он никогда не добавляет к нагрузке более 15 мс.
дата, время, открытый, высокий, низкий, близко, объем
01/03/2011,08: 00: 00,94.38,94.38,93.66,93.66,3800
Чтобы сократить время пересчета, я сохраняю всю вычисленную последовательность индикаторов на диск в одном файле с разделителем \n и обычно занимает менее 0,5 мс для загрузки и анализа, когда в кеше файлов Windows. Простая итерация по всем данным временного ряда, чтобы вернуть набор записей в диапазоне дат в течение 3 мс с полным годом в 1 минуту баров. Я также сохраняю ежедневные бары в отдельном файле, который загружается еще быстрее из-за более низких объемов данных.
Я использую слой .net4 System.Runtime.Caching для кэширования сериализованного представления предварительно вычисленной серии и с паровым гигабайтом ОЗУ, выделенным для кеша, я получаю почти 100% -ный коэффициент попадания в кеш, поэтому мой доступ к любому предикату - установленный набор индикаторов для любого символа обычно работает под 1 мс.
Вытягивание любого фрагмента данных, который требуется от индикатора, обычно меньше 1 мс, поэтому расширенные запросы просто не имеют смысла. Используя эту стратегию, я мог бы легко загрузить 10-летнюю шкалу за 1 минуту менее чем за 20 мс.
// Parse a \n delimited file into RAM then
// then split each line on space to into a
// array of tokens. Return the entire array
// as string[][]
let readSpaceDelimFile fname =
System.IO.File.ReadAllLines(fname)
|> Array.map (fun line -> line.Split [|' '|])
// Based on a two dimensional array
// pull out a single column for bar
// close and convert every value
// for every row to a float
// and return the array of floats.
let GetArrClose(tarr : string[][]) =
[| for aLine in tarr do
//printfn "aLine=%A" aLine
let closep = float(aLine.[5])
yield closep
|]
Ответ 5
Я использую HDF5 как репозиторий временных рядов. Он имеет ряд эффективных и быстрых стилей сжатия, которые можно смешивать и сопоставлять. Он может использоваться с несколькими языками программирования.
Я использую boost:: date_time для поля timestamp.
В финансовой сфере я создаю конкретные структуры данных для каждого из баров, тиков, сделок, котировок,...
Я создал ряд пользовательских итераторов и использовал стандартные функции библиотеки шаблонов, чтобы иметь возможность эффективно искать конкретные значения или диапазоны записей, основанных на времени.