Повільні запити в таблиці мільярдів-рядків // використаний індекс


10

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

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

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

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)

Оскільки існує кілька типів даних, які блок даних може (або не може) включати, існують інші таблиці, на які посилається data_blockтаблиця.

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...

Можливо, що в одному блоці даних є 3x даніA, 1x dataB, але немає данихC.

Дані зберігатимуться протягом декількох тижнів, тому в цій таблиці я буду мати ~ 5 мільярдів рядків. На даний момент у мене ~ 600 мільйонів рядків у таблиці, і мої запити займають дуже багато часу. Тому я вирішив скласти індекс timestampі більше mac, тому що мої вибрані висловлювання завжди запитують з часом, а часто і з часом + mac.

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);

... але мої запити все ще займають віки. Наприклад, я запитував дані за один день та один mac:

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Index Scan using index_ts_mac on data_block  (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
  Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms

Я зробив повний вакуум перед запуском запиту. Чи є елегантний спосіб вирішити таку проблему з великими таблицями, щоб зробити запит <10 сек?

Я читав про розділення, але це не працюватиме з моїми данимиA, dataB, посиланнями dataC на data_block_id, правда? Якби це якось спрацювало, чи варто робити перегородки з часом або над mac?

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

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);

Але все-таки запити займають> 30 сек. Особливо, коли я роблю LEFT JOINзі своїми таблицями даних. Ось EXPLAIN ANALYZEзапит із новим індексом:

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block  (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
  Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on index_mac_ts  (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
        Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms 

На жаль, моя апаратура суворо обмежена. Я використовую Intel i3-2100 @ 3.10Ghz, 4 Гб оперативної пам’яті. Мої поточні налаштування такі:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2

Відповіді:


1

Це може відображати моє упередження MS SQL, але я б спробував класифікувати таблицю за timestamp. Якщо ви часто перетягуєте дані протягом певного періоду часу, це допоможе, оскільки дані фізично зберігатимуться безперервно. Система може прагнути до точки початку, сканувати до кінця діапазону і зробити це. Якщо ви запитуєте протягом конкретної години, це лише 3 600 000 записів.

Якщо ваш запит (який є ...?) Призначений для певної машини, після Postgres потрібно буде відфільтрувати 99,9% цих 3,6-мільймових записів. Якщо цей фільтр на тисячу тисяч вибірковіший, ніж типовий фільтр дат, слід використовувати поле більш селективне macяк перший компонент вашого індексу. Можливо, все-таки варто скупчитись.

Якщо це все ще не робить, я б розділив те саме поле, яке ви індексуєте, timestampабо mac.

Ви не вказали типи даних. Чи вони відповідають даним? Наприклад, зберігання дат у тексті не потребує розлиття таблиці.


2
У Postgres немає кластерних індексів (хоча він може кластеризувати таблицю вздовж індексу - але це потрібно зробити вручну і не буде "залишатися")
a_horse_with_no_name

дякую за пораду. тепер він працює швидше, ніж раніше, але все ще при дуже низькій продуктивності> 30 сек за запит. Я також робив кластеризацію, але як сказав @a_horse_with_no_name: у postgres це один кадр. мої типи даних вірні, я думаю. я додав їх у запитання
manman

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

-2

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

Наше оточення було іншим. Microsoft SQL Server на машині класу сервера (4 ядра, 24 ГБ пам'яті). Будь-який шанс перейти на сервер?

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

У вашій схемі у вас буде 4 дуже великих таблиці. Важливо, щоб усі ваші об’єднання використовували індекси в обох таблицях. Сканування таблиці займе назавжди. Чи доцільно об'єднати їх у 1 таблицю з полями, які не мають змоги?


вставки в партіях: я міг би робити масові вставки, але на даний момент я працюю над тестовою базою даних, де взагалі не робиться вставок під час запуску запиту. але дякую, я подумаю про це пізніше :) індекси: у мене є індекси на всіх таблицях. у таблицях даних індекс на id, у таблиці data_block на (mac, часова мітка). Проблема також є, коли я шукаю даніA зліва, але немає. навіть за допомогою індексу він шукає таблиці даних. нульові поля: не можливі, тому що в блоці даних може бути декілька даних одного виду. 1xdata_block -> 4xdataA напр.
manman

Чи дає ваш інструмент DB аналізатор запитів? Можливо, вам знадобиться індекс на data_block на основі id.
KC-NH

Я спробую, але я не розумію, чому це може допомогти!
manman

-2

Ви потрапляєте в притаманні межі масштабованості Postgres (або будь-який інший RDBMS).

Пам'ятайте, що індекс RDBMS є B-деревом. B-Дерево - це O (log n) як для середнього, так і для найгіршого випадку. Це робить його приємним, безпечним, передбачуваним вибором для розумних значень N. Він руйнується, коли N стає занадто великим.

Бази даних NoSQL є (здебільшого) хеш-таблицями. Хеш-таблиця є O (1) в середньому випадку і O (n) в гіршому. Якщо припустити, що ви можете уникнути найгіршого випадку, він спрацьовує дуже добре для дуже великих значень N.

Крім того, хеш-таблицю легко паралелізувати, а b-дерево - ні. Це робить хеш-таблиці більш придатними для розподіленої архітектури обчислень.

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


2
Багато RDBMS мають набагато більше варіантів, ніж індекси B-дерева (хеш, растрові карти та інші). Деякі СУБД зберігають рядки, а деякі зберігають стовпці. І O (logn) непоганий, навіть для мільярдів рядків. І вони, можливо, не можуть досягти жодного обмеження, коли вони використовують пам'ять 4 Гб.
ypercubeᵀᴹ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.