Медленные запросы к таблице миллиардов строк // индекс используется


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 dataA, 1x dataB, но нет dataC.

Данные будут храниться в течение нескольких недель, поэтому в этой таблице будет около 5 миллиардов строк. На данный момент в таблице ~ 600 миллионов строк, и мои запросы занимают очень много времени. Поэтому я решил создать индекс заново timestampи mac, потому что мои операторы select всегда запрашивают со временем, а часто и со временем + 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 ​​секунд?

Я читал о разделении, но это не будет работать с моими dataA, 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,10 ГГц, 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 таблицу с пустыми полями?


вставки в пакетах: я мог бы делать массовые вставки, но в данный момент я работаю над тестовой базой данных, где никакие вставки вообще не выполняются во время выполнения запроса. но спасибо, я подумаю об этом позже :) индексы: у меня есть индексы на каждой таблице. для таблиц данных индекс по идентификатору, для таблицы data_block - (mac, timestamp). проблема также существует, когда я ищу dataA за левое соединение, но нет. даже с индексом он ищет таблицы данных. Обнуляемые поля: невозможно, потому что блок данных может иметь более одного типа данных. 1xdata_block -> 4xdataA напр.
manman

Ваш инструмент БД дает вам анализатор запросов? Вам может понадобиться индекс data_block на основе идентификатора.
KC-NH

Я попробую, но я не понимаю, почему это может помочь !?
Человек

-2

Вы выходите за пределы встроенной масштабируемости Postgres (или любой другой RDBMS).

Помните, что индекс RDBMS - это B-дерево. B-Tree - это O (log n) как для среднего, так и для наихудшего случая. Это делает его хорошим, безопасным, предсказуемым выбором для разумных значений N. Он ломается, когда N становится слишком большим.

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

Кроме того, хеш-таблицу легко распараллелить, а b-дерево - нет. Это делает хеш-таблицы более подходящими для архитектуры распределенных вычислений.

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


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