Ускорение дорогостоящей операции соединения - огромный стол с маленьким - PullRequest
0 голосов
/ 04 января 2019

Я использую Postgres для хранения большого количества транзакций и пытаюсь сохранить время чтения для определенного оператора Select в десятках миллисекунд.

Схема таблицы A (> 100-миллиметровые строки): (userID int, itemID int). Индексируется по идентификатору пользователя

Схема таблицы B (строки 1 мм): (categoryID int, itemID int). Индексируется по категории ID. Количество категорий = 500, и каждый itemID принадлежит только одной категории.

Запрос, который я хочу оптимизировать, для выполнения которого в настоящий момент у меня уходит ~ 100 мс:

select * from TableA 
where userID = x and itemID in 
(select itemID from TableB
where categoryID = y)

Простым способом решения этой проблемы было бы создание денормализованной таблицы с userID, itemID и categoryID в качестве столбцов и индексацией (userID, categoryID). Однако сопоставление categoryID -> itemID может измениться, поэтому я хотел избежать полного сканирования таблицы и обновлять строки каждый раз, когда это происходит.

Существуют ли другие методы / методы индексации для ускорения этой операции JOIN? Любые альтернативные способы размещения данных также приветствуются. Спасибо!

Редактировать: добавление примера плана запроса.

[('  ->  Hash Semi Join  (cost=159.50..382.67 rows=164 width=50)'),
 ('        Hash Cond: (tableA.itemId = tableB.itemId)'),
 ('        ->  Index Scan using userId on tableA  (cost=0.57..208.31 rows=5185 width=50)'),
 ('              Index Cond: (userId = 4000)'),
 ('        ->  Hash  (cost=117.05..117.05 rows=3350 width=4)'),
 ('              Buckets: 4096  Batches: 1  Memory Usage: 161kB',),
 ('              ->  Index Scan using categoryId on tableB (cost=0.42..117.05 rows=3350 width=4)'),
 ('                    Index Cond: (categoryId = 1002)',), ('Planning time: 0.149 ms',)]

Ответы [ 3 ]

0 голосов
/ 04 января 2019

Другой подход заключается в создании массива действительных itemID и фильтрации по нему. Тогда вы избежите операции JOIN. Это может быть медленнее, в зависимости от ваших данных.

select * from TableA 
where userID = x
  and itemID = any((select array_agg(/*DISTINCT */itemID)
                      from TableB
                     where categoryID = y)::int4[])
0 голосов
/ 05 января 2019

Я нашел изящный способ решить эту проблему путем денормализации таблицы A и использования внешних ключей Postgres.

Schema of TableA (> 100mm rows): (userID int, itemID int, categoryID int)
Index - (userID, categoryID)
FK - (itemID, categoryID) references tableB (itemID, categoryID)
update cascade
delete cascade

Schema of TableB (1mm rows): (categoryID int, itemID int)
PK - (itemID, categoryID)

Все пары элементов пользователя для категории теперь можно выбрать, выполнив выборку для таблицы A. Ограничение внешнего ключа гарантирует, что строки в tableA будут обновлены, если categoryID для любого элемента изменится в tableB.

select userid, itemid from tableA where userid = x and categoryid = y 

Спасибо за ваши предложения!

0 голосов
/ 04 января 2019

Возможно Exists поможет здесь: Разница между EXISTS и IN

По вашему запросу:

Select * from TableA a
Where userID = x
and exists (Select itemId from TableB b where categoryID = y  and a.itemId = b.itemId)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...