zoukankan      html  css  js  c++  java
  • 为什么vacuum后表还是继续膨胀?

    摘要: 对于更新和删除操作频繁的表,会存在大量垃圾数据,导致磁盘空间的浪费和查询扫描时额外的IO开销,需要定期执行清理操作(vacuum)来控制行存表以及表上索引的膨胀。本文将对vacuum的原理以及影响vacuum效果的因素进行简单介绍。

    本文分享自华为云社区《为什么vacuum后表还是继续膨胀?》,原文作者:大威天龙:-。

    vacuum简介 :

    对于GaussDB中的行存表,在更新元组或者删除元组后,旧版本的数据仍然存在,仅在元组头信息中标记了删除或更新的事务号(xmax)。对于更新和删除操作频繁的表,会存在大量垃圾数据,导致磁盘空间的浪费和查询扫描时额外的IO开销,需要定期执行清理操作(vacuum)来控制行存表以及表上索引的膨胀。

    vacuum 操作的内部原理:

    Vacuum 的主要步骤:

    1.移除死亡元组并对满足条件的老元组执行frozen操作。

    2.移除指向死亡元组的索引元组,更新对应表的fsm 和 vm 文件

      • FSM: free space map 空闲空间映射文件,插入数据时会根据该文件来选择合适的page.
      • VM: visibility map 可见性映射文件,后续vacuum时会根据该文件来选择是否扫描某个page,提高vacuum效率;同时在进行index-only-scan时也会使用该文件来提高可见性判断的效率)。

    3.更新统计数据pg_stat_all_tables。 Linepointer 不会被移除,用于在之后复用。
    示意图如下:

    vacuum 之前


    vacuum 之后

    为什么vacuum后表还是继续膨胀

    影响vacuum效果的因素

    1、Oldestxmin的推进
    vacuum 只能清理掉当前全局存活的最老事务(OldestXmin)之前的事务所产生的垃圾数据,所以如果仍然存在老事务的话(比如长事务或者长sql的存在),新事务所产生的垃圾数据并不会被vacuum立即清理。例如:

    会话1:新建表row_tbl并插入一条数据,起一个事务先不提交

    gaussdb=# create table row_tbl(a int, b int);
    CREATE TABLE
    gaussdb=# insert into row_tbl values(1,1);
    INSERT 0 1
    gaussdb=# begin;
    BEGIN
    gaussdb=# SELECT txid_current_snapshot();
     txid_current_snapshot
    -----------------------
     210115:210115:
    (1 row)
    gaussdb=# SELECT txid_current();
     txid_current
    --------------
           210115
    (1 row)

    会话2:删除数据后做vacuum清理操作,再次插入数据,数据也没有复用之前空间;查询视图可以发现垃圾数据并没有被清理掉。

    gaussdb=# select ctid,* from row_tbl;
     ctid  | a | b
    -------+---+---
     (0,1) | 1 | 1
    (1 row)
    gaussdb=# delete row_tbl;
    DELETE 1
    gaussdb=# SELECT txid_current_snapshot();
     txid_current_snapshot
    -----------------------
     210115:210122:
    (1 row)
    gaussdb=# vacuum row_tbl;
    VACUUM
    gaussdb=# insert into row_tbl values(2,2);
    INSERT 0 1
    gaussdb=# select ctid,* from row_tbl;
     ctid  | a | b
    -------+---+---
     (0,2) | 2 | 2
    (1 row)
    gaussdb=# select n_dead_tup, last_vacuum from pg_stat_all_tables where relname='row_tbl';
     n_dead_tup |          last_vacuum
    ------------+-------------------------------
              1 | 2021-06-10 20:04:58.987631+08
    (1 row)

    会话1:将会话1的事务结束再执行vacuum,查询视图可以发现没有死亡元组,插入数据可以发现复用了旧的空间(即ctid是(0,1)的空间)。

    gaussdb=# SELECT txid_current_snapshot();
     txid_current_snapshot
    -----------------------
     210136:210136:
    (1 row)
    gaussdb=# vacuum row_tbl;
    VACUUM
    gaussdb=# select n_dead_tup, last_vacuum from pg_stat_all_tables where relname='row_tbl';
     n_dead_tup |          last_vacuum
    ------------+-------------------------------
              0 | 2021-06-10 20:09:10.516564+08
    (1 row)
    gaussdb=# insert into row_tbl values(3,3);
    INSERT 0 1
    gaussdb=# select ctid,* from row_tbl;
     ctid  | a | b
    -------+---+---
     (0,1) | 3 | 3
     (0,2) | 2 | 2
    (2 rows)

    2、LinePointer状态还未处于unused

    元组被删除后,只有当vacuum将元组的LinePointer(或者叫item pointer, 指向具体的元组)置为LP_UNUSED状态后,该LinePointer才有可能在新插入数据时复用。

    3、Fsm还未生成
    插入数据时,依赖fsm文件来选择可用的page,如果fsm没有生成则会导致使用新的page而不是复用旧的。

    4、批量导入
    在旧版本Gaussdb中,对表进行批量插入数据的操作时,会直接申请新的page来插入数据。所以在某些场景下虽然vacuum后清理了脏数据,但由于业务场景以批量插入为主,导致vacuum对膨胀的控制效果并不理想。目前已经支持批量插入数据时对空间的复用。

    一些建议与总结

    1. 尽量避免长事务,可以通过视图pg_running_xacts查看是否有老事务没有结束或者两阶段事务残留
    2. 定期做vacuum来及时回收垃圾空间
    3. 对于已经膨胀的索引可以通过reindex来缩小大小。
    4. vacuum能清理垃圾数据,但无法将这些空间还给操作系统,对于已经膨胀的表只能通过vacuum full来缩小大小。

    想了解GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技~

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    必备java参考资源列表
    java中使用js函数
    6.游戏特别离不开脚本(3)-JS脚本操作java(2)(直接解析JS公式,并非完整JS文件或者函数)
    6.游戏特别离不开脚本(3)-JS脚本操作java(直接解析JS公式,并非完整JS文件或者函数)
    6.游戏特别离不开脚本(2)-获取脚本引擎
    6.游戏特别离不开脚本(1)-原因
    5.eclipse 自带的jdk没有源码,改了它
    4.调个绿豆沙护眼色
    3.myeclipse 8.5 m1 注册码
    Zxing中文乱码的简单解决办法
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/14926898.html
Copyright © 2011-2022 走看看