Найкращий дизайн бази даних та таблиці для мільярдів рядків даних [закрито]


74

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

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

Інформація, яку мені потрібно зберігати (наразі) - це Ідентифікатор місцезнаходження, часова марка (дата та час), температура та використання електричної енергії.

Щодо обсягу даних, який потрібно зберігати, це приблизне значення, але щось у цьому напрямку:
20 000+ місцеположень, 720 записів на місяць (погодинні вимірювання, приблизно 720 годин на місяць), 120 місяців (10 років тому) ) і багато років у майбутнє. Прості розрахунки дають такі результати:

20 000 локацій x 720 записів x 120 місяців (10 років тому) = 1 728 000 000 записів .

Це минулі записи, нові записи будуть імпортовані щомісяця, тобто приблизно 20 000 х 720 = 14 400 000 нових записів на місяць .

Постійно зростатимуть і загальні локації.

На всіх цих даних необхідно виконати наступні операції:

  1. Отримайте дані за певну дату І період часу: усі записи для певного ідентифікатора місцеположення між датами 01.01.2013 та 01.01.2017 та між 07:00 та 13:00.
  2. Прості математичні операції протягом певного періоду часу та часу, наприклад, температура MIN та MAG та використання електроенергії для певного ідентифікатора місцеположення протягом 5 років між 07:00 та 13:00.

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

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

Основними моїми варіантами були Кассандра та MongoDB, але я маю дуже обмежені знання та немає реального досвіду, коли мова йде про великі дані та NoSQL, я не дуже впевнений. Я також читав, що PostreSQL також добре обробляє такі обсяги даних.

Мої запитання такі:

  1. Чи варто використовувати базу даних NoSQL для таких великих обсягів даних. Якщо я не можу дотримуватися MySQL?
  2. Яку базу даних я повинен використовувати?
  3. Чи слід зберігати дату та час в окремих, індексованих (по можливості) стовпцях, щоб швидко отримувати та обробляти дані протягом певного часу та періодів дати, чи це можна зробити, зберігаючи часову позначку в одному стовпчику?
  4. Чи підходить тут моделювання даних часових рядів, і якщо ні, ви не могли б дати мені покажчики на гарний дизайн таблиці?

Дякую.


29
2017. Хоча це не мало, це не особливо велика кількість даних для належного обладнання. І я ненавиджу вам говорити, але поки що те, що у вас там, звучить як реляційні дані.
TomTom

6
Я зберігав таблиці з декількома туберкульозами з десятками мільярдів рядків у MS SQL Server 2008-2014, використовуючи хороший ключ (дата епохи), стиснення, розділення та забезпечення того, щоб мої запити / індекси вирівнювали розділи. Мені довелося перейти на NoSQL (Hadoop), коли я почав отримувати петабайт даних для аналізу та індексації по-різному. NoSQL повинен мати інші міркування, і в цьому випадку він, здається, не підходить.
Алі Разегі

3
@AliRazeghi Hadoop не має нічого спільного з SQL або NoSQL - це просто двигун зберігання даних. Існує безліч інтерфейсів SQL, підтримуваних Hadoop.
мустаччо

3
Які ваші обмеження: гроші, витрачені на програмне забезпечення / ліцензії?
користувач3067860

1
Коли у вас є нескінченні гроші, то я б запропонував придбати прилад SAP HANA. Він чудово підходить для агрегації на великих наборах даних. Але ви, швидше за все, не мали нескінченних грошей.
Філіп

Відповіді:


90

Це саме те, що я роблю кожен день, за винятком того, що замість того, щоб використовувати щогодини дані, я використовую дані 5 хвилин. Я завантажую близько 200 мільйонів записів щодня, тому сума, про яку ви тут говорите, не є проблемою. Дані за 5 хвилин мають розмір близько 2 ТБ, і я маю дані про погоду за 50 років за погодинним рівнем розташування. Тож дозвольте відповісти на запитання на основі мого досвіду:

  1. Не використовуйте для цього NoSQL. Дані високоструктуровані та ідеально підходять для реляційної бази даних.
  2. Я особисто використовую SQL Server 2016 і не маю жодних проблем із застосуванням обчислень для цього обсягу даних. Спочатку це було в екземплярі PostgreSQL, коли я розпочав свою роботу, і він не міг обробляти об'єм даних, як це було у невеликому екземплярі AWS.
  3. Я настійно рекомендую витягувати частину години дати та зберігати її окремо від самої дати. Повірте, вчитися на моїх помилках!
  4. Я зберігаю більшість списків даних (DATE, TIME, DATAPOINT_ID, VALUE), але люди не хочуть інтерпретувати дані. Будьте готові до деяких жахливих запитів щодо даних та величезної кількості поворотів. Не бійтеся створити денормалізовану таблицю для наборів результатів, які є занадто великими, щоб обчислити їх на льоту.

Загальна порада: Більшість даних я зберігаю між двома базами даних, перша - це дані прямого періоду часу та нормалізується. Моя друга база даних дуже денормована і містить попередньо агреговані дані. Настільки швидко, як і моя система, я не осліплю від того, що користувачі навіть не хочуть чекати 30 секунд, щоб звіт завантажився - навіть якщо я особисто думаю, що 30 секунд для стискання 2 ТБ даних надзвичайно швидко.

Щоб детальніше пояснити, чому я рекомендую зберігати годину окремо від дати, ось декілька причин, чому я це роблю так:

  1. Спосіб подання електричних даних проводиться за допомогою " Hour Ending"- отже, 01:00 - це фактично середнє значення електроенергії за попередню годину, а 00:00 - година закінчення 24. (Це важливо, оскільки вам потрібно шукати дві дати, щоб включати значення 24 години - день, коли ви шукають плюс першу позначку наступного дня.) Однак дані про погоду фактично подаються вперед (фактично і прогноз на наступну годину). На моєму досвіді з цими даними, споживачі бажають проаналізувати вплив впливу погоди на ціну / попит на електроенергію. Якби ви використовували прямолінійне порівняння дати, ви фактично порівнювали б середню ціну за попередню годину з середньою температурою для наступної години, хоча часові позначки однакові.DATETIME стовпчик.
  2. Продуктивність. Я б сказав, що принаймні 90% звітів, які я створюю, - це графіки, як правило, графік ціни за годину або за одну дату, або за певний час. Виділення часу з дати може зменшити швидкість запиту, який використовується для створення звіту, залежно від діапазону дат, який ви хочете побачити. Не рідкість, коли споживачі хочуть побачити одну дату, рік-рік-рік за останні 30 років (адже для погоди це потрібно для генерації норм 30 років) - це може бути повільним. Звичайно, ви можете оптимізувати запит і додати індекси, і довірте мені, у мене є кілька божевільних індексів, яких я б хотів не мати, але це змушує систему працювати швидко.
  3. Продуктивність. Я ненавиджу писати один і той же фрагмент коду не один раз. Я зберігав дату та час у тому ж стовпці, поки мені не довелося писати один і той же запит знову і знову, щоб витягнути часовий проміжок. Через деякий час мені просто стало нудно робити це і витягнути його до власної колонки. Чим менше код вам потрібно написати, тим менше шансів на помилку. Крім того, необхідність писати менше коду означає, що ви можете швидше виводити звіти, ніхто не хоче чекати цілих днів звітів.
  4. Кінцеві користувачі. Не всі кінцеві користувачі є споживачами живлення (тобто вміють писати SQL). Наявність даних, вже збережених у форматі, який вони можуть внести в Excel (або інший подібний інструмент) з мінімальними зусиллями, зробить вас героєм в офісі. Якщо користувачі не можуть легко отримати доступ до даних або керувати ними, вони не використовуватимуть вашу систему. Повірте, я створив ідеальну систему пару років тому, і ніхто не використовував її через цю причину. Дизайн бази даних полягає не лише в дотриманні заздалегідь визначеного набору правил / вказівок, а в тому, щоб зробити систему зручною.

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


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

Я спочатку зберігав дату / час у UTC, але потім споживачі скаржилися, оскільки їм завжди доведеться підлаштовуватися під місцевий час. Зрештою мій дизайн змінився, щоб споживачам було легше використовувати дані.
Містер Броунстоун

4
Я з цим не погоджуюся. Нічого з цього не викликає побоювань у сучасній базі даних, як це демонструється фактичними цифрами . Якщо користувачі даних занадто дурні, щоб використовувати sql, тоді вам потрібно створити для них інтерфейс - схему ви не з’єднуєте. Витягнути годину - погана ідея
Еван Керролл

1
Яке таке обладнання?
kennes

1
Це неймовірне обладнання, залежно від того, скільки користувачів ви обслуговуєте. Оскільки це відповідь на псевдооптимізацію, я думаю, що включення вашої технології є корисним. Я був у повному шоці, почувши, що ти можеш розчавити 2 Тб за 30 секунд - це неймовірно швидко. Моє особисте судження убік, я думаю, це буде корисно майбутнім людям, які прагнуть оптимізувати дані часових рядів!
kennes

57

Індекси PostgreSQL та BRIN

Перевірте це на собі. Це не проблема для 5-річного ноутбука з ssd.

EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
  SELECT
    x::int AS id,
    (x::int % 20000)::int AS locid,  -- fake location ids in the range of 1-20000
    now() AS tsin,                   -- static timestmap
    97.5::numeric(5,2) AS temp,      -- static temp
    x::int AS usage                  -- usage the same as id not sure what we want here.
  FROM generate_series(1,1728000000) -- for 1.7 billion rows
    AS gs(x);

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series gs  (cost=0.00..15.00 rows=1000 width=4) (actual time=173119.796..750391.668 rows=1728000000 loops=1)
 Planning time: 0.099 ms
 Execution time: 1343954.446 ms
(3 rows)

Тому для створення таблиці знадобилося 22 хвилини. Багато в чому тому, що стіл - скромні 97 Гб. Далі ми створюємо індекси,

CREATE INDEX ON electrothingy USING brin (tsin);
CREATE INDEX ON electrothingy USING brin (id);    
VACUUM ANALYZE electrothingy;

На створення індексів також знадобилося багато часу. Хоча через те, що вони БРІН, вони лише в 2-3 МБ, і вони легко зберігаються в операційній пам'яті. Читання 96 Гб не є миттєвим, але це не справжня проблема для мого ноутбука при вашому навантаженні.

Тепер ми його запитуємо.

explain analyze
SELECT max(temp)
FROM electrothingy
WHERE id BETWEEN 1000000 AND 1001000;
                                                                 QUERY PLAN                                                                  
---------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5245.22..5245.23 rows=1 width=7) (actual time=42.317..42.317 rows=1 loops=1)
   ->  Bitmap Heap Scan on electrothingy  (cost=1282.17..5242.73 rows=993 width=7) (actual time=40.619..42.158 rows=1001 loops=1)
         Recheck Cond: ((id >= 1000000) AND (id <= 1001000))
         Rows Removed by Index Recheck: 16407
         Heap Blocks: lossy=128
         ->  Bitmap Index Scan on electrothingy_id_idx  (cost=0.00..1281.93 rows=993 width=0) (actual time=39.769..39.769 rows=1280 loops=1)
               Index Cond: ((id >= 1000000) AND (id <= 1001000))
 Planning time: 0.238 ms
 Execution time: 42.373 ms
(9 rows)

Оновити часові позначки

Тут ми генеруємо таблицю з різними часовими позначками, щоб задовольнити запит на індексацію та пошук у стовпчику часових позначок, створення займає трохи більше часу, оскільки to_timestamp(int)це значно повільніше, ніж now()(що кешується для транзакції)

EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
  SELECT
    x::int AS id,
    (x::int % 20000)::int AS locid,
    -- here we use to_timestamp rather than now(), we
    -- this calculates seconds since epoch using the gs(x) as the offset
    to_timestamp(x::int) AS tsin,
    97.5::numeric(5,2) AS temp,
    x::int AS usage
  FROM generate_series(1,1728000000)
    AS gs(x);

                                                               QUERY PLAN                                                                
-----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series gs  (cost=0.00..17.50 rows=1000 width=4) (actual time=176163.107..5891430.759 rows=1728000000 loops=1)
 Planning time: 0.607 ms
 Execution time: 7147449.908 ms
(3 rows)

Тепер замість цього ми можемо запустити запит на значення часової позначки ,,

explain analyze
SELECT count(*), min(temp), max(temp)
FROM electrothingy WHERE tsin BETWEEN '1974-01-01' AND '1974-01-02';
                                                                        QUERY PLAN                                                                         
-----------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=296073.83..296073.84 rows=1 width=7) (actual time=83.243..83.243 rows=1 loops=1)
   ->  Bitmap Heap Scan on electrothingy  (cost=2460.86..295490.76 rows=77743 width=7) (actual time=41.466..59.442 rows=86401 loops=1)
         Recheck Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
         Rows Removed by Index Recheck: 18047
         Heap Blocks: lossy=768
         ->  Bitmap Index Scan on electrothingy_tsin_idx  (cost=0.00..2441.43 rows=77743 width=0) (actual time=40.217..40.217 rows=7680 loops=1)
               Index Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
 Planning time: 0.140 ms
 Execution time: 83.321 ms
(9 rows)

Результат:

 count |  min  |  max  
-------+-------+-------
 86401 | 97.50 | 97.50
(1 row)

Тож за 83.321 мс ми можемо зібрати 86 401 запис у таблиці з 1,7 мільярдами рядків. Це повинно бути розумним.

Закінчення години

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

SELECT date_trunc('hour', tsin) + '1 hour' AS tsin,
  count(*),
  min(temp),
  max(temp)
FROM electrothingy
WHERE tsin >= '1974-01-01'
  AND tsin < '1974-01-02'
GROUP BY date_trunc('hour', tsin)
ORDER BY 1;
          tsin          | count |  min  |  max  
------------------------+-------+-------+-------
 1974-01-01 01:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 02:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 03:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 04:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 05:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 06:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 07:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 08:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 09:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 10:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 11:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 12:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 13:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 14:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 15:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 16:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 17:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 18:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 19:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 20:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 21:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 22:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-01 23:00:00-06 |  3600 | 97.50 | 97.50
 1974-01-02 00:00:00-06 |  3600 | 97.50 | 97.50
(24 rows)

Time: 116.695 ms

Важливо зазначити, що він не використовує індекс на агрегацію, хоча це міг би. Якщо це ваш типовий запит, ви, мабуть, хочете, щоб BRIN на date_trunc('hour', tsin)ньому лежить, є невелика проблема, яка date_truncне є непорушною, тому вам доведеться спочатку обернути його, щоб зробити так.

Розмежування

Іншим важливим моментом інформації на PostgreSQL є те, що PG 10 приносить розділення DDL . Так ви, наприклад, можете легко створювати розділи на кожен рік. Розбиття вашої скромної бази даних на незначні, які є крихітними. Роблячи це, ви повинні мати можливість використовувати та підтримувати btree індекси, а не BRIN, що було б ще швидше.

CREATE TABLE electrothingy_y2016 PARTITION OF electrothingy
    FOR VALUES FROM ('2016-01-01') TO ('2017-01-01');

Або що завгодно.


13

Мене дивує те, що тут ніхто не згадував бенчмаркінг - це поки @EvanCarroll не прийшов разом зі своїм чудовим внеском!

Якби я був ти, я витратив би деякий час (і так, я знаю, що це дорогоцінний товар!), Налаштовуючи системи, виконуючи те, що, на вашу думку, буде (отримайте тут кінцевий користувач!), Скажімо, ваші 10 найпоширеніших запитів.

Мої власні думки:

Рішення NoSQL можуть працювати дуже добре для конкретних випадків використання, але часто негнучкі до спеціальних запитів. Для забавного прийому на NoSQL Брайана Акера - колишнього головного архітектора MySQL, дивіться тут !

Я погоджуюся з @ Mr.Brownstone, що ваші дані виразно підходять до реляційного рішення (і цю думку підтвердив Еван Керролл )!

Якби я взяв на себе будь-які витрати, це було б для моєї дискової технології! Я б витрачав будь-які гроші, які мав у своєму розпорядженні на NAS або SAN або, можливо, на деяких SSD дисках, щоб зберігати мої рідко написані сукупні дані!

По- перше , я хотів би подивитися на те , що я є в наявності в даний час . Проведіть кілька тестів і покажіть результати керівникам, які приймають рішення. У вас вже є проксі-сервер у вигляді роботи ЕК ! Але швидкий тест або два збиті разом на вашому власному обладнанні були б переконливішими!

Тоді подумайте про те, щоб витратити гроші! Якщо ви збираєтеся витрачати гроші, перегляньте спочатку апаратне забезпечення, а не програмне забезпечення. AFAIK, ви можете найняти дискову технологію на випробувальний період, а ще краще, розгорнути на хмарі пару доказів концепції.

Мій особистий перший порт заклику для такого проекту, як це був би PostgreSQL. Це не означає, що я би виключав ідентичне рішення, але закони фізики та диски однакові для всіх! "Yae cannae beet закони про" фізику Джима ":-)


6

Якщо ви ще цього не зробили, перегляньте СУБД часового ряду, оскільки він оптимізований для зберігання та запиту даних, де основним фокусом є тип дати / часу. Зазвичай бази даних часового ряду використовуються для запису даних у діапазоні хвилини / секунди / підсекунди, тому я не впевнений, чи це все-таки підходить для погодинних кроків. Однак, цей тип СУБД, мабуть, варто вивчити. В даний час InfluxDB, здається, є найбільш усталеною та широко використовуваною базою даних часових рядів.


1
Який приклад СУБД часового ряду?
єпископ

2
Погляньте тут .
Vérace

4

Зрозуміло, що це не проблема NoSQL, але я б припустив, що, хоча рішення RDBMS буде працювати, я думаю, що підхід OLAP підійде набагато краще, і з огляду на дуже обмежений діапазон даних, я настійно пропоную дослідити використання БД на основі стовпців. а не рядковий. Подумайте про це таким чином, у вас може бути 1,7 мільярда частин даних, але вам потрібно лише 5 біт, щоб індексувати кожне можливе значення години або дня місяця.

У мене є досвід роботи з подібною проблемною областю, де Sybase IQ (зараз SAP IQ) використовується для зберігання до 300 мільйонів лічильників на годину даних управління продуктивністю телекомунікаційного обладнання, але я сумніваюся, чи є у вас бюджет на таке рішення. На арені з відкритим кодом MariaDB ColumnStore є дуже перспективним кандидатом, але я б рекомендував також дослідити MonetDB.

Оскільки ефективність запитів є головним рушієм для вас, врахуйте, як запити будуть формулюватися. Ось де OLAP та RDBMS виявляють свої найбільші відмінності: - за допомогою OLAP ви нормалізуєтеся для виконання запитів, а не зменшуйте повторення, зменшуйте сховище та навіть не наводите послідовність. Отже, крім оригінальної часової позначки (ви пам’ятаєте, щоб захопити її часовий пояс, сподіваюся?), Є окреме поле для часової позначки UTC, інші - за датою та часом, а ще більше за рік, місяць, день, годину, хвилину та зміщення UTC. Якщо у вас є додаткова інформація про місцеположення, сміливо зберігайте її в окремій таблиці місцеположень, яку можна шукати на вимогу, і не соромтесь зберігати ключ до цієї таблиці у вашому головному записі, але зберігайте повне ім’я місцезнаходження у головній таблиці як ну, зрештою,

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


Ви також можете розглянути Greenplum як стовпчик, якщо ви дивитесь на них! Як "бонус" - він заснований на PostgreSQL!
Vérace

Я мав хороший досвід роботи з HP Vertica. У нас була одна таблиця з 9 стовпцями, яка мала 130 мільярдів рядків, без великої настройки. Це просто спрацювало.
ThatDataGuy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.