PostgreSQL数据优化——死元组清理

最近遇到一个奇怪的问题,一个百万级的PostgreSQL表,只有3个索引。但是每次执行insert或update语句就要几百ms以上。经过查询发现是一个狠简单的问题,数据库表死元组太多了,需要手动清理。

在 PG 中,update/delete 语句的实现通过 MVCC 机制的多版本链实现。如下图所示,更新一条元组时,会将原来的元组标记,并新增一条元组。后续的事物通过快照来判断元组的可见性。

对于一条已经被更新/删除的元组来说,当这条元组对所有事物都不可见后,它的存在就没有意义了,理应被删除,对于这种元组,我们称之为"死元组"。当一张表有大量更新/删除时,如果不做清理的话,表里面就会积攒很多这样的"死元组",占用大量的空间,造成表空间膨胀。

一、清理前

  • 查询死元组数量SQL
sql 复制代码
SELECT
    c.relname 表名,
    (current_setting('autovacuum_analyze_threshold')::NUMERIC(12,4))+(current_setting('autovacuum_analyze_scale_factor')::NUMERIC(12,4))*reltuples AS 自动分析阈值,
    (current_setting('autovacuum_vacuum_threshold')::NUMERIC(12,4))+(current_setting('autovacuum_vacuum_scale_factor')::NUMERIC(12,4))*reltuples AS 自动清理阈值,
    reltuples::DECIMAL(19,0) 活元组数,
    n_dead_tup::DECIMAL(19,0) 死元组数
FROM
    pg_class c 

LEFT JOIN pg_stat_all_tables d

    ON C.relname = d.relname
WHERE
    c.relname ='你要查询的表名'  AND reltuples > 0
    AND n_dead_tup > (current_setting('autovacuum_analyze_threshold')::NUMERIC(12,4))+(current_setting('autovacuum_analyze_scale_factor')::NUMERIC(12,4))*reltuples;
  • 此时的数据库插入耗时测试,执行update语句

Affected rows: 1

时间: 1.371s

二、配置自动清理

AUTOVACUUM:自动清理元组。开启自动清理后,PostgreSQL会在合适的时候自动执行VACUUM操作。

sql 复制代码
-- 查看当前autovacuum的状态
SHOW autovacuum;
 
-- 开启autovacuum
SET autovacuum = on;

三、使用VACUUM手动清理

自动清理,有时候可能由于参数配置,效果不佳。可以使用VACUUM命令手动清理,注意,清理过程中会锁表

sql 复制代码
VACUUM FULL VERBOSE 模式名.表名;
VACUUM FULL VERBOSE ANALYZE 模式名.表名;

清理后update效果

Affected rows: 1

时间: 0.427s

四、查询历史清理信息

sql 复制代码
SELECT
    relname 表名,
    seq_scan 全表扫描次数,
    seq_tup_read 全表扫描记录数,
    idx_scan 索引扫描次数,
    idx_tup_fetch 索引扫描记录数,
    n_tup_ins 插入的条数,
    n_tup_upd 更新的条数,
    n_tup_del 删除的条数,
    n_tup_hot_upd 热更新条数,
    n_live_tup 活动元组估计数,
    n_dead_tup 死亡元组估计数,
     last_vacuum 最后一次手动清理时间,
    last_autovacuum 最后一次自动清理时间,
    last_analyze 最后一次手动分析时间,
    last_autoanalyze 最后一次自动分析时间,
    vacuum_count 手动清理的次数,
    autovacuum_count 自动清理的次数,
     analyze_count 手动分析此表的次数,
    autoanalyze_count 自动分析此表的次数,
    ( CASE WHEN n_live_tup > 0 THEN n_dead_tup :: float8 / n_live_tup :: float8 ELSE 0 END ) :: NUMERIC ( 12, 2 ) AS "死/活元组的比例"
FROM
    pg_stat_all_tables
WHERE
    schemaname = 'public'
ORDER BY n_dead_tup::float8 DESC;
相关推荐
养生技术人18 小时前
Oracle OCP认证考试题目详解082系列第57题
运维·数据库·sql·oracle·开闭原则
不良人天码星18 小时前
redis-zset数据类型的常见指令(sorted set)
数据库·redis·缓存
心灵宝贝19 小时前
libopenssl-1_0_0-devel-1.0.2p RPM 包安装教程(openSUSE/SLES x86_64)
linux·服务器·数据库
EnCi Zheng21 小时前
SpringBoot + PostgreSQL 密码认证失败 Windows 系统解决方案
windows·spring boot·postgresql
程序新视界21 小时前
MySQL中,IS NULL和IS NOT NULL不会走索引?错!
数据库·mysql·dba
wdfk_prog21 小时前
闹钟定时器(Alarm Timer)初始化:构建可挂起的定时器基础框架
java·linux·数据库
许长安21 小时前
Redis(二)——Redis协议与异步方式
数据库·redis·junit
java_python源码21 小时前
python高校心理健康服务小程序(源码+文档+调试+基础修改+答疑)
数据库·sqlite
简色1 天前
题库批量(文件)导入的全链路优化实践
java·数据库·mysql·mybatis·java-rabbitmq
点灯小铭1 天前
基于单片机的自动存包柜设计
数据库·单片机·mongodb·毕业设计·课程设计