同时刷新物化视图会导致表膨胀

2023-12-29

在 PostgreSQL 9.5 中,我决定创建一个物化视图“effects”并安排每小时并发刷新,因为我希望它始终可用:

REFRESH MATERIALIZED VIEW CONCURRENTLY effects;

一开始一切都很顺利,我的物化视图令人耳目一新,磁盘空间使用情况或多或少保持不变。


问题

一段时间后,磁盘使用率开始下降linearly grow.

我得出的结论是,这种增长的原因是物化视图,并从以下位置运行查询this https://dba.stackexchange.com/a/23933/157163回答得到以下结果:

               what                |  bytes/ct   | bytes_pretty | bytes_per_row
-----------------------------------+-------------+--------------+---------------
 core_relation_size                | 32224567296 | 30 GB        |         21140
 visibility_map                    |      991232 | 968 kB       |             0
 free_space_map                    |     7938048 | 7752 kB      |             5
 table_size_incl_toast             | 32233504768 | 30 GB        |         21146
 indexes_size                      | 22975922176 | 21 GB        |         15073
 total_size_incl_toast_and_indexes | 55209426944 | 51 GB        |         36220
 live_rows_in_text_representation  |   316152215 | 302 MB       |           207
 ------------------------------    |             |              |
 row_count                         |     1524278 |              |
 live_tuples                       |      676439 |              |
 dead_tuples                       |     1524208 |              |
(11 rows)

然后,我发现该表上次自动清理是在两天前,通过运行:

SELECT relname, n_dead_tup, last_vacuum, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup desc;

我决定手动调用vacuum (VERBOSE) effects。它运行了大约半个小时并产生了以下输出:

vacuum (VERBOSE) effects;
INFO:  vacuuming "public.effects"
INFO:  scanned index "effects_idx" to remove 129523454 row versions
DETAIL:  CPU 12.16s/55.76u sec elapsed 119.87 sec

INFO:  scanned index "effects_campaign_created_idx" to remove 129523454 row versions
DETAIL:  CPU 19.11s/154.59u sec elapsed 337.91 sec

INFO:  scanned index "effects_campaign_name_idx" to remove 129523454 row versions
DETAIL:  CPU 28.51s/151.16u sec elapsed 315.51 sec

INFO:  scanned index "effects_campaign_event_type_idx" to remove 129523454 row versions
DETAIL:  CPU 38.60s/373.59u sec elapsed 601.73 sec

INFO:  "effects": removed 129523454 row versions in 3865537 pages
DETAIL:  CPU 59.02s/36.48u sec elapsed 326.43 sec

INFO:  index "effects_idx" now contains 1524208 row versions in 472258 pages
DETAIL:  113679000 index row versions were removed.
463896 index pages have been deleted, 60386 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.01 sec.

INFO:  index "effects_campaign_created_idx" now contains 1524208 row versions in 664910 pages
DETAIL:  121637488 index row versions were removed.
41014 index pages have been deleted, 0 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.

INFO:  index "effects_campaign_name_idx" now contains 1524208 row versions in 711391 pages
DETAIL:  125650677 index row versions were removed.
696221 index pages have been deleted, 28150 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.

INFO:  index "effects_campaign_event_type_idx" now contains 1524208 row versions in 956018 pages
DETAIL:  127659042 index row versions were removed.
934288 index pages have been deleted, 32105 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.

INFO:  "effects": found 0 removable, 493 nonremovable row versions in 3880239 out of 3933663 pages
DETAIL:  0 dead row versions cannot be removed yet.

There were 666922 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 180.49s/788.60u sec elapsed 1799.42 sec.

INFO:  vacuuming "pg_toast.pg_toast_1371723"
INFO:  index "pg_toast_1371723_index" now contains 0 row versions in 1 pages
DETAIL:  0 index row versions were removed.
0 index pages have been deleted, 0 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.

INFO:  "pg_toast_1371723": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet.
There were 0 unused item pointers.
Skipped 0 pages due to buffer pins.
0 pages are entirely empty.
CPU 0.00s/0.00u sec elapsed 0.00 sec.

VACUUM

此时我认为问题已经解决并开始思考什么会干扰自动真空。可以肯定的是,我再次运行查询来查找该表的空间使用情况,令我惊讶的是它没有改变。

直到我打电话之后REFRESH MATERIALIZED VIEW effects; 不同时。现在检查表大小的查询的输出是:

               what                | bytes/ct  | bytes_pretty | bytes_per_row
-----------------------------------+-----------+--------------+---------------
 core_relation_size                | 374005760 | 357 MB       |           245
 visibility_map                    |         0 | 0 bytes      |             0
 free_space_map                    |         0 | 0 bytes      |             0
 table_size_incl_toast             | 374013952 | 357 MB       |           245
 indexes_size                      | 213843968 | 204 MB       |           140
 total_size_incl_toast_and_indexes | 587857920 | 561 MB       |           385
 live_rows_in_text_representation  | 316175512 | 302 MB       |           207
 ------------------------------    |           |              |
 row_count                         |   1524385 |              |
 live_tuples                       |    676439 |              |
 dead_tuples                       |   1524208 |              |
(11 rows)

然后一切又恢复正常了……


问题

问题已经解决了,但是还是有很多困惑

  1. 谁能解释一下我遇到的问题是什么?
  2. 我以后怎样才能避免这种情况呢?

首先我们来解释一下膨胀

REFRESH MATERIALIZED CONCURRENTLY实施于src/backend/commands/matview.c https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/commands/matview.c;h=e1eb7c374b833402c20255db384529dc0b7cee34;hb=09e99ce86e3dfb4716618b1dda4074b45ba56a09#l547,评论很有启发性:

/*
 * refresh_by_match_merge
 *
 * Refresh a materialized view with transactional semantics, while allowing
 * concurrent reads.
 *
 * This is called after a new version of the data has been created in a
 * temporary table.  It performs a full outer join against the old version of
 * the data, producing "diff" results.  This join cannot work if there are any
 * duplicated rows in either the old or new versions, in the sense that every
 * column would compare as equal between the two rows.  It does work correctly
 * in the face of rows which have at least one NULL value, with all non-NULL
 * columns equal.  The behavior of NULLs on equality tests and on UNIQUE
 * indexes turns out to be quite convenient here; the tests we need to make
 * are consistent with default behavior.  If there is at least one UNIQUE
 * index on the materialized view, we have exactly the guarantee we need.
 *
 * The temporary table used to hold the diff results contains just the TID of
 * the old record (if matched) and the ROW from the new table as a single
 * column of complex record type (if matched).
 *
 * Once we have the diff table, we perform set-based DELETE and INSERT
 * operations against the materialized view, and discard both temporary
 * tables.
 *
 * Everything from the generation of the new data to applying the differences
 * takes place under cover of an ExclusiveLock, since it seems as though we
 * would want to prohibit not only concurrent REFRESH operations, but also
 * incremental maintenance.  It also doesn't seem reasonable or safe to allow
 * SELECT FOR UPDATE or SELECT FOR SHARE on rows being updated or deleted by
 * this command.
 */

因此,通过删除行并从临时表中插入新行来刷新物化视图。这当然会导致死元组和表膨胀,这已由您证实VACUUM (VERBOSE) output.

在某种程度上,这就是你付出的代价CONCURRENTLY.

其次,让我们来揭穿这个神话:VACUUM无法删除死元组

VACUUM将删除死行,但它不能减少膨胀(可以通过VACUUM (FULL),但这会锁定视图,就像REFRESH MATERIALIZED VIEW没有CONCURRENTLY).

我怀疑您用来确定死元组数量的查询只是一个估计,导致死元组数量错误。

一个例子可以证明这一切

CREATE TABLE tab AS SELECT id, 'row ' || id AS val FROM generate_series(1, 100000) AS id;

-- make sure autovacuum doesn't spoil our demonstration
CREATE MATERIALIZED VIEW tab_v WITH (autovacuum_enabled = off)
AS SELECT * FROM tab;

-- required for CONCURRENTLY
CREATE UNIQUE INDEX ON tab_v (id);

Use the pgstattuple精确测量表膨胀的扩展:

CREATE EXTENSION pgstattuple;

SELECT * FROM pgstattuple('tab_v');
-[ RECORD 1 ]------+--------
table_len          | 4431872
tuple_count        | 100000
tuple_len          | 3788895
tuple_percent      | 85.49
dead_tuple_count   | 0
dead_tuple_len     | 0
dead_tuple_percent | 0
free_space         | 16724
free_percent       | 0.38

现在让我们删除表中的一些行,刷新并再次测量:

DELETE FROM tab WHERE id BETWEEN 40001 AND 80000;

REFRESH MATERIALIZED VIEW CONCURRENTLY tab_v;

SELECT * FROM pgstattuple('tab_v');
-[ RECORD 1 ]------+--------
table_len          | 4431872
tuple_count        | 60000
tuple_len          | 2268895
tuple_percent      | 51.19
dead_tuple_count   | 40000
dead_tuple_len     | 1520000
dead_tuple_percent | 34.3
free_space         | 16724
free_percent       | 0.38

很多死元组。VACUUM摆脱这些:

VACUUM tab_v;

SELECT * FROM pgstattuple('tab_v');
-[ RECORD 1 ]------+--------
table_len          | 4431872
tuple_count        | 60000
tuple_len          | 2268895
tuple_percent      | 51.19
dead_tuple_count   | 0
dead_tuple_len     | 0
dead_tuple_percent | 0
free_space         | 1616724
free_percent       | 36.48

死的元组消失了,但现在有很多空闲空间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

同时刷新物化视图会导致表膨胀 的相关文章

  • 具有 JPA、PostgreSQL 和 NULL 值的 JodaTime

    我试图将 JPA 的 JodaTime DateTime 字段保留到 PostgreSQL 但遇到了指向数据库 NULL 值的空指针的问题 我正在使用 NetBeans 7 beta 2 IDE 持久性实现是 EclipseLink 2 2
  • Heroku 上的 Python 入门 - 未找到 pg_config 可执行文件

    我一直在关注文档 直到安装requirements txt 文件 尝试安装第 6 行 psycopg2 2 5 3 时总是失败 这是消息 Downloading unpacking psycopg2 2 5 3 from r require
  • 将 PostgreSQL 中的 IP 地址转换为整数?

    有没有一个查询可以完成这个任务 例如 给定一个条目 216 55 82 34 我想用 分割字符串 并应用等式 IP 号 16777216 w 65536 x 256 y z 其中 IP 地址 w x y z 仅通过查询就可以实现这一点吗 您
  • 在PostgreSQL中使用查询设置列类型

    优秀后answer https stackoverflow com questions 4336259 how to query the schema details of a table in postgres作者 Alexandre G
  • 如何在 postgreSQL 中从时间戳中减去/添加分钟

    我有以下场景 我有员工登记他们的上班 下班手续 但他们有10分钟的容忍度 我通过这种观点得到的最新条目 CREATE OR REPLACE VIEW employees late entries id created datetime en
  • 寻找免费的 GUI 工具来使用 PostgreSQL [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 目前 我正在使用独立的 GUI 工具DbSchema http www dbschema com 设计
  • 如何从主机连接到 Docker Postgres 容器

    我按照以下说明搭建了一个 Rails 开发环境https docs docker com compose rails https docs docker com compose rails 它可以工作 但我无法从主机连接到 Postgres
  • 多个连接到同一个表

    我有这组表格和数据 CREATE TABLE item id INT PRIMARY KEY name VARCHAR CREATE TABLE property id INT PRIMARY KEY name VARCHAR CREATE
  • PostgreSQL 如何创建数据库或模式的副本?

    有没有一种简单的方法可以在 PostgreSQL 8 1 中创建数据库或模式的副本 我正在测试一些软件 它对数据库中的特定模式进行大量更新 我想复制它 以便我可以与原始版本进行一些比较 如果它位于同一服务器上 则只需使用带有 TEMPLAT
  • PostgreSQL:有效地将 JSON 数组拆分为行

    我有一个表 表 A 其中包含一个包含 JSON 编码数据的文本列 JSON 数据始终是一个包含一到几千个普通对象的数组 我有另一个表 表 B 其中有几列 包括数据类型为 JSON 的列 我想从表 A 中选择所有行 将 json 数组拆分为其
  • 我可以以编程方式配置 PostgreSQL 以不消除全文搜索中的停用词吗?

    我正在使用 PostgreSQL 全文搜索来进行项目 其中传统停用词 a the if 等 应该被索引和可搜索 这不是默认行为 例如 我可能希望我的用户找到查询 to be or not to be 的结果 The 文档 http www
  • Postgresql 的 SQL_NO_CACHE?

    MySQL 关键字是否有等效的 postgresqlSQL NO CACHE 或 SQL Serverdbcc drop clean buffers 即您可以简单地将其包含在 SQL 语句中或作为脚本的一部分吗 UPDATE 这个问题 查看
  • 手动更改postgresql中查询的执行计划?

    是否可以在postgresql中手动更改执行计划的操作顺序 例如 如果我总是想在过滤之前进行排序操作 尽管这在 postgresql 的正常使用中没有意义 是否可以通过例如手动强制执行该操作改变运营的内部成本 如果我实现自己的功能呢 是否可
  • Postgresql 串行错误自动增量

    我在 postgresql 上遇到问题 我认为 postgresql 中有一个错误 我错误地实现了一些东西 有一个表包括colmn1 primary key colmn2 unique colmn3 插入一行后 如果我尝试使用现有的另一次插
  • PostgreSQL - 根据另一个单元格值设置默认单元格值

    如果我有一个专栏说column a任何给定值 我想要另一列column b有一个default value根据 的值column a 换句话说 if column a peter then column b default value do
  • 如何在 PostgreSQL 中使用条件和子查询创建唯一索引?

    我使用 PGSQL 并尝试添加下面的索引 CREATE UNIQUE INDEX fk client ON user client fk client WHERE fk client NOT IN SELECT fk client FROM
  • Postgres:显示继承的字段

    我应该实现什么查询来获取继承的列 读过this http www alberton info postgresql meta info html综合帖子没有找到解决办法 如果我理解正确的话 您想知道作为表之间继承的一部分的列的名称 SELE
  • 最近邻居的 Postgis SQL

    我正在尝试计算最近的邻居 为此 我需要传递一个参数来限制与邻居的最大距离 例如 半径1000米内最近的邻居是哪些 我做了以下事情 我用数据创建了表 id name latitude longitude 之后 我执行了以下查询 SELECT
  • Django 中从 sqlite 迁移到 postgresql

    我想迁移自sqlite to PostgreSQL db 我安装了 postgresql 并在其 shell 上创建数据库 然后配置我的 django 设置如下 default ENGINE django db backends postg
  • postgreSQL 在 WAMP 上的集成

    我刚刚在 Windows 7 上安装了 postgreSQL 我正在尝试将 postgreSQL 与 WAMP 服务器集成 为此 我在 httpd conf 和 php ini 文件中进行了以下更改 1个加载模块c path to libp

随机推荐

  • 如何在 HTML 工具提示中使用回车符?

    我目前正在向我们的网站添加详细的工具提示 并且我希望 无需求助于出色的 jQuery 插件 我知道有很多 使用回车符来格式化工具提示 要添加提示 我正在使用title属性 我浏览了常用网站并使用了以下基本模板 a title link wi
  • c# 在html中查找图像并下载它们

    我想下载存储在 html 网页 中的所有图像 我不知道将下载多少图像 并且我不想使用 HTML AGILITY PACK 我在谷歌中搜索 但所有网站都让我更加困惑 我尝试了正则表达式 但只有一个结果 人们给了你正确的答案 你也不能挑剔和懒惰
  • dplyr 查找表/模式匹配[重复]

    这个问题在这里已经有答案了 我一直在寻找一种聪明的或 更整洁 的方式来利用 tidyverse 中的查找表 但找不到令人满意的解决方案 我有一个数据集和查找表 Sample data data lt data frame patients
  • Azure 数据工厂通过访问密钥连接到 Blob 存储

    我正在尝试在 Azure 数据工厂中构建一个非常基本的数据流 从 blob 存储中提取 JSON 文件 对某些列执行转换 然后存储在 SQL 数据库中 我最初使用托管身份对存储帐户进行身份验证 但在尝试测试与源的连接时收到以下错误 com
  • 使用 Laravel 5.2 的内置身份验证将旧的 md5 密码迁移到 bcrypt

    我正在将旧的 PHP 应用程序迁移到 Laravel 5 2 该应用程序有一个巨大的用户表 大约 50K 用户 密码都是 MD5 哈希值 显然这是不可接受的 但我不想向所有 50 000 个用户发送电子邮件要求他们重置密码 而是想在幕后将密
  • 尽管有定义,但我有一个编译器错误“未定义”

    from gasp import GRID SIZE 30 MARGIN GRID SIZE BACKGROUND COLOR color BLACK Colors we use WALL COLOR 0 6 255 0 9 255 0 9
  • Gremlin 掉落多个顶点

    我正在尝试删除给定 Gremlin 查询返回的所有顶点 目标是删除作为特定顶点的子级的所有子顶点 这是一个例子 gremlin gt g V dcb26be6 8d39 ae81 6ef2 6f60d06bce10 emit repeat
  • 非轮询/非阻塞定时器?

    到目前为止我发现的最好的解决方案就是使用sleep 功能 我想在计时器到期事件发生时运行我自己的回调函数 有没有什么事件驱动的方法可以解决这个问题 from time import sleep Sleep for a minute time
  • 在 Haskell 中创建除数列表而不按顺序除法

    我正在哈斯克尔学习 我一直在实现一个制作除数列表的函数 我的第一个代码在这里 Code divisors Integral a gt a gt a divisors n n lt 1 otherwise filter 0 mod n 1 n
  • JavaScript 正则表达式 - 正向前瞻 - 给我语法错误

    这段正则表达式 lt href 应该匹配 href 值中的所有内容 除了哈希值及其在 href url 中跟随的内容 它似乎在正则表达式调试器 测试器下工作正常 例如http gskinner com RegExr http gskinne
  • @SelectProvider中mybatis参数替换是如何工作的

    我继承了一些我试图理解的代码以及我为找到一些东西所做的任何搜索 SelectProvider结果什么也没有 Java DAO SelectProvider type CategoryDaoSelectProvider class metho
  • 使用 Azure 部署应用程序时出现“未知生命周期阶段“mvn”。您必须指定有效的生命周期阶段或目标”

    我正在尝试使用 Azure Devops 在云中心部署 mule 应用程序 在尝试部署应用程序时 我正在运行一个作业 其中有一个 Maven 任务 该任务假设运行 Maven 命令 mvn 包部署 DmuleDeploy Dcloud en
  • Pygame:key.get_pressed() 与事件队列不一致

    我正在尝试使用 Python 中的 pygame 为应用程序制定简单的控件 我已经掌握了基础知识 但我遇到了奇怪的困难 我使用箭头键来控制我的角色 如果我按住一个箭头键 然后按住另一个箭头键 对角线移动 角色将按预期移动 但是 如果我释放s
  • 使用 React Router v4 的 IIS 规则

    我正在尝试使用 BrowserRouter 让我的路由与 IIS 和 React Router v4 一起使用 在我的本地主机上 我的所有路由都按预期工作 React Router 按照我想要的方式处理一切 http www example
  • 在容器中使用 new 放置

    我刚刚遇到了一些 C 容器实现 该类使用内部缓冲区来管理其对象 这是一个没有安全检查的简化版本 template
  • 不同语言中的数组 - 存储引用还是原始对象?

    当使用数组时 我试图理解不同语言中原始内存的样子 考虑以下 Java 代码 String a hi String b there String c everyone String array a b c 显然数组持有参考 而不是对象 也就是
  • 在 angularjs 中转换 $.param

    在我使用 JQuery 之前 我使用它来发送带有参数的 URL window location myUrl param paramName ok anotherParam hello 但对于 angularjS 来说 这并不以同样的方式工作
  • 如何将 JavaScript 代码转换为其等效的 PHP 代码?

    我有js代码 var b aHR0cDovL3d3dy5oZHpvZy5jb20vZ2V0X2ZpbGUv S84Y2 5 T 4Zm yNmVkNTk0ZmI5Yzc2ZWI2Y2Y2YWVm 85ND w 85NDU4Ny85NDU4N
  • 迭代 Capybara 中的项目

    我有一个包含 block 类的多个元素的页面 在 Capybara 中 我希望能够在完成操作之前循环遍历并引用此类中的每个元素 但是 到目前为止我尝试过的代码都不起作用 这是我尝试过的 within block nth child 1 do
  • 同时刷新物化视图会导致表膨胀

    在 PostgreSQL 9 5 中 我决定创建一个物化视图 effects 并安排每小时并发刷新 因为我希望它始终可用 REFRESH MATERIALIZED VIEW CONCURRENTLY effects 一开始一切都很顺利 我的