从大表中有效地选择不同的(a,b)
Posted
技术标签:
【中文标题】从大表中有效地选择不同的(a,b)【英文标题】:Efficiently selecting distinct (a, b) from big table 【发布时间】:2019-10-04 00:27:09 【问题描述】:我在 Postgres 9.6 DB 中有一个包含大约 5400 万行的表,并且希望找到所有不同的两列对(大约有 400 万个这样的值)。我对感兴趣的两列有一个索引:
create index ab_index on tbl (a, b)
获得此类配对的最有效方法是什么?我试过了:
select a,b
from tbl
where a>$previouslargesta
group by a,b
order by a,b
limit 1000
还有:
select distinct(a,b)
from tbl
where a>previouslargesta
order by a,b
limit 1000
还有这个递归查询:
with recursive t AS (
select min(a) AS a from tbl
union all
select (select min(a) from tickets where a > t.a)
FROM t)
select a FROM t
但一切都很慢。
有没有更快的方法来获取这些信息?
【问题讨论】:
LIMIT 1000
是用于测试还是只需要 1000 个结果行?哪些行重要吗? (ORDER BY a,b
也很重要?)显示您的确切索引定义。平均行大小(以字节为单位)?你能显示你尝试过的递归查询吗?
嗨@erwinbrandstetter。感谢您对我的问题感兴趣。我最终需要得到 a 和 b 的所有组合。我使用的页面大小为 1000。
【参考方案1】:
您的表有 5400 万行并且...
大约有 400 万个这样的值
所有行的 7.4% 是一个很高的百分比,索引主要只能通过提供预排序的数据来提供帮助,最好是在 index-only scan 中。对于较小的结果集有更复杂的技术(见下文),并且有 much 更快的分页方法,一次返回更少的行(见下文),但对于一般情况,一个普通的@987654328 @ 可能是最快的:
SELECT DISTINCT a, b -- *no* parentheses
FROM tbl;
-- ORDER BY a, b -- ORDER BY wasn't not mentioned as requirement ...
不要将它与需要括号的DISTINCT ON
混淆。见:
(a, b)
上的 B-tree 索引 ab_index
已经是最好的索引。不过,必须对其进行全面扫描。挑战在于有足够的 work_mem
来处理 RAM 中的所有内容。使用标准设置,它在磁盘上至少占用 1831 MB,通常会更多,而且有些膨胀。如果您负担得起,请在会话中使用 2 GB(或更多)的 work_mem
设置运行查询。见:
SET work_mem = '2 GB';
SELECT DISTINCT a, b ...
RESET work_mem;
只读表有帮助。否则,您需要足够积极的VACUUM
设置以允许仅索引扫描。然而,更多的 RAM 将有助于(通过适当的设置)保持索引兑现。
还升级到 Postgres 的最新版本(撰写时为 11.3)。大数据有很多改进。
分页
如果您要按照示例查询的指示添加分页,请紧急考虑ROW 值比较。见:
Optimize query with OFFSET on large table SQL syntax term for 'WHERE (col1, col2) < (val1, val2)'SELECT DISTINCT a, b
FROM tbl
WHERE (a, b) > ($previous_a, $previous_b) -- !!!
ORDER BY a, b
LIMIT 1000;
递归 CTE
对于一般的大查询,这也可能更快,也可能不会更快。对于小子集,它变得更加更具吸引力:
WITH RECURSIVE cte AS (
( -- parentheses required du to LIMIT 1
SELECT a, b
FROM tbl
WHERE (a, b) > ($previous_a, $previous_b) -- !!!
ORDER BY a, b
LIMIT 1
)
UNION ALL
SELECT x.a, x.b
FROM cte c
CROSS JOIN LATERAL (
SELECT t.a, t.b
FROM tbl t
WHERE (t.a, t.b) > (c.a, c.b) -- lateral reference
ORDER BY t.a, t.b
LIMIT 1
) x
)
TABLE cte
LIMIT 1000;
这可以充分利用您的索引,并且应该尽可能快。
进一步阅读:
Optimize GROUP BY query to retrieve latest row per user对于重复使用并且表上没有或很少写入负载,请考虑基于上述查询之一的MATERIALIZED VIEW
- 以获得更快的读取性能。
【讨论】:
【参考方案2】:我不能保证 Postgres 的性能,但这是我在类似情况下在 sql server 上使用过的一种技术,并且被证明比其他技术更快:
将不同的 A 变成一个临时的 a
将不同的 B 转换为临时 b
将 a 和 b 时间跨过笛卡尔坐标系到一个 temp abALL
对 abALL 进行排名(可选)
创建一个视图 myview 作为 select top 1 a,b from tbl (your_main_table)
将 temp abALL 与 myview 加入 temp abCLEAN
如果您的排名没有超过,请在此处排名 abCLEAN
【讨论】:
以上是关于从大表中有效地选择不同的(a,b)的主要内容,如果未能解决你的问题,请参考以下文章