【问题标题】:Postgres indexes for inner join with where clause on both tables两个表上带有 where 子句的内部连接的 Postgres 索引
【发布时间】:2020-10-09 17:31:37
【问题描述】:

假设我们的数据库具有以下架构,我们将 Postgres 用于 RBAC 权限模型和组,并尝试考虑所需的最佳索引:

科目表

id, external_type, external_id, group_id

资源表

id, group_id, external_type, external_id, role_id

两张表都旨在回答一个问题:

主体 [S] 能否对 资源 [R]

执行动作 [A]

因此,我们需要从用户参与的所有权限组中检索用户在资源上的所有角色。

  • 主题可以在主题表中拥有 K 行,他所在的每个组对应一个。
  • 一个资源在资源表中可以有 M 行,每个组对应一个为该资源分配的角色。
  • 我们选择对一张表中的资源、组和角色进行非规范化处理,以优化读取性能。
  • 我们选择不对同一张表中的主题进行非规范化 - 以避免在每次组结构更改时更新许多记录。
  • K 和 M 都可以很大 - 一个用户可以属于多个组,而一个资源可以属于多个组。

所以查询将是:

SELECT role_id
FROM resources
INNER JOIN subjects
ON resources.group_id=subjects.group_id
WHERE subjects.external_type="user" AND subjects.external_id=123
  AND resources.external_type="order" AND resources.external_id=456

我们决定定义以下索引:

Subjects: <external_type, external_id>, <group_id>
Resources: <external_type, external_id>, <group_id>

有人可以解释一下与where 子句的内部连接如何与2 个表使用索引相关吗?它们是并行执行,然后通过ON 语句连接,还是只有一个表的索引将用于where,然后使用ON 连接?

我们应该使用不同的复合索引吗?以某种方式将group_id 添加到复合索引中?

任何对类似用例的引用或为复杂的JOIN 查询选择索引都会有所帮助。

【问题讨论】:

  • 您的索引没问题。该查询将在 范围索引扫描 上使用 嵌套循环,然后在另一个表的索引上使用 索引搜索。在这种情况下,我认为哪个表是主要的还是次要的并不重要。现在:1)查询慢吗? 2) 查询返回多少行?这可能会影响堆读取,但应该不错。
  • 这是真实的、真实的查询吗?如果是这样,您可以将role_id 添加到索引中以获得额外的性能。这可能会略微提高性能。
  • WHERE 条件对各个查询的选择性如何,即会通过多少行?实际查询将产生多少行?我要求估价。
  • @TheImpaler 系统仍在开发中。 1) 我们正在努力创建一个测试用例并在上面运行EXPLAIN。 2)尽管 where 子句的每个部分可能有很多匹配项(在每个表上匹配 ),但交集应该返回很少的结果 - 也回答你的问题@Laurenz Albe

标签: sql postgresql indexing left-join inner-join


【解决方案1】:

这可以通过多种方式完成。它可以独立地从索引和表中读取,然后将它们散列连接或合并连接在一起。或者它可以忽略一个或两个索引,而是对表进行 seq 扫描,如果它认为这样会更快(因为索引将返回大部分行)。或者它可以做一个嵌套循环,它使用 WHERE 子句中的常量加上 ON 子句中不断变化的 group_id 来形成一个三元组,它将在其中一个表(内表)的三列索引中查找。外部表也可以由其索引驱动(仅使用前两列,在查询期间保持不变)或使用顺序扫描。

如果您想知道正在使用什么计划,请执行EXPLAIN 或更好的EXPLAIN (ANALYZE, BUFFERS) 查询。

【讨论】:

  • 感谢您的详尽解释。我们正在努力创建一个包含许多记录的测试用例,然后我们将运行EXPLAIN。只是为了验证 - 你会推荐所提供的索引:主题:&lt;external_type, external_id&gt;&lt;group_id&gt;,资源:&lt;external_type, external_id&gt;&lt;group_id&gt;,或者:主题:&lt;external_type, external_id, group_id&gt;,资源:&lt;external_type, external_id, group_id&gt;?跨度>
  • @Eliranf,你的替代品在我看来是一样的——我看不出有什么不同。我会在资源表索引的末尾添加 role_id,以期获得仅索引扫描。
  • 非常感谢@jjanes,不同之处在于您是指每个表有一个包含所有 3 列的复合索引,还是每个表有 2 个不同的索引 - 的一个复合和一个单列 group_id只有
  • 我明白了。我在您的评论和问题中都忽略了这一点。根据您显示的一个查询,我在 group_id 的单列索引中看不到任何值。 Group_id 应该添加到一个表或另一个表或两者的复合索引中。我的回答是基于错误的想法,即它已经是多列索引的一部分,而不仅仅是单列索引。但是使用 EXPLAIN 来查看 PostgreSQL 计划做什么仍然是一个好主意。
猜你喜欢
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
  • 1970-01-01
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
  • 2016-08-17
相关资源
最近更新 更多