Как хранить 7,3 миллиарда строк рыночных данных (оптимизированных для чтения)? - PullRequest
76 голосов
/ 22 марта 2012

У меня есть набор данных за 1 минуту с данными о 1000 акциях с 1998 года, всего около 1001 * строк.

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

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

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

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

Edit:

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

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

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

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

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

Ответы [ 13 ]

47 голосов
/ 31 марта 2012

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

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

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

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

Затем обрежьте файл до этого размера и mmap (в Linux или используйте CreateFileMapping для Windows) в память:

//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;

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

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

27 голосов
/ 13 сентября 2016

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

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

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

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

InfluxDB vs Cassandra query speed

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

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

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

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

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

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

InfluxDB vs. MongoDB on-disk storage requirements and compression

Запросы также очень просты.Если ваши строки выглядят как <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 не сможет выполнить какое-либо сжатие для типа данных, которые у вас есть .

26 голосов
/ 22 марта 2012

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

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

Обновление

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

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

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

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

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

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

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

15 голосов
/ 24 марта 2012

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

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

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

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

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

РЕДАКТИРОВАТЬ: я думаю, что я пропустил объяснение почему этот подход хорошо подходит здесь: выУ вас есть очень предсказуемый аспект для большей части ваших данных - биржевой тикер, дата и время.Выражая тикер один раз (в качестве имени файла) и оставляя дату / время полностью неявно в позиции данных, вы удаляете целую кучу работы.Это немного похоже на разницу между String[] и Map<Integer, String> - знание того, что индекс вашего массива всегда начинается с 0 и увеличивается с шагом 1 до длины массива, обеспечивает быстрый доступ и более эффективное хранение.

14 голосов
/ 22 марта 2012

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

4 голосов
/ 13 августа 2013

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

k_equity_id k_date k_minute

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

k_equity_id k_date

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

4 голосов
/ 11 декабря 2012

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

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

3 голосов
/ 30 марта 2012

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

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

3 голосов
/ 27 марта 2012

Позвольте мне порекомендовать вам взглянуть на 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]

Предполагая, что "stock" - это название акции, а "date" - это "DateField", созданное из столбцов "date" и "minute" ваших входных данных при индексации. Solr невероятно гибок, и я действительно не могу сказать достаточно хороших слов об этом. Так, например, если вам нужно сохранить поля в исходных данных, вы, вероятно, можете найти способ динамически создать «DateField» как часть запроса (или фильтра).

2 голосов
/ 20 сентября 2016

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...