Как хранить 7,3 миллиарда строк рыночных данных (оптимизировано для чтения)?

У меня есть набор данных за 1 минуту из 1000 акций с 1998 года, которые составляют около (2012-1998)*(365*24*60)*1000 = 7.3 Billion строк.

Большинство (99,9%) времени я буду выполнять только читать запросы.

Каков наилучший способ сохранить эти данные в db?

  • 1 большая таблица с 7,3-строчными рядами?
  • 1000 таблиц (по одному для каждого символа акции) с 7,3 М строк каждый?
  • любая рекомендация механизма базы данных? (Я планирую использовать Amazon RDS MySQL)

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

Edit:

Это пример строки:

'XX', 20041208, 938, 43.7444, 43.7541, 43.735, 43.7444, 35116.7, 1, 0, 0

Столбец 1 - это символ запаса, столбец 2 - дата, столбец 3 - минута, остальные - цены с открытым-высоким-низким-близким, объем и 3 целых столбца.

Большинство запросов будут такими, как "Дайте мне цены на AAPL между 12 апреля 2012 года 12:15 и 13 апреля 2012 года 12:52"

Об оборудовании: я планирую использовать Amazon RDS, поэтому я гибко отношусь к этому

Ответ 1

Расскажите нам о запросах и вашей аппаратной среде.

Мне очень хотелось бы пойти NoSQL, используя Hadoop или что-то подобное, если вы можете использовать parallelism.

Обновление

Хорошо, почему?

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

  • Мой опыт работы с подобными системами предполагает, что доступ будет либо большой последовательный (вычисление какого-то анализа временных рядов), либо очень гибкий интеллектуальный анализ данных (OLAP). Последовательные данные могут обрабатываться лучше и быстрее последовательно; OLAP означает вычисление лотов и множество индексов, которые либо занимают много времени или много места.

  • Если вы делаете то, что эффективно работает с большим количеством данных в мире OLAP, однако, лучше всего использовать подход, ориентированный на столбцы.

  • Если вы хотите делать случайные запросы, особенно делая перекрестные сравнения, система Hadoop может быть эффективной. Зачем? Поскольку

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

Но дело в том, что до тех пор, пока мы не узнаем о вашей рабочей нагрузке, невозможно сказать что-либо окончательное.

Ответ 2

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

Подготовьте структуру C/С++ для хранения формата записи:

struct StockPrice
{
    char ticker_code[2];
    double stock_price;
    timespec when;
    etc
};

Затем вычислите sizeof (StockPrice [N]), где N - количество записей. (В 64-битной системе) Это должно быть всего несколько сотен гигабайт, и он будет помещен на жесткий диск на $50.

Затем обрезайте файл на этот размер и mmap (в linux или используйте CreateFileMapping на окнах) в память:

//pseduo-code
file = open("my.data", WRITE_ONLY);
truncate(file, sizeof(StockPrice[N]));
void* p = mmap(file, WRITE_ONLY);

Отметьте mmaped указатель на StockPrice * и сделайте проход ваших данных, заполняющих массив. Закройте mmap, и теперь вы будете иметь свои данные в одном большом двоичном массиве в файле, который может быть удален позже.

StockPrice* stocks = (StockPrice*) p;
for (size_t i = 0; i < N; i++)
{
    stocks[i] = ParseNextStock(stock_indata_file);
}
close(file);

Теперь вы можете снова воспроизвести его только для чтения из любой программы, и ваши данные будут доступны:

file = open("my.data", READ_ONLY);
StockPrice* stocks = (StockPrice*) mmap(file, READ_ONLY);

// do stuff with stocks;

Итак, теперь вы можете рассматривать его так же, как массив массивов в памяти. Вы можете создавать различные типы структур данных индекса в зависимости от ваших "запросов". Ядро будет иметь дело с возможностью обмена данными на/с диска прозрачно, чтобы он был безумно быстрым.

Если вы ожидаете иметь определенный шаблон доступа (например, непрерывную дату), лучше отсортировать массив в этом порядке, чтобы он последовательно попадал на диск.

Ответ 3

Насколько я понимаю, HDF5 был разработан специально для хранения данных запаса по времени в качестве одного потенциального приложения. Специалисты-штабелеры продемонстрировали, что HDF5 хорош для больших объемов данных: хромосомы, физика.

Ответ 4

Хорошо, так что это немного от других ответов, но... мне кажется, что если у вас есть данные в файловой системе (возможно, один запас на файл) с фиксированным размером записи, вы можете получить на данных действительно легко: учитывая запрос для конкретного запаса и временного диапазона, вы можете найти нужное место, получить все необходимые данные (вы точно знаете, сколько байтов), преобразовать данные в необходимый формат (который может быть очень быстрым в зависимости от вашего формата хранения), и вы отсутствуете.

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

Затем вы добавляете кеширование - я бы предложил дать другим серверам разные запасы для обработки, например, - и вы можете в значительной степени просто использовать память. Если вы можете позволить себе достаточно памяти на достаточном количестве серверов, обходите часть "загрузка по требованию" и просто загружайте все файлы при запуске. Это упростило бы вещи за счет более медленного запуска (что, очевидно, влияет на переход на другой ресурс, если только вы не можете позволить себе иметь два сервера для любого конкретного запаса, что было бы полезно).

Обратите внимание, что вам не нужно сохранять символ запаса, дату или минуту для каждой записи, потому что они неявны в загружаемом файле и позиции в файле. Вы также должны учитывать, какую точность вам нужно для каждого значения, и как эффективно это хранить - вы дали 6SF в своем вопросе, который вы могли бы хранить в 20 бит. Потенциально сохраните три 20-битных целых числа в 64-разрядных хранилищах: прочитайте его как long (или независимо от вашего 64-разрядного целочисленного значения) и используйте маскирование/сдвиг, чтобы вернуть его к трем целым числам. Вам нужно будет знать, какой вес использовать, конечно, что вы, вероятно, могли бы закодировать в запасных 4 битах, если вы не можете сделать его постоянным.

Вы не сказали, что представляют собой три других целочисленных столбца, но если вы можете уйти с 64 битами для этих трех, вы можете сохранить целую запись в 16 байт. Это всего лишь ~ 110 ГБ для всей базы данных, что на самом деле не очень...

РЕДАКТИРОВАТЬ: Еще одна вещь, которую следует учитывать, заключается в том, что, по-видимому, акции не меняются в выходные дни или даже на ночь. Если фондовый рынок открыт только 8 часов в день, 5 дней в неделю, тогда вам нужно всего 40 значений в неделю вместо 168. В этот момент вы можете получить только около 28 ГБ данных в ваших файлах... что звучит намного меньше, чем вы, вероятно, изначально думали. Наличие большого количества данных в памяти очень разумно.

EDIT: Я думаю, что я пропустил объяснение того, почему этот подход здесь подходит: у вас очень предсказуемый аспект для значительной части ваших данных - биржевой тикер, дата и время. Выделяя тикер один раз (как имя файла) и оставляя дату/время полностью неявным в позиции данных, вы удаляете целую кучу работы. Это немного похоже на разницу между String[] и a Map<Integer, String> - зная, что ваш индекс массива всегда начинается с 0 и увеличивается с шагом 1 до длины массива, что обеспечивает быстрый доступ и более эффективное хранилище.

Ответ 5

У меня есть набор данных за 1 минуту из 1000 акций [...] больше всего (99,9%) того времени, когда я буду выполнять только читать запросы.

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

Этот вопрос был задан в 2012 году, и с тех пор несколько двигателей баз данных разрабатывают функции, специально предназначенные для управления временными рядами. Я получил отличные результаты с InfluxDB, который является открытым исходным кодом, написанным на Go и MIT-лицензированным.

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

InfluxDB vs Cassandra query speed

Оптимизация для временных рядов включала определенные компромиссы. Например:

Обновления существующих данных являются редким явлением, и контент-обновления никогда не происходят. Данные временных рядов - это преимущественно новые данные, которые никогда не обновляются.

Pro: Ограничение доступа к обновлениям позволяет повысить производительность запросов и записи

Con: функциональность обновления значительно ограничена

В открыть исходные критерии,

InfluxDB превзошел MongoDB во всех трех тестах с большей пропускной способностью записи на 21 раз, при использовании на дисках на 84 раза меньше и обеспечив относительно равную производительность при достижении скорости запроса.

InfluxDB vs. MongoDB требования к дисковой памяти и сжатие

Запросы также очень просты. Если ваши строки выглядят как <symbol, timestamp, open, high, low, close, volume>, с помощью InfluxDB вы можете просто сохранить это, а затем запросить легко. Скажем, за последние 10 минут данных:

SELECT open, close FROM market_data WHERE symbol = 'AAPL' AND time > '2012-04-12 12:15' AND time < '2012-04-13 12:52'

Нет идентификаторов, никаких ключей и никаких объединений. Вы можете сделать много интересных агрегатов. Вам не нужно вертикально разбить таблицу как на PostgreSQL, или свести вашу схему в массивы секунд как в MongoDB. Кроме того, InfluxDB сжимается очень хорошо, а PostgreSQL не сможет выполнить сжатие по типу данных, которые у вас есть.

Ответ 6

Вот попытка создания сервера рыночных данных в верхней части базы данных Microsoft SQL Server 2012, которая должна быть хорошей для анализа OLAP, свободного проекта с открытым исходным кодом:

http://github.com/kriasoft/market-data

Ответ 7

Позвольте мне порекомендовать вам взглянуть на apache solr, который, я думаю, был бы идеальным для вашей конкретной проблемы. В основном, вы должны сначала индексировать свои данные (каждая строка является "документом" ). Solr оптимизирован для поиска и изначально поддерживает запросы диапазона по датам. Ваш номинальный запрос,

"Give me the prices of AAPL between April 12 2012 12:15 and April 13 2012 12:52"

переведет что-то вроде:

?q=stock:AAPL AND date:[2012-04-12T12:15:00Z TO 2012-04-13T12:52:00Z]

Предполагая, что "запас" - это имя запаса, а "дата" - это "Датфилд", созданный из столбцов "дата" и "минута" ваших входных данных при индексировании. Солр невероятно гибкий, и я действительно не могу сказать достаточно хороших вещей об этом. Например, если вам нужно поддерживать поля в исходных данных, вы можете найти способ динамически создавать "DateField" как часть запроса (или фильтра).

Ответ 8

Во-первых, нет 365 торговых дней в году, с праздниками 52 выходных дней (104) = скажем, 250 x фактический часовой день открыт, как кто-то сказал, и использовать символ в качестве первичного ключа не хорошая идея, так как символы меняются, используйте k_equity_id (числовое) с символом (char), так как символы могут быть такими, как A или GAC-DB-B.TO, а затем в ваших таблицах данных информации о ценах, поэтому ваша оценка в 7,3 миллиарда намного превышает расчет, так как она составляет около 1,7 миллиона строк на символ в течение 14 лет.

k_equity_id k_date k_minute

и для таблицы EOD (которая будет просматриваться на 1000x по сравнению с другими данными)

k_equity_id k_date

Во-вторых, не храните данные OHLC по минутам в той же таблице DB, что и таблица EOD (конец дня), так как любой, кто хочет посмотреть на pnf или линейную диаграмму, в течение года, имеет нулевой процент в минимальной информации.

Ответ 9

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

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

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

Ответ 10

Вам следует сравнить медленные решения с простой оптимизированной моделью памяти. Он несжатый, он помещается в сервер RAM RAM объемом 256 ГБ. Снимок подходит в 32 К, и вы просто индексируете его по позициям на дату и время. Затем вы можете создавать специализированные снимки, так как их открытие часто равно закрытию предыдущего.

[править] Почему, по вашему мнению, имеет смысл использовать базу данных вообще (rdbms или nosql)? Эти данные не изменяются, и они вписываются в память. Это не тот случай, когда dbms может добавить значение.

Ответ 11

Если у вас есть оборудование, я рекомендую MySQL Cluster. Вы получаете интерфейс MySQL/RDBMS, с которым вы так хорошо знакомы, и получаете быстрые и параллельные записи. Считывание будет медленнее, чем обычный MySQL из-за задержек в сети, но у вас есть преимущество в возможности распараллеливать запросы и чтения из-за того, как работает MySQL Cluster и NDB.

Удостоверьтесь, что у вас достаточно машин MySQL Cluster и достаточно памяти/оперативной памяти для каждого из них, хотя MySQL Cluster представляет собой архитектуру базы данных с большой памятью.

Или Redis, если вы не против интерфейса key-value/NoSQL для чтения/записи. Удостоверьтесь, что Redis имеет достаточно памяти - его сверхбыстрый для чтения и записи, вы можете делать с ним базовые запросы (хотя и не RDBMS), но также является базой данных в памяти.

Как говорили другие, более подробная информация о запросах, которые вы будете запускать, поможет.

Ответ 12

Если ваш вариант использования - простые строки чтения без агрегации, вы можете использовать Aerospike cluster. Это в базе данных памяти с поддержкой файловой системы для сохранения. Он также оптимизирован для SSD.

Если для вашего случая использования необходимы агрегированные данные, перейдите к кластеру Mongo DB с диапазоном дат. Вы можете вводить данные о тире в год в осколках.

Ответ 13

Вам понадобятся данные, хранящиеся в столбце столбчатой ​​таблицы/базы данных. Системы баз данных, такие как Vertica и Greenplum, представляют собой столбчатые базы данных, и я считаю, что SQL Server теперь позволяет использовать столбчатые таблицы. Они чрезвычайно эффективны для SELECT из очень больших наборов данных. Они также эффективны при импорте больших наборов данных.

Свободная столбчатая база данных MonetDB.