sql - 加入倾斜关系时,有没有办法改进 PostgreSQL 估计?

标签 sql postgresql

我目前正在使用存储移动设备的 PostgreSQL 12 数据库。其中,它有两个表 device_typesdevice_groups .数据分布使得大多数device_groups很少device_types在他们中,例如“2020 年发布的 iOS 设备”大约有 10 个 device_types , 而少数 device_groups有非常多的device_types ,例如“现有的所有 Android 型号”,大约有 80'000 device_types .
这很麻烦,因为 PostgreSQL 查询规划器会做出错误的估计,因此在使用 device_group 时会在查询中稍后选择次优连接类型。 “现有的所有 Android 机型”。根据其统计,它预计只有极少数 device_types平均而言,但实际上在某些情况下必须处理 80'000 个设备的中间结果集。
我在下面创建了一个最小的复制案例。
这是数据模型:

CREATE TABLE device_groups (
    id              integer CONSTRAINT device_groups_pk PRIMARY KEY
);

CREATE TABLE device_types (
    id              integer CONSTRAINT device_types_pk PRIMARY KEY,
    device_group_id integer CONSTRAINT device_types_fk_device_groups_id REFERENCES device_groups(id)
);
现在我们插入一些倾斜的数据。大多数设备组有 ~10 device_types ,一个有80k device_types :
/* create 100 device groups */
INSERT INTO device_groups(id)
SELECT i
FROM generate_series(1, 100) as t(i);

/* 99 device groups have only a few entries, 10 on average: */
INSERT INTO device_types(id, device_group_id)
SELECT i, trunc(random() * 98 + 1)
FROM generate_series(1, 1000) as t(i);

/* device group 100 has 80k devices: */
INSERT INTO device_types(id, device_group_id)
SELECT i, 100
FROM generate_series(1001, 81001) as t(i);

ANALYZE device_groups;
ANALYZE device_types;
不出所料,这个查询运行良好,因为 Postgres 拥有估计结果基数所需的所有统计信息:
EXPLAIN ANALYZE
SELECT *
FROM device_types
WHERE device_types.device_group_id = 100;
/* => correctly estimates ~80k rows: */
/* Seq Scan on device_types  (cost=0.00..1371.51 rows=79932 width=8) (actual time=0.104..665.699 rows=80001 loops=1) */
这个查询(对我来说有点出乎意料)对于它将返回的行数也有正确的估计:
EXPLAIN ANALYZE
SELECT *
FROM device_types
JOIN device_groups ON device_types.device_group_id = device_groups.id
WHERE device_groups.id = 100;
/* => also correctly estimates ~80k rows: */
/* Nested Loop  (cost=0.00..2173.08 rows=79932 width=12) (actual time=0.165..1930.591 rows=80001 loops=1) */
现在我们添加一个额外的间接层,通过添加第三个表 device_group_collections我们加入反对,这就是问题所在:
CREATE TABLE device_group_collections (
    device_group_id integer CONSTRAINT device_group_collections_fk_device_groups_id REFERENCES device_groups(id),
    collection_id   integer
);

INSERT INTO device_group_collections(device_group_id, collection_id) VALUES (100, 25);

ANALYZE device_group_collections;

EXPLAIN ANALYZE
SELECT *
FROM device_types
JOIN device_groups ON device_types.device_group_id = device_groups.id
JOIN device_group_collections ON device_group_collections.device_group_id = device_groups.id
WHERE device_group_collections.collection_id = 25;
/* => estimates only 810 rows, but in reality there will be 80'001 rows: */
/* Hash Join  (cost=3.42..1484.29 rows=810 width=20) (actual time=19.817..1949.068 rows=80001 loops=1) */
这是 Postgres 查询规划器的一般限制,还是有什么方法可以帮助它理解数据集的偏斜?

最佳答案

这是计划者的限制。
准确估计您的中间查询只是因为规划器意识到 ON device_types.device_group_id = device_groups.id WHERE device_groups.id = 100意味着 device_types.device_group_id 也必须等于 100,它可以在 device_types.device_group_id 上查找该值的统计信息。
如果您的 device_groups 表也有一个“名称”列并且查询指定了 ON device_types.device_group_id = device_groups.id WHERE device_groups.name = 'name for 100' ,那么就是 不是 被准确估计。
我不知道这个问题的任何通用解决方案。

关于sql - 加入倾斜关系时,有没有办法改进 PostgreSQL 估计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65861780/

相关文章:

sql - 如何在 where 子句中包含列 USER_VIEWS.TEXT

javascript - Sequelize : Modify output of date timestamp using Getters

sql - 如何根据计数进行更新 - SQL (postgres)

java - 需要帮助查找 Oracle 异常.. Java+Oracle+Prepared Statement

SQL 数据库规范化和外键实践(可变/空白键?)

mysql - 如何从值为最小值或最大值的一对多关系中仅连接一行?

postgresql -/var/lib/pgsql目录权限错误

mysql - 如何选择两列最大值的位置

sql - 尝试从两个 PostgreSQL 数据库导入数据库时​​出现语法错误

python - 在 Python 中过滤 SQL 语句以防止恶意注入(inject)