暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL事务以及Clog日志查看

墨香溪的溪 2021-08-02
3660

事务ID:

无论事务何时开始,都会由事务管理器分配一个唯一的事务ID(txid)。PostgreSQL 的txid 由32位无符号整数组成。2^32=4294967296, 也就是有42亿左右个事务。在事务开始后,使用内置函数 txid_current()会返回当前事务id。

    example:
    ivandb=# begin;
    BEGIN
    ivandb=# select txid_current();
    txid_current
    --------------
    653
    (1 row)
    ivandb=# insert into t3 values('ivan4');

    PostgreSQL 保留以下三个特别的事务ID:

    0:表示无效事务ID。

    1:表示初始化事务id,只在初始化数据库集蔟的时候使用。

    2:表示冻结事务id。(这个很重要)

    因为在PostgreSQL中,事务ID有限,所以事务ID的使用是循环的。对一个事务来说,在它之前的21亿个事务都属于过去,之后的21亿属于未来。

    事务状态:

    在PostgreSQL中定义了四种事务状态,分别是IN_PROGRESS(进行中),CMMITTED(已提交),ABORTED(回滚,中断)以及SUB_COMMITTED(子事务已提交)

      \src\include\access\clog.h
      /*
      * Possible transaction statuses --- note that all-zeroes is the initial
      * state.
      *
      * A "subcommitted" transaction is a committed subtransaction whose parent
      * hasn't committed or aborted yet.
      */
      typedef int XidStatus;
      #define TRANSACTION_STATUS_IN_PROGRESS 0x00
      #define TRANSACTION_STATUS_COMMITTED 0x01
      #define TRANSACTION_STATUS_ABORTED 0x02
      #define TRANSACTION_STATUS_SUB_COMMITTED 0x03

      commit log:

      commit log的物理文件存在$PGDATA/pg_xact 目录下。

      Clog 会在启动的时候加载到内存里面,一般修改也会在内存中操作。所以要把commit log刷到物理文件,需要执行checkpoint.

      具体定义:\src\backend\access\transam\clog.c

        /*
        * Defines for CLOG page sizes. A page is the same BLCKSZ as is used
        * everywhere else in Postgres.
        *定义CLOG 的页大小,页的大小应该跟数据库页面大小一致。
        * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
        * CLOG page numbering also wraps around at 0xFFFFFFFF/CLOG_XACTS_PER_PAGE,
        * and CLOG segment numbering at
        * 0xFFFFFFFF/CLOG_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
        * explicit notice of that fact in this module, except when comparing segment
        * and page numbers in TruncateCLOG (see CLOGPagePrecedes).
        */
        /* We need two bits per xact, so four xacts fit in a byte */
        #define CLOG_BITS_PER_XACT 2 -- 2bit 记录一个事务
        #define CLOG_XACTS_PER_BYTE 4 -- 一字节记录4个事务
        #define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE) -- 一页记录(8196*4=32784个事务)
        #define CLOG_XACT_BITMASK ((1 << CLOG_BITS_PER_XACT) - 1)
        #define TransactionIdToPage(xid) ((xid) (TransactionId) CLOG_XACTS_PER_PAGE) --txid存在于哪页 >> txid/(BLCKSZ*4)
        #define TransactionIdToPgIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE) --txid 在页中的偏移量 txid%(8192*4)
        #define TransactionIdToByte(xid) (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE) --页内的第几个byte (txid%(8192*4))/4
        #define TransactionIdToBIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_BYTE) --该字节中的第2*n bits.(n=[0-3]) xid%4

        note:\src\include\access\slru.h

          /*
          * Define SLRU segment size. A page is the same BLCKSZ as is used everywhere
          * else in Postgres. The segment size can be chosen somewhat arbitrarily;
          * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG
          * or 64K transactions for SUBTRANS.
          *
          * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
          * page numbering also wraps around at 0xFFFFFFFF/xxxx_XACTS_PER_PAGE (where
          * xxxx is CLOG or SUBTRANS, respectively), and segment numbering at
          * 0xFFFFFFFF/xxxx_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need
          * take no explicit notice of that fact in slru.c, except when comparing
          * segment and page numbers in SimpleLruTruncate (see PagePrecedes()).
          */
          #define SLRU_PAGES_PER_SEGMENT 32

          example:

          打开四个session:

          同时执行以下SQL:

            begin;
            select txid_current();
            insert into t3 values('ivan4');

            获取了四个事务id: 660,661,662,663,根据以上算法,这四个事务ID会在同一页,同一个byte 上。

            --存在0号页

              postgres=# select 660/(8196*4),661/(8196*4),662/(8196*4),663/(8196*4);
              ?column? | ?column? | ?column? | ?column?
              ----------+----------+----------+----------
              0 | 0 | 0 | 0

              --存在于页中的偏移量

                postgres=# select 660%(8196*4),661%(8196*4),662%(8196*4),663%(8196*4);
                ?column? | ?column? | ?column? | ?column?
                ----------+----------+----------+----------
                660 | 661 | 662 | 663

                --页内第165个字节。

                  postgres=# select (660%(8196*4))/4,(661%(8196*4))/4,(662%(8196*4))/4,(663%(8196*4))/4;
                  ?column? | ?column? | ?column? | ?column?
                  ----------+----------+----------+----------
                  165 | 165 | 165 | 165

                  --字节内偏移量(2bit 为一个状态)

                    postgres=# select 660%4,661%4,662%4,663%4;
                    ?column? | ?column? | ?column? | ?column?
                    ----------+----------+----------+----------
                    0 | 1 | 2 | 3

                    --查看clog.

                      postgres=# checkpoint;
                      CHECKPOINT
                        [postgres@flyme pg_xact]$ ls -lrt
                        total 8
                        -rw------- 1 postgres postgres 8192 Apr 5 22:09 0000

                        note: 可以看到此时的clog 是8k,也就是一个页。(0 号)

                          [postgres@flyme pg_xact]$ hexdump -C ./0000 -s 165 -n 1
                          000000a5 00 |.|
                          000000a6

                          此时的状态为0, 因为4个session 都属于未提交状态。

                          --提交事务660

                          执行checkpoint 刷新clog. 可以看到物理clog 已经刷新。

                            [postgres@flyme pg_xact]$ ls -lrt
                            total 8
                            -rw------- 1 postgres postgres 8192 Apr 5 22:21 0000
                            [postgres@flyme pg_xact]$ date
                            Sun Apr 5 22:21:56 CST 2020

                            dump 该bytes.

                              [postgres@flyme pg_xact]$ hexdump -C ./0000 -s 165 -n 1
                              000000a5 01 |.|
                              000000a6

                              为字节为1. 转换为8位二进制:

                                postgres=# select 01::bit(8);
                                bit
                                ----------
                                00000001

                                可以看到,该字节的0号位已经更新为已提交状态。

                                --回滚事务662

                                执行checkpoint 刷新clog。

                                dump 该bytes.

                                  [postgres@flyme pg_xact]$ hexdump -C ./0000 -s 165 -n 1
                                  000000a5 21 |!|
                                  000000a6

                                  note:此处有坑,21为16进制数据。

                                  转换为8位二进制:

                                    postgres=# select x'21'::bit(8);
                                    bit
                                    ----------
                                    00100001
                                    (1 row)

                                    可以看到该字节的2号位,已经更新为aborted 状态。

                                    其他验证类似。此处不再举例。


                                    文章转载自墨香溪的溪,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                    评论