Postgres 仅索引扫描:我们可以忽略可见性映射或避免堆获取吗?
Postgres Index-only-scan: can we ignore the visibility map or avoid heap fetches?
抱歉,在实际问题之前有很多上下文,因为我们已经对此进行了彻底的研究,我想为您提供完整的上下文。
某些上下文:postgres 仅索引扫描依赖于可见性映射 (VM)。如果页面在可见性映射中未标记为不完全可见,postgres 会获取该页面以确保数据对该事务可见,即使在仅执行索引扫描时也是如此。不幸的是,这会大大减慢仅索引扫描的速度。索引可能 return 来自 10k 行,但索引本身仅跨越 50 页(在 IO 方面非常快)。但是,如果未设置 VM,它会进行额外的 10k 堆提取(在 IO 方面慢 200 倍)。
详情:https://wiki.postgresql.org/wiki/Index-only_scans#The_Visibility_Map_.28and_other_relation_forks.29
自己尝试一下:在 VACUUM 之前和之后解释仅索引查询的分析。您可以看到在 VACUUM 之后堆获取的数量下降(假设您之前在 VM 中有一些脏页)
已尝试:我们已经调整了 autovacuum,并且我们正在定期清理。这很有帮助,但我们希望更快。
问题(最后):是否可以在进行仅索引扫描时跳过堆提取?我知道我们在阅读时不会有完美的 MVCC,但我们对此没有意见。索引中的数据足够接近,绝对不值得花费数千次堆提取来确保我们查看的不是稍微陈旧的数据。借用 NoSQL 的一个术语,我们可以接受 "eventual consistency" 次读取。
谢谢!
目前无法在 PostgreSQL 中做您想做的事。这样做会很有趣,但需要做一些工作,不太可能被核心接受,很难进行扩展,并且可能会产生比您预期的更糟糕的副作用。
你基本上必须向 PostgreSQL 添加一个 DIRTY READ
隔离级别,除了它会比那个更弱,因为它也可能 return 删除数据、更新行的旧版本,以及来自唯一索引的多个值。后一个问题可能会使查询计划器非常不安,因为允许假设唯一索引的结果是唯一的。
我认为这样的更改被 PostgreSQL 核心接受的可能性非常接近于零。可能的用例非常有限。
我认为证明添加此类功能合理的唯一方法是通过支持原始读取来更轻松地从堵塞 loss/corruption 和意外删除中恢复。这对于堆的序列扫描而不是仅索引扫描最有意义。
用另一种方式解决这个问题可能更有意义,例如在数据库顶部为不需要完全新鲜的数据设置一个非持久缓存层(Redis 等)。
抱歉,在实际问题之前有很多上下文,因为我们已经对此进行了彻底的研究,我想为您提供完整的上下文。
某些上下文:postgres 仅索引扫描依赖于可见性映射 (VM)。如果页面在可见性映射中未标记为不完全可见,postgres 会获取该页面以确保数据对该事务可见,即使在仅执行索引扫描时也是如此。不幸的是,这会大大减慢仅索引扫描的速度。索引可能 return 来自 10k 行,但索引本身仅跨越 50 页(在 IO 方面非常快)。但是,如果未设置 VM,它会进行额外的 10k 堆提取(在 IO 方面慢 200 倍)。
详情:https://wiki.postgresql.org/wiki/Index-only_scans#The_Visibility_Map_.28and_other_relation_forks.29
自己尝试一下:在 VACUUM 之前和之后解释仅索引查询的分析。您可以看到在 VACUUM 之后堆获取的数量下降(假设您之前在 VM 中有一些脏页)
已尝试:我们已经调整了 autovacuum,并且我们正在定期清理。这很有帮助,但我们希望更快。
问题(最后):是否可以在进行仅索引扫描时跳过堆提取?我知道我们在阅读时不会有完美的 MVCC,但我们对此没有意见。索引中的数据足够接近,绝对不值得花费数千次堆提取来确保我们查看的不是稍微陈旧的数据。借用 NoSQL 的一个术语,我们可以接受 "eventual consistency" 次读取。
谢谢!
目前无法在 PostgreSQL 中做您想做的事。这样做会很有趣,但需要做一些工作,不太可能被核心接受,很难进行扩展,并且可能会产生比您预期的更糟糕的副作用。
你基本上必须向 PostgreSQL 添加一个 DIRTY READ
隔离级别,除了它会比那个更弱,因为它也可能 return 删除数据、更新行的旧版本,以及来自唯一索引的多个值。后一个问题可能会使查询计划器非常不安,因为允许假设唯一索引的结果是唯一的。
我认为这样的更改被 PostgreSQL 核心接受的可能性非常接近于零。可能的用例非常有限。
我认为证明添加此类功能合理的唯一方法是通过支持原始读取来更轻松地从堵塞 loss/corruption 和意外删除中恢复。这对于堆的序列扫描而不是仅索引扫描最有意义。
用另一种方式解决这个问题可能更有意义,例如在数据库顶部为不需要完全新鲜的数据设置一个非持久缓存层(Redis 等)。