Оскільки я молодий розробник і не дуже досвідчений у використанні баз даних (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