Як зберігати 7,3 мільярда рядків ринкових даних (оптимізовано для читання)?


84

У мене є набір даних з 1 хвилини даних з 1000 запасів, починаючи з 1998 року, що складає приблизно (2012-1998)*(365*24*60)*1000 = 7.3 Billionрядки.

Більшу частину (99,9%) часу я виконую лише запити на читання .

Який найкращий спосіб зберігати ці дані в базі даних?

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

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

Редагувати:

Це зразок рядка:

`` 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, тому я гнучкий у цьому


5
Опишіть очікуваний типовий запит
Вільям Персел

10
"Я думаю, вам слід використовувати MongoDB, оскільки це веб-масштаб".
ta.speot.is

8
Напевно, вам потрібен один великий стіл, розділений символом запасу.
ta.speot.is

1
Набір даних величезний! Можливо, ви захочете пошукати дані та аналітику, щоб побачити, що ви знайдете.
Mike Purcell

2
І "стандартної СУБД" з єдиною таблицею для цього недостатньо? (Я маю справу лише з мільйонами, але "працює для мене". Можливо, просто спробуйте це і подивіться. Не забудьте індексувати / кластер / розділ, як потрібно.)

Відповіді:


30

Розкажіть про запити та апаратне середовище.

Я б дуже спокусився перейти на NoSQL , використовуючи Hadoop або щось подібне, якщо ви можете скористатися перевагами паралелізму.

Оновлення

Гаразд, чому?

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

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

  • Якщо ви робите те, що фактично великі пробіги проти багатьох даних у світі OLAP, однак, підхід, орієнтований на стовпці, може бути найкращим.

  • Якщо ви хочете робити випадкові запити, особливо роблячи перехресне порівняння, система Hadoop може бути ефективною. Чому? Оскільки

    • ви можете краще використовувати паралелізм на відносно невеликих товарних апаратних засобах.
    • Ви також можете краще реалізувати високу надійність та надмірність
    • багато з цих проблем природно піддаються парадигмі MapReduce.

Але справа в тому, що поки ми не дізнаємося про ваше навантаження, не можна сказати нічого остаточного.


7
Яку перевагу тут пропонує "NoSQL"? Чому б не один великий стіл у традиційній СУБД ? (З правильними індексами тощо) Кожен користується "NoSQL", "NoSQL", "NoSQL", але ... чому ?

5
Потрібно сказати, що моєю пропозицією буде також підхід NoSQL із використанням Apache Accumulo (це особиста перевага). Малий набір даних (для Accumulo) та тип запитів, які йому потрібні, здаються цілком підходящими за допомогою розподіленого стеку ітераторів.
Бінарний ботанік

Дякуємо за розширену відповідь. Я можу поставити +1.

1
Іноді деякі коментарі тут просто мене бентежать. '-1 для використання бази даних, де це не має сенсу?' Весь відповідь сперечається проти традиційної бази даних.
Charlie Martin

51

Отже, бази даних призначені для ситуацій, коли у вас є велика складна схема, яка постійно змінюється. У вас є лише одна "таблиця" із заповненою простими числовими полями. Я б зробив це так:

Підготуйте структуру C / C ++ для зберігання формату запису:

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

Потім обчисліть розмір (StockPrice [N]), де N - кількість записів. (На 64-бітній системі) Це має бути лише кілька сотень концертів і вміститися на жорсткий диск вартістю 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, і тепер ви отримаєте свої дані в одному великому двійковому масиві у файлі, який згодом можна буде знову створити mmaped.

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;

Тож тепер ви можете поводитися з нею як із масивом структур у пам'яті. Ви можете створювати різні типи індексних даних, залежно від того, які ваші "запити". Ядро буде мати справу з обміном даних на / з диска прозоро, тому це буде шалено швидко.

Якщо ви очікуєте мати певний шаблон доступу (наприклад, суміжну дату), найкраще відсортувати масив у такому порядку, щоб він послідовно потрапляв на диск.


11
Витратьте кілька сотень, щоб покласти його на SSD замість жорсткого диска. Випадкове читання відбувається приблизно в сто разів швидше. Або витратьте 10 тис. На баран. Ще в сто разів швидше
Стефан Еггермонт,

1
@Andrew Tomazos дякую, чувак, це одна "відповідь"
Pavneet_Singh

1
StockPrice sizeof буде char [4] = 4 байта int = 4 байти короткі = 2 байти float = 4 байти float = 4 байти float = 4 байти float = 4 байти float = 4 байти int = 4 байти int = 4 байти int = 4 байт ------------ 42 байти близько 306,6 мільярдів байт = ~ 285,5435013771057 ГБ пам'яті ... удачі вам у цьому
ZagNut

3
@ZagNut: Якщо ви маєте на увазі, що вам потрібно 300 ГБ фізичної пам’яті, то це неправильно - mmap не копіює всю річ в пам’ять, вона переглядає її в / у / за необхідності (так само, як і файл підкачки) .
Ендрю Томазос,

33

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

Зберігання один раз і багаторазове зчитування числових даних на основі часу є випадком використання, який називається "часовий ряд". Інші поширені часові ряди - це дані датчиків в Інтернеті речей, статистика моніторингу серверів, події додатків тощо.

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

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

InfluxDB проти швидкості запиту Кассандри

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

Оновлення існуючих даних трапляється рідко, і спірні оновлення ніколи не відбуваються. Дані часових рядів - це переважно нові дані, які ніколи не оновлюються.

Pro: Обмеження доступу до оновлень дозволяє підвищити ефективність запитів і записів

Недолік: функціональність оновлення значно обмежена

У тестах з відкритим джерелом ,

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

Вимоги до стиснення та стиснення на диску InfluxDB проти 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 не зможе виконувати жодне стиснення щодо типу даних, які у вас є .


17

Добре, отже, це дещо від інших відповідей, але ... мені здається, що якщо у вас є дані у файловій системі (можливо, один запас на файл) із фіксованим розміром запису, ви можете отримати дані дуже просто: отримавши запит для певного запасу та часового діапазону, ви можете шукати потрібне місце, отримати всі потрібні дані (ви точно будете знати, скільки байтів), перетворити дані у потрібний формат (що може дуже швидко, залежно від формату вашого сховища), і вас немає.

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

Далі ви додаєте кешування - я б запропонував надати різним серверам різні запаси для обробки, наприклад - і ви можете в значній мірі просто обслуговувати з пам'яті. Якщо ви можете дозволити собі достатньо пам'яті на достатній кількості серверів, обійдіть частину "навантаження на вимогу" і просто завантажте всі файли під час запуску. Це спростило б ситуацію за рахунок повільнішого запуску (що, очевидно, впливає на відмову, якщо ви не можете дозволити собі завжди мати два сервери для будь-якого конкретного запасу, що було б корисно).

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

Ви ще не сказали, на що схожі інші три цілочисельні стовпці, але якщо ви могли б уникнути 64 біт і для цих трьох, ви могли б зберегти цілий запис у 16 ​​байтах. Це всього ~ 110 ГБ для всієї бази даних, що насправді не дуже багато ...

РЕДАГУВАТИ: Інша річ, яку слід врахувати, полягає в тому, що, мабуть, запаси не змінюються ні на вихідних, ні навіть на ніч. Якщо фондовий ринок відкритий лише 8 годин на день, 5 днів на тиждень, то вам потрібно лише 40 значень на тиждень замість 168. На той момент у ваших файлах може бути лише близько 28 ГБ даних у ваших файлах ... що звучить набагато менший, ніж ви, мабуть, спочатку думали. Наявність такої кількості даних у пам’яті є цілком розумним.

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


Знову це залежить від того, як він використовує дані. Якщо його запит полягає в тому, щоб витягнути певні дані по всій дошці (з розумним символом запасу), це призведе до читання кожного файлу та наявності конкретних кодувань дати, щоб витягти правильні дані з кожного. Або якщо він хоче, щоб акції, що найкраще виконуються на тиждень, тоді це було б кошмаром, якщо такий тип налаштувань вимагає читати всі записи, сортувати та порівнювати. Без такої інформації ми можемо лише здогадуватися, що це для фіксованого сховища - можливо, як масовий DW, який в якийсь момент буде подавати звітний DW (джерело ETL).
Wolf5370

2
@ Wolf5370: Так, нам, звичайно, потрібно знати, якими будуть запити, але ми маємо принаймні деякі вказівки з питання: "Більшість запитів будуть такими:" Дайте мені ціни на AAPL між 12 квітня 2012 р. 12:15 та 13 квітня 2012 р. 12:52 ". Було б непогано знати, якими будуть інші запити, а також відносні частоти та вимоги до продуктивності.
Джон Скіт,

@JonSkeet це насправді залежить від робочого навантаження, але я маю певні знання домену про цю систему, і рідко буває просто "вибрати одну акцію в одному діапазоні": набагато частіше "вибирати акції в цьому портфоліо понад цей діапазон, обчислити & beta; а потім спробуйте цей список можливих запасів і подивіться, що таке "beta". " Ось чому це спонукає вас до чогось подібного до OLAP.
Charlie Martin

2
@CharlieMartin: Ну, я просто йшов по тому, що сказано в запитанні. Однак, якщо ви можете в основному отримати все це в пам'яті (на декількох серверах), це все ще досить просто - попросіть кожен сервер про відповідні запаси в портфелі, а потім складіть результати. Я думаю, що мій погляд на використання відомих аспектів даних (один раз на хвилину, але не на вихідних чи на ніч) все ще корисний з точки зору значного зменшення труднощів отримати все це в пам'яті.
Джон Скіт,

Ця дискусія нагадує мені цитату Фреда Брукса "Представлення - це суть програмування" та пов'язані з цим проблеми в "Перлах програмування" Бентлі.
CS

14

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


2
+1 для конкретного рішення. Я, однак, люблю SQL DQL (здебільшого) та гнучкість, яку він надає ... не впевнений, що потрібно HDF5, щоб вийти з "ієрархічного зору".

4

Ось спроба створити Сервер ринкових даних поверх бази даних Microsoft SQL Server 2012, що має бути добре для аналізу OLAP, безкоштовного проекту з відкритим кодом:

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


Так Не впевнений, чи застосовується цей конкретний проект, але, безумовно, пропонував би ОП розглянути структуру таблиць фактів OLAP або Data Warehousing, обидва підходи (іноді використовуються разом) розроблені для розгляду даних такого типу з дуже великою кількістю рядків. Це справді залежить від того, який аналіз вони мають намір провести.
AaronLS

4

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

k_equity_id k_date k_minute

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

k_equity_id k_date

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


3

Дозвольте мені порекомендувати вам поглянути на апаш-сольр , який, на мою думку, буде ідеальним для вашої конкретної проблеми. По суті, ви спочатку індексуєте свої дані (кожен рядок є "документом"). 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]

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


Ви також можете використовувати Amazon EC2 для налаштування вашого екземпляра solr
aliasmrchips

3
SOLR чудово працює для пошуку, але вам все одно потрібно десь зберігати дані, щоб заповнити індекси.
Mike Purcell

Правда. Я припускаю, що Віктор П десь має дані, і їх потрібно буде проіндексувати. Для цього знадобляться додаткові ресурси ... Однак усі запропоновані підходи також роблять.
aliasmrchips

@aliasmrchips: Я думаю, що підхід InfluxDB працює краще - він одночасно ефективно зберігає (висока пропускна здатність, стиск у 80 разів кращий, ніж Mongo), і легко запитує.
Дан Даскалеску

3

Я думаю, що будь-яка велика СУБД може впоратися з цим. На атомному рівні одна таблиця з правильним секціонуванням здається розумною (розділ, заснований на використанні ваших даних, якщо це виправлено - це, як правило, символ або дата).

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

Думаю, ми могли б допомогти вам більше з якоюсь ідеєю щодо використання даних.


3

Вам слід порівняти повільні рішення з простою оптимізованою в пам'яті моделлю. Нестиснуте вміщується на 256 ГБ оперативної пам'яті. Знімок поміщається в 32 К, і ви просто індексуєте його позиційно за датою та часом. Тоді ви можете робити спеціалізовані знімки, оскільки відкриття одного часто дорівнює закриттю попереднього.

[редагувати] Чому, на вашу думку, має сенс взагалі використовувати базу даних (rdbms або nosql)? Ці дані не змінюються, і вони поміщаються в пам’ять. Це не є випадком використання, коли DBM може додати значення.


Насправді, є кілька причин, не в останню чергу, якщо у вас є 256 ГБ пам'яті, було б непогано, якби було місце для тимчасового простору, операційної системи тощо. Потім виникають такі проблеми, як контрольна точка, реєстрація та відмовостійкість - як тільки ви почнете обчислювати будь-які проміжні результати, вам знову знадобиться керувати сховищем. Я погоджуюсь, що СУБД не є найкращим вибором - але щось розумніше, ніж "завантажити великий масив в пам'ять", є абсолютно необхідним.
Чарлі Мартін

контрольна точка, реєстрація та відмовостійкість надзвичайно проста для майже статичних даних. Це звучить як ідеальне рішення для стилю превайлера
Стівен Еггермонт,

Знову ж таки, без кращого знання програми не можна сказати точно, але загалом додаток не настільки статичний, як ви думаєте, тому що ви хочете підтримувати набори результатів і тому, що ви знову робите дорогі розрахунки , контрольна точка та попередньо обчислені часткові результати.
Чарлі Мартін,

2

Якщо у вас є обладнання, я рекомендую кластер MySQL . Ви отримуєте знайомий вам інтерфейс MySQL / RDBMS і отримуєте швидкі та паралельні записи. Читання буде повільнішим за звичайний MySQL через мережеву затримку, але ви маєте перевагу в тому, що можете паралелізувати запити та читання завдяки тому, як працює кластер MySQL та механізм зберігання NDB.

Переконайтеся, що у вас є достатньо машин MySQL Cluster і достатньо пам’яті / оперативної пам’яті для кожного з них - MySQL Cluster - це сильно орієнтована на пам’ять архітектура баз даних.

Або Redis , якщо вам не завадить інтерфейс ключ-значення / NoSQL для читання / запису. Переконайтеся, що Redis має достатньо пам’яті - надзвичайно швидкий для читання та запису, ви можете робити із ним основні запити (хоча це не RDBMS), але це також база даних у пам’яті.

Як сказали інші, знання більше про запити, які ви будете виконувати, допоможе.


2

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

Безкоштовна стовпчаста база даних - MonetDB .


1

Якщо ви використовуєте прості рядки для читання без агрегування, ви можете використовувати кластер Aerospike. Це в базі даних пам'яті з підтримкою файлової системи для постійності. Це також оптимізовано SSD.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.