加快昂贵的联接操作-大型表和小型表

时间:2019-01-04 08:35:34

标签: postgresql indexing

我正在使用Postgres存储大量事务,并试图将特定Select语句的读取时间保持在几十毫秒内。

TableA的架构(> 100mm行):( userID int,itemID int)。按用户ID索引

TableB的架构(1mm行):( categoryID int,itemID int)。按categoryID索引。类别数= 500,每个itemID仅属于一个类别。

我要为其优化的查询目前需要约100毫秒来执行:

[('  ->  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',)]

解决此问题的简单方法是创建一个非规范化表,其中将userID,itemID和categoryID作为列和索引(userID,categoryID)。但是,categoryID-> itemID映射可以更改,因此我想避免对表进行全面扫描,并且每次发生时都更新行。

是否还有其他技术/索引方法来加快此JOIN操作?布置数据的任何替代方式也将被理解。谢谢!

编辑:添加示例查询计划。

ncols         174
nrows         115
xllcorner     14.97
yllcorner     -34.54
cellsize      0.11

3 个答案:

答案 0 :(得分:1)

也许“存在者”会在这里提供帮助: Difference between EXISTS and IN

对于您的查询:

Select * from TableA a
Where userID = x
and exists (Select itemId from TableB b where categoryID = y  and a.itemId = b.itemId)

答案 1 :(得分:0)

另一种方法是创建有效itemID的数组并对其进行过滤。然后,您将避免JOIN操作。但是,它可能会变慢,具体取决于您的数据。

select * from TableA 
where userID = x
  and itemID = any((select array_agg(/*DISTINCT */itemID)
                      from TableB
                     where categoryID = y)::int4[])

答案 2 :(得分:0)

我找到了一种巧妙的方法来解决此问题,方法是对tableA进行非规范化并使用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)

现在可以通过在tableA上进行选择来获取类别的所有用户项对。如果表B中任何项目的categoryID更改,则外键约束可确保表A中的行得到更新。

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

感谢您的建议!