sql - Postgres : how can a "select count(*)" cause dirty blocks?

标签 sql postgresql caching

在尝试优化 Postgres 11 中的某些查询时,我偶然发现了这种我无法理解的行为。

> explain (analyze, buffers) select count(*) from events;
                                                                    QUERY PLAN                                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=2533599.69..2533599.70 rows=1 width=8) (actual time=113869.828..113869.828 rows=1 loops=1)
   Buffers: shared hit=204077 read=2205033 dirtied=16112
   I/O Timings: read=319766.985
   ->  Gather  (cost=2533599.48..2533599.69 rows=2 width=8) (actual time=113869.814..113871.340 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=204077 read=2205033 dirtied=16112
         I/O Timings: read=319766.985
         ->  Partial Aggregate  (cost=2532599.48..2532599.49 rows=1 width=8) (actual time=113866.031..113866.032 rows=1 loops=3)
               Buffers: shared hit=204077 read=2205033 dirtied=16112
               I/O Timings: read=319766.985
               ->  Parallel Seq Scan on events  (cost=0.00..2507901.58 rows=9879158 width=0) (actual time=0.048..111664.011 rows=8055167 loops=3)
                     Buffers: shared hit=204077 read=2205033 dirtied=16112
                     I/O Timings: read=319766.985
 Planning Time: 0.142 ms
 Execution Time: 113871.415 ms

我的理解是我的 select count(*) 弄脏了 16112 个 block 。

两个问题:

  • 我的理解正确吗?
  • 只读操作如何使 block 变脏?

最佳答案

为了回答这个问题,我必须解释一些有关 PostgreSQL 内部结构的内容。

在 PostgreSQL 中,行永远不会就地更新。相反,每个 UPDATE 都会创建该行的一个新版本。同样,DELETE 不会删除该行,而是将其标记为无效。

每个行版本都带有创建它的事务的 ID 以及将其标记为无效的事务的 ID。这些事务 ID 共同决定行版本的可见性

COMMITROLLBACK 根本不接触表,它们仅在提交日志中标记事务已提交或中止。 p>

现在,读取行版本的查询必须查阅提交日志以确定它是否可以看到行版本。例如,如果创建行版本的事务被回滚,则行版本将不可见。

你可以想象,如果没有适当的优化,这会在提交日志上产生大量流量,这会损害性能:如果访问行的语句发现创建或删除事务已结束,它将在行版本上设置所谓的提示位。后续读者现在不必再查阅提交日志。

您的查询是第一个读取表中某些行的查询,因此它设置了这些提示位。此修改使得包含行版本的 block “脏”,即必须将其写入存储。

这解释了读取查询如何最终在 PostgreSQL 中写入数据。因此,在批量数据修改后VACUUM表通常是一个好主意:它将承担第一个读取器设置提示位的责任。

关于sql - Postgres : how can a "select count(*)" cause dirty blocks?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59716940/

相关文章:

c# - 应用程序中的服务器错误 '/demo'

SQL - 多个条件 where 子句同一列

ruby-on-rails - 处理完请求后杀死 puma 线程

performance - PostgreSQL查询速度是可变的

sql - 如何在 PostgreSQL 中高效查询版本化行/实体?

php - 在 php 5.2 中使用 foreach 的 mysql 结果不起作用,但在 php 5.4 中有效

sql - EF 4.3 迁移重命名列而不是删除它

android - Android WebView 缓存目录中的文件格式是什么(data_1、f_000001 等)?

javascript - 如何在 IE 中调试 "Back Navigation Caching"?

javascript - HTML - 如何检查文件(.js 或 .css)是否已加载或从缓存中获取?