PostgreSQL的并发控制

1. 并发控制概述

本文是《PostgreSQL指南--内幕探索》(铃木启修著 冯若航 刘阳明 张文升译)的读书笔记,仅供自己学习使用,请勿转载。这是一本好书,如有需要请直接购买书籍。

当多个事务同时在数据库中运行,并发控制是一种用于维持一致性(consistency)和隔离性(isolation)的技术。

从宽泛意义上讲,有三种并发控制技术,分别是多版本并发控制(Multi Version Concurrency Control,MVCC)、严格两阶段锁定(Strict Two-Phase Locking, S2PL)和乐观并发控制(Optimisitic Concurrency Control, OCC),每种技术都有多种变体。在 MVCC 中,每个写操作都会创建一个新版本的数据项,通过这种方式保证事务间的隔离。MVCC 的主要优势是 “读不阻塞写,写不阻塞读”。相反,S2PL 在写操作时会获取对象的排它锁,阻塞对象上的读操作。 PostgreSQL和一些其它关系型数据库采用了一种 MVCC 的变体,叫做快照隔离(Snapshot Isolation, SI)

Oracle 采用回滚段来实现 SI。 当写入新数据时,旧版本先写入回滚段,随后用新版本覆写至数据区域。PostgreSQL 没有回滚段,新版本的数据直接插入相关表页,旧版本的数据设置标志位,标志其已被删除。在读取对象时,PostgreSQL按照可见性检查规则,为每个事务选择正确的对象版本作为响应。

SI 可以避免脏读( PG 的 RC、RR、Serializable 隔离级别)、不可重复读(PG 的 RR、Serializable 隔离级别)、幻读(PG 的 RR、Serializable 隔离级别)。但 SI 无法实现真正的可串行化,因为在 SI 中可能出现串行化异常,例如写偏差和只读事务偏差。PostgreSQL 在 9.1 后添加了可串行化快照隔离(Serializable Snapshot Isolation, SSI ),提供了真正的 SERIALIZABLE 隔离等级(SQL SERVER也使用 SSI,而oracle 仍使用 SI), 已解决前面的问题。

PostgreSQL 对 DML 使用 SSI,对 DDL 使用 2PL。

2. 事务标识

PostgreSQL 的事务标识 txid 是一个 32 位无符号整型,取值空间大约为 42 亿。可以使用内置的select txid_current(); 获取当前事务id。

PostgreSQL 保留以下三个特殊 txid:

  • 0 表示无效的 txid;
  • 1 表示初始启动的 txid,仅用于数据库集群的初始化过程;
  • 2 表示冻结的 txid;

首先我们来看一下PG中事务ID的比较逻辑。

/*
 * TransactionIdPrecedes --- is id1 logically < id2?
 */
bool
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
{
  /*
   * If either ID is a permanent XID then we can just do unsigned
   * comparison.  If both are normal, do a modulo-2^32 comparison.
   */
  int32   diff;

  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
    return (id1 < id2);

  diff = (int32) (id1 - id2);
  return (diff < 0);
}

TransactionIdIsNormal宏的作用是判断id1是否大于等于3。这里值得注意的是diff = (int32) (id1 - id2)。这里使用了一个编程技巧,比如发生了事务ID回卷:

id1 = 4294967200
id2 = 100

// int32    -2147483648 ~ 2147483647 (32亿+)
// uint32   0 ~ 4294967295 (42亿+)

id1 - id2 = 4294967100这个值强转成(int32)类型后是一个负数,会发生return true,函数会认为id1 < id2,也就是说id2是更新的事务。

我们继续考虑下一个场景,id2继续增长到21亿多时,id1和id2的差值已经在int32的范围内了,所以diff会大于零,return false函数会认为id1= 42亿+ > id2= 21亿+,结论是id1是更新的事务!这里就出现问题了,一个老事务被判断成了新事务。

所以PostgreSQL必须保证一个数据库中两个有效的事务之间的年龄差最多是,约为20亿。

PG 的 txid 空间是 4B 的,且将 txid 空间视作一个环。对于某个特定的 txid,其前约 21 个 txid 对于它来说属于过去,对于它是可见的;其后约 21 亿个 txid 属于将来,对于它来说是不可见的。那么这种机制就有可能产生事务回环(或称 事务回卷)。假设 Tuple1 是 txid =100 这个事务创建的,那么 Tuple1 的 t_xmin 就是 100,假设Tuple1 一直未被修改。现在事务来到了 2^31 + 100, 这时正好执行了一条 SELECT,此时 txid =100 对于当前事务而言,属于过去,所以Tuple1 对 当前事务是可见的。但是如果再执行相同的 SELECT,此时 txid 步进至 2^31 + 101, 对当前事务而言,txid = 100 的事务又变成未来,因此 Tuple1 对当前事务不再可见。这就是所谓的事务回卷问题。为了解决这个问题,PG 引入了冻结事务标识的概念,并实现了一个 FREEZE 的过程。这个后面会将。

3. 元组结构

表页中的元组结构分为普通元组和TOAST元组两者,这里仅介绍普通元组的结构。

堆元组由三部分组成: HeapTupleHeaderData 数据结构,空值位图,以及用户数据。如下图所示:

元组结构.jpg

​ 图 1 元组结构

其中 HeapTupleHeaderData 的定义在 src/include/access/htup_details.h头文件中。

typedef struct HeapTupleFields
{
    TransactionId   t_xmin;     /* 插入事务的xid */
    TransactionId   t_xmax;     /* 删除或更新事务的xid */
    
    union
    {
        CommandId       t_cid;  /* 插入或删除的命令 ID */   
        TransactionId   t_xvac; /* 老式 VACUUM FULL 的事务ID */
    } t_field3;
} HeapTupleFields;

typedef struct DatumTupleFields
{
    int32   datum_len_;     /* 可变首部的长度 */
    int32   datum_typmod;   /* -1 或是记录类型的标识 */
    Oid     datum_typeid;   /* 复杂类型的 oid 或记录 id*/
} DatumTupleFields;

typedef struct ItemPointerData
{
    BlockIdData     ip_blkid;   /* 32bit, 块号 */
    OffsetNumber    ip_posid;   /* 16bit, 块内偏移量 */
} ItemPointerData;

typedef struct BlockIdData 
{
    uint16  bi_ho;  /* 高16位 */
    unit16  bi_lo;  /* 低16位 */
} BlockIdData;

typedef struct HeapTupleHeaderData
{
    union 
    {
        HeapTupleFields     t_heap;
        DatumTupleFields    t_datum;
    } t_choice;
    
    ItemPointerData         t_ctid;
    
    /* 下面的字段必须与结构 MinimalTupleData 相匹配 */
    unit16      t_infomask2;    /* 属性和标记位 */
    unit16      t_infomask;     /* 很多标记位... */
    unit8       t_hoff;         /* 首部 + 位图 + 填充的长度 */
    /* ^ - 23 bytes - ^ */
    bits8       t_bits[1];      /* NULL 值的位图 —— 变长的 */
    
    /* 本结构后面还有更多数据*/
} HeapTupleHeaderData;

typedef HeapTupleHeaderData * HeapTupleHeader;

t_xmin 和 t_xmax 的含义不用再赘述,t_cid 的含义如下:

  • 保存命令标识( command id, cid),cid 的意思是在当前事务中,执行当前命令之前执行了多少 SQL 命令, 从零开始计数。

4. 元组的增、删、改 以及 FSM机制

4.1 元组的增、删、改

PG 的元组的增、删、 改已经是老生常谈的问题了:

  • 元组插入时,例如 begin; insert into tbl values('A'); commit;新元组直接插入目标表的页面中,被插入的元组的 t_xmin 被设置为插入事务 txid, t_xmax 被设置为 0,t_cid 被设置为 0, t_ctid 被设置为 (0, 1), 指向自身。
  • 元组删除时,目标元组不是实际被删除,而是逻辑上被标记为删除:目标元组的 t_xmax 被设置为 删除事务的 txid, 如果删除事务已提交,此时目标元组即成为死元组。
  • 元组更新是元组删除 + 元组插入。

我们可以使用第三方贡献的扩展插件 pageinspect ,查看页面的具体内容,例如:

select lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
from heap_page_items(get_raw_page(tb1, 0));

4.2 FSM 机制

插入堆或索引元组时,PG 使用 空闲空间映射 (Free Space Map, FSM)来选择可供插入的页面。其机制可以参考这篇文章: https://www.jianshu.com/p/4064dbb72414 。

4.2.1 FSM 机制的基本原理:

FSM 机制采用一个字节表示空闲空间的大小范围,我们将这个字节叫做 FSM 级别(category)。FSM级别和真实的FSM范围之间的映射关系如表 3-1 所示。
表1 FSM 级别和空闲空间范围之间的映射关系

字节取值 表示的空闲空间范围(字节)
0 0 ~ 31
1 32 ~ 63
...... ......
255 8164 ~ 8192

为了实现快速查找,FSM 文件并不是简单使用数组顺序存储每个表块的空闲空间,而是使用了树结构。在FSM块之间使用了一种三层树结构,其中第 0 层和第 1 层是辅助层,第二层 FSM 块实际存储各表块的空闲空间级别(category)。第0层 FSM 块只有一个,作为树根;第 1 层 FSM 块可以有多个,作为第 0 层 FSM 块的子节点;第 2 层 FSM 块同理。每个 FSM 块内又构成一棵局部的最大堆二叉树,但每一层 FSM 块内的最大堆二叉树又略有不同。
1. 第 2 层 FSM 块内大根堆二叉树的每一个叶子节点对应一个表块的空闲空间级别。按照从左到右的顺序,最左边 FSM 块的最左边叶子节点代表表文件的第一块,左边第二个叶子节点代表表文件的第二块。
2. 第 1 层 FSM 块内大根堆二叉树的叶子节点从左到右对应第 2 层 FSM 块的根节点。
3. 第 0 层 FSM 块内大根堆二叉树的叶子节点从左到右对应第 1 层 FSM 块的根节点。
从上面的介绍可知,三层树结构形成了一个逻辑上的大根堆结构,其叶子节点从左到右依次对应表文件中的文件块。

在单个 FSM 块内,使用大根堆能保证所有叶子节点的最大值被上升到根节点处。因此我们只要看单个 FSM 块内的根节点值就可指导此FSM块内空闲空间的上限值。

每个 FSM 块大小为 8KB,除去必要的页头信息,FSM块剩下的空间都用来存储块内的二叉树结构,每个叶子节点都用一个字节表示,因此FSM块内大约可以保存4000个叶子节点(实际为4069,计算方法见宏定义SlotsPerFSMPage,这里为了简化描述将其近似为4000)。为什么这样一个三层的树结构就能完全表示一个表文件的所有数据块的空闲空间呢?这是因为,一个 FSM 文件如果采用三层树结构,大约可以记录 4000^3 个叶子节点,对应于 4000^3 个表块,远大于 2^32 ( 单个表的最大块数,PG 块号数据结构 BlockIdData 中定义块号长度为32,故单个表最多只能有(2^32-1)个表块, 一个表块是 8KB, 因此一个表的数据文件不超过 32 TB)。因此,这样的三层树结构足以记录表文件所有文件块的空闲空间值。

一个 FSM 文件内所包含的 FSM 块和表块的映射关系如图 2 所示。这里假设 FSM 块内大约可以保存 4000 个叶子节点,为了阐述得更明了一些。

图2 FSM物理块与逻辑地址对照.jpg

​ 图2 FSM 页数据结构示意图

4.2.2 FSM 的相关数据结构介绍:

// FSM 块的数据结构:FSMPageData
/*
 * FSM块的数据结构. See src/backend/storage/freespace/README for
 * details.
 */
typedef struct
{
    /*
     * fp_next_slot 是一个整数,用于提示下一次开始查询二叉树时的叶子节点位置
     *(也就是一个满足请求的二叉树叶子节点序号,从零开始计数,用slot表示),
     * 若该FSM块是叶子节点,则将fp_next_slot置为 slot + 1, 否则置为slot。
     */
    int         fp_next_slot;

    /*
     * fp_nodes 可变长数组,存储当前 FSM 块内存储的大根堆二叉树结构。
     */
    uint8       fp_nodes[FLEXIBLE_ARRAY_MEMBER];
} FSMPageData;

typedef FSMPageData *FSMPage;

2. 一个FSM块中可以保存的节点总数目(8192 - 24 - 4 = 8164),一个字节对应于一个节点
#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
                      offsetof(FSMPageData, fp_nodes))

3. FSM块中保存的非叶子节点数目(8192/2 - 1 = 4095)
#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)

4. FSM块中保存的叶子节点数目 (8164 - 4095 = 4069)
#define LeafNodesPerPage (NodesPerPage - NonLeafNodesPerPage)

5. FSM块中的slots个数(即FSM块中可以保存的叶子节点总数,4069)
#define SlotsPerFSMPage LeafNodesPerPage

\src\backend\storage\freespace\freespace.c
6. FSM 相关宏定义
/*
 * 我们仅使用一个字节来存储文件块上的空闲空间,
 * 所以我们将空闲空间划分为256个级别,
 * 最高级别, 255, 表示一个文件块的空闲空间不少于
 * MaxFSMRequestSize 字节 , 第二高级别
 * 代表空闲空间大小从 254 * FSM_CAT_STEP 到
 * MaxFSMRequestSize字节,左闭右开区间.
 *
 * 对于默认的BLCKSZ 等于8k , MaxFSMRequestSize的值为8164 字节, 
 * 空闲空间级别的划分如下所示:
 *
 *
 * 范围        级别
 * 0    - 31   0
 * 32   - 63   1
 * ...    ...  ...
 * 8096 - 8127 253
 * 8128 - 8163 254
 * 8164 - 8192 255
 *
 */
事实上,大小在0~31字节的空闲空间将不会被使用,而当申请空闲空间大小在8164~8192字节之间时,系统将为请求分配MaxFSMRequestSize大小的空闲空间,也即一个空闲的表块。

//FSM空闲空间的级别总数(256)
#define FSM_CATEGORIES  256    

// FSM空闲空间级别的步长(8192/256 = 32)
#define FSM_CAT_STEP    (BLCKSZ / FSM_CATEGORIES)

// 可申请的空闲空间的最大值,就是表块内元组的最大大小(8164字节)
#define MaxFSMRequestSize   MaxHeapTupleSize

/*
 * FSM树结构的总深度,默认是3. 我们需要能够寻址2^32-1 个表块,
 * 而1626 是能满足 X^3 >= 2^32-1的最小的整数. 类似的,
 * 216 能满足 X^4 >= 2^32-1的最小的整数. 
 */
#define FSM_TREE_DEPTH  ((SlotsPerFSMPage >= 1626) ? 3 : 4)

// FSM三层树结构根节点所在的层号(3-1 =2)
#define FSM_ROOT_LEVEL  (FSM_TREE_DEPTH - 1)

// FSM三层树结构叶子节点所在的层号(0)
#define FSM_BOTTOM_LEVEL 0

4.2.3 题外话:利用 FSM 机制求解表膨胀率

德哥给出了一种基于 FSM 机制精确地求解膨胀率的方法,参考这一篇文章: https://github.com/digoal/blog/blob/master/201306/20130628_01.md, 他认为这是一个绝招。

这里为了偷个懒,直接把德哥的测试和分析过程搬了过来。

创建测试表 :

digoal=# create table fsm_test(id int, info text);  
CREATE TABLE  

插入测试数据

digoal=# insert into fsm_test select generate_series(1,1000000),'test';  
INSERT 0 1000000  

查看对象大小 :

digoal=# select pg_relation_size('fsm_test');  
 pg_relation_size   
------------------  
         44285952  
(1 row)  

表对应的文件

digoal=# select pg_relation_filepath('fsm_test');  
 pg_relation_filepath   
----------------------  
 base/16385/33311  
(1 row)  

fsm文件加_fsm后缀.

digoal=# select * from pg_stat_file('base/16385/33311_fsm');  
 size  |         access         |      modification      |         change         | creation | isdir   
-------+------------------------+------------------------+------------------------+----------+-------  
 32768 | 2013-06-27 21:59:21+08 | 2013-06-27 21:59:21+08 | 2013-06-27 21:59:21+08 |          | f  
(1 row)  

fsm文件共计4个page, 所以有2个是三级page(2,3). 计算时也只需要统计三级page的信息.

digoal=# select 32768/8192;  
 ?column?   
----------  
        4  
(1 row)  

fsm文件跟随对象文件名. 所以在truncate对象后fsm会重新生成. 但是必须要第一次vacuum后fsm才会出现, 否则是没有的.

digoal=# truncate fsm_test;  
TRUNCATE TABLE  
digoal=# insert into fsm_test select generate_series(1,1000000),'test';  
INSERT 0 1000000  
digoal=# select pg_relation_filepath('fsm_test');  
 pg_relation_filepath   
----------------------  
 base/16385/33325  
(1 row)  

新fsm文件名 :

digoal=# select * from pg_stat_file('base/16385/33325_fsm');  
 size  |         access         |      modification      |         change         | creation | isdir   
-------+------------------------+------------------------+------------------------+----------+-------  
 32768 | 2013-06-28 09:55:16+08 | 2013-06-28 09:55:16+08 | 2013-06-28 09:55:16+08 |          | f  
(1 row)  

查看freespace map page的内容可以通过pageinspect插件 :

digoal=# create extension pageinspect;  
CREATE EXTENSION  
digoal=# select * from fsm_page_contents(get_raw_page('fsm_test','fsm',0));  
 fsm_page_contents   
-------------------  
 fp_next_slot: 0  +  
   
(1 row)  
  
digoal=# select * from fsm_page_contents(get_raw_page('fsm_test','fsm',1));  
 fsm_page_contents   
-------------------  
 fp_next_slot: 0  +  
   
(1 row)  
  
digoal=# select * from fsm_page_contents(get_raw_page('fsm_test','fsm',2));  
 fsm_page_contents   
-------------------  
 fp_next_slot: 0  +  
   
(1 row)  
  
digoal=# select * from fsm_page_contents(get_raw_page('fsm_test','fsm',3));  
 fsm_page_contents   
-------------------  
 fp_next_slot: 0  +  
   
(1 row)  

表明fsm_test表目前没有剩余空间.

直接从get_raw_page取数据 :

digoal=# select * from get_raw_page('fsm_test','fsm',3);  
\x0000000000000000000000001800002000200420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  

删除fsm_test的数据, 只留最后一行.

digoal=# delete from fsm_test where id<1000000;  
DELETE 999999  
digoal=# delete from fsm_test where id<1000000;  
DELETE 999999  
digoal=# vacuum verbose analyze fsm_test ;  
INFO:  vacuuming "public.fsm_test"  
INFO:  "fsm_test": found 0 removable, 0 nonremovable row versions in 0 out of 5406 pages  
DETAIL:  0 dead row versions cannot be removed yet.  
There were 0 unused item pointers.  
0 pages are entirely empty.  
CPU 0.00s/0.00u sec elapsed 0.00 sec.  
INFO:  vacuuming "pg_toast.pg_toast_33311"  
INFO:  index "pg_toast_33311_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_33311": 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.  
0 pages are entirely empty.  
CPU 0.00s/0.00u sec elapsed 0.00 sec.  
INFO:  analyzing "public.fsm_test"  
INFO:  "fsm_test": scanned 5406 of 5406 pages, containing 1 live rows and 0 dead rows; 1 rows in sample, 1 estimated total rows  
VACUUM  

vacuum后空间回收并可以重复利用, 但是还占用了磁盘空间.

这个时候查看fsm就不一样了.

digoal=# select * from get_raw_page('fsm_test','fsm',3);  
\x00000000000000000000000018000020002004200000000000000000f4f400e8f40000e8e8f40000000000e8e8e8e8e8f400000000000000000000e8e8e8e8e8e8e8e8e8e8f4000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  

分层后信息如下 :

  1. 头信息 :
\x00000000000000000000000018000020002004200000000000000000  
  1. 树信息如下 :
f4  
f400  
e8f40000  
e8e8f40000000000  
e8e8e8e8e8f400000000000000000000  
e8e8e8e8e8e8e8e8e8e8f4000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  
e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000  

最后一级表示heap page的剩余空间, 刚好4069个字节. 往上是2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1;

所以在计算剩余空间的时候,也只需要计算最后的4096个字节, 同时去除1级和2级fsm page.

本例的计算方法如下 :

do language plpgsql $$  
declare  
  v_fsm_node_id int;  
  v_fsm_pageid int;  
  v_fsm_pages int;  
  v_rel_pages int;  
  v_fsm_file text;  
  v_block_size int;  
  x int;  
  res int8 := 0;  
begin  
  -- 查出block_size  
  select setting::int into v_block_size from pg_settings where name='block_size';  
  -- 查出fsm文件名  
  select pg_relation_filepath('fsm_test')||'_fsm' into v_fsm_file;  
  -- 查处fsm占用多少个page  
  select size/v_block_size into v_fsm_pages from pg_stat_file(v_fsm_file);  
  -- 查出表占用多少个page  
  select relpages into v_rel_pages from pg_class where relname='fsm_test';  
  raise notice '%', v_rel_pages;  
  -- 查处剩余空间  
  for v_fsm_pageid in 2..(v_fsm_pages-1) loop  
    for v_fsm_node_id in 8249..16385 by 2 loop  
      execute $_$select (x'$_$||substring(textin(byteaout(get_raw_page('fsm_test','fsm',v_fsm_pageid))),v_fsm_node_id,2)||$_$')::int$_$ into x;   
      -- raise notice '%', x;  
      res := res+x;  
    end loop;  
  end loop;  
  raise notice 'res:%', res;  
end;  
$$;  

结果

NOTICE:  5406  
NOTICE:  res:1254204  
DO  

因此这个表当前可用的剩余空间占比如下 :

digoal=# select 1254204/(5406*256.0);  --1254204(所有FSM块标识的空闲空间级别的总和)*32(空闲空间级别的步长是32)/8192(block_size) / 5406(数据块数)
-[ RECORD 1 ]--------------------  
?column? | 0.90625867092119866815  

这个结果和pgstattuple得到的结果一致.

digoal=#  select * from pgstattuple('fsm_test');  
-[ RECORD 1 ]------+---------  
table_len          | 44285952  
tuple_count        | 1  
tuple_len          | 33  
tuple_percent      | 0  
dead_tuple_count   | 0  
dead_tuple_len     | 0  
dead_tuple_percent | 0  
free_space         | 40134544  
free_percent       | 90.63  

所以监控膨胀用 pgstattuple 这个插件就可以了, 前面使用 freespace map 查看只是让大家了解里面的细节.

4.2.4 pg_freespacemap 扩展

扩展 pg_freespacemap 可以提供特定表或索引上的空闲空间信息。下面的查询列出了特定表上每个页面的空闲率。

create extension pg_freespacemap;

select *, round(100 * avail/8192, 2) as "freespace ratio"
from pg_freespace(accounts);

blkno  |  avail |  freespace  ratio
-------+--------+-----------------------
    0  |   7904 |               96.00
    1  |   7520 |               91.00
    2  |   7136 |               87.00
... ... 

5. 提交日志

PG 在提交日志 (Commit Log, CLOG)中保存事务的状态。 提交日志分配于共享内存值中,并用于事务处理过程的全过程。

5.1 事务状态

PG 定义了 4 种事务状态,即 IN_PROGRESS, COMMITED, ABORTED, SUB_COMMITED。

SUBCOMMITED 用于子事务,本文暂省略了这方面的讨论,只涉及前三种。

5.2 提交日志如何工作

提交日志(CLOG)在逻辑上是一个数组,由共享内存中一系列 8KB 的页面组成。数组的序号对应着相应事务的 txid,其内容则是事务的状态。

当需要获取事务的状态时,PG 将调用内部函数读取 CLOG,并返回请求事务的状态。

5.3 提交日志的维护

当 PG 关机或执行存档过程时,CLOG 数据会写入 pg_clog 子目录下的文件中(在10.0,pg_clog 被重命名为 pg_xact)。这些文件被命名为 0000,0001 等。文件的最大尺寸为 256 KB。例如,当 CLOG 使用 37个页面时(296 KB),数据会写入 0000 和 0001 两个文件,大小分别为 256 KB 和 40 KB。

当 PG 启动时,会加载存储在 pg_clog ( pg_xact ) 中的文件,用其数据初始化 CLOG。

CLOG 的大小会不断增长,VACUUM 会删除旧的不需要的 CLOG。

6. 事务快照

事务快照是一个数据集,存储着某个特定事务在某个特定时间点所看到的的事务状态信息:哪些事务处于活跃状态。活跃状态意味着事务还没开始或正在进行中。

事务快照在 PG 中的文本表示格式定义为 xmin:xmax:xip_list。PG 的内置函数 txid_current_snapshot 显示当前事务的快照。

select txid_current_snapshot();
txid_current_snapshot
--------------------------------
100:104:100,102
(1 row)

xmin:xmax:xip_list 的含义如下:

  1. txid < xmin 的事务对当前事务来说都不活跃,要么已提交并可见,要么已回滚并死亡;
  2. txid >= xmax 的事务都处于活跃状态,对当前事务来说都不可见;
  3. 处于xmin <= txid < xmax 之间的, 且被 xip_list 指定的这部分事务,也是活跃事务,这部分事务也不可见。 处于 xmin <= txid < xmax 之间的未被 xip_list 指定的其它事务,是可见的,认为它们不活跃。

事务快照是由事务管理器提供的。在 Read Committed 隔离级别,事务在执行每条 SQL 时都会获取快照,在其它情况下( RR 或 SERIALIZABLE),事务只会在执行第一条 SQL 时获取一次快照。获取的快照用于元组的可见性检查,后面会讲到。

使用快照进行元组的可见性检查时,所有活跃事务都被看作处于 IN PROGRESS 状态,无论它们事实上是否已经提交或终止。

7. 可见性检查规则

可见性检查规则是一组规则,用于确定一条元组是否对一个事务可见,可见性检查规则会用到元组的 t_xmin、t_xmax, 提交日志 CLOG 以及获取的事务快照。

所选规则相当复杂,下面作简要阐述,一共有 10 条,可分为 3 种情况。

7.1 t_xmin 的状态为 ABORTED

t_xmin 表示最早仍活跃的事务 id, 元组的 t_xmin 通常表示插入此元组的事务 id, 如果在 CLOG 中它为 ABORTED,说明插入事务已终止,元组不可见。

规则1: /*创建元组的事务已终止*/
    if t_xmin status is ABORTED then
        return Invisiable
    end if

上述规则可写为如下数学表达式:

规则1: If Status(t_xmin) = ABORTED => Invisible

7.2 t_xmin 的状态为 IN_PROGRESS

t_xmin 状态为 IN_PROGRESS 的元组对当前事务而言基本上都是不可见的,除了以下例外:

规则2 : If Status(t_xmin) = IN_PROGRESS ^ t_xmin = current_txid ^ t_xmax = INVALID => Visible

也就是说,被当前事务正在插入,且尚未删除的元组,对当前事务而言是可见的。否则,都是不可见的。

不可见的情形对应与下面两个规则:

规则3 : If Status(t_xmin) = IN_PROGRESS ^ t_xmin = current_txid ^ t_xmax != INVALID => Invisible 规则 3 表示被当前事务正在插入,且已经删除的元组,对当前事务而言是不可见的。

规则4 : If Status(t_xmin) = IN_PROGRESS ^ t_xmin != current_txid => Invisible 规则 4 表示被不是当前事务正在插入的元组,对当前事务而言是不可见的。

7.3 t_xmin 的状态为 COMMITTED

t_xmin 状态为 COMMITTED 的元组对当前事务一般是可见的(符合规则 6,8,9),除了以下三种例外:

规则5: If Status(t_xmin) = COMMITTED ^ Snapshot(t_xmin) = active => Invisible

也就是说,插入该元组的事务(t_xmin)已提交,但是在当前事务获取的事务快照中它处于活跃状态(认为它是 IN_PROGRESS),此元组对于当前事务不可见;

规则7: If Status(t_xmin) = COMMITTED ^ Status(t_xmax) = IN_PROGRESS ^ t_xmax = current_txid => Invisible

也就是说,插入该元组的事务(t_xmin)已提交,且正在被当前事务删除,此元组对于当前事务也不可见;

规则10If Status(t_xmin) = COMMITED ^ Status(t_xmax) = COMMITTED ^ Snapshot(t_xmax) != active => Invisible

也就是说,插入该元组的事务(t_xmin)已提交,且元组已被删除,在当前事务获取的事务快照中删除元组的事务不处于活跃状态,删除有效,该元组对于当前事务来说不可见。

上面阐述了 t_xmin 状态为 COMMITTED (从 CLOG 中看),元组对于当前事务不可见的三种场景。

下面介绍一下,t_xmin 状态为 COMMITTED,元组对于当前事务来说可见的三种场景:

规则6:``````If Status(t_xmin) = COMMITTED ^ ( t_xmax = INVALID V Status(t_xmax) = ABORTED) => Visible```

也就是说,插入该元组的事务(t_xmin)已提交,元组未被删除或删除元组的事务处于 ABORTED 状态,该元组对当前事务可见。

规则8: If Status(t_xmin) = COMMITTED ^ Status(t_xmax) = IN_PROGRESS ^ t_xmax != current_txid => Visible

也就是说,插入该元组的事务(t_xmin)已提交,元组正在被非当前事务删除,该元组对当前事务处于可见状态。

规则9:``````If Status(t_xmin) = COMMITTED ^ Status(t_xmax) = COMMITTED ^ Snapshot(t_xmax) = active => Visible```

也就是说,插入该元组的事务(t_xmin)已提交,元组已被删除,但是在当前事务获取的事务快照中删除元组的事务处于活跃状态,认为删除无效,该元组对于当前事务仍可见。

8. 可见性检查

本节描述 PG 可见性检查的过程。可见性检查即如何为给定事务挑选堆元组的恰当版本。本节还介绍了 PG 是如何防止 脏读、可重复读以及幻读的。

8.1 可见性检查的过程

如图3 所示的场景中,SQL 命令按如下时序进行。

T1: 启动事务 txid = 200

T2: 启动事务 txid = 201

T3: 执行 txid =200 和 txid =201 事务的 SELECT 命令

T4: 执行 txid =200 事务的 UPDATE 命令

T5: 执行 txid =200和 txid =201 事务的 SELECT 命令

T6: 提交 txid =200事务

T7: 执行 txid =201 事务的 SELECT 命令

可见性检查一例.jpg

​ 图 3 可见性检查一例

txid =200 的事务的隔离级别为 Read Comitted, txid =201 事务的隔离级别为 Read Committed 或 Repeatable Read。

我们将研究 SELECT 命令是如何为每条元组执行可见性检查的。

  1. T3 的 SELECT 命令。

    根据规则6: (Tuple1) => Status(t_xmin:199) = COMMITTED ^ t_xmax = INVALID => Visible

    即 插入 Tuple1 的事务 txid:199 已提交,且未被删除,所以 Tuple1 对 txid:200 和 txid:201 都是可见的。所以 T3 时刻 txid:200 和 txid:201 的 查询结果都是 “Jekyll”, 即 Tuple1 的 user data 部分。

  2. T5 的 SELECT 命令。

    首先看一下 txid:200 事务执行的 SELECT 命令。根据规则 7, Tuple1 对 txid:200 不可见; 根据规则 2,Tuple2 对 txid:200 可见,因此该 SELECT 命令 返回 “Hyde”。

    • 规则7: (Tuple1) => Status(t_xmin:199) = COMMITTED ^ Status(t_xmax:200) = IN_PROGRESS ^ t_xmax:200 = current_txid:200 => Invisible
    • 规则2:(Tuple1) => Status(t_xmin:200) = IN_PROGRESS ^ t_xmin:200 = current_txid:200 ^ t_xmax = INVALID => Visible

    再看一下 txid:201 事务执行的 SELECT 命令。根据规则 8,Tuple1 对 txid:201 可见;根据规则4, Tuple2 对 txid:201 不可见,因此该 SELECT 命令返回 “Jekyll”。

    • 规则8:(Tuple1) => Status(t_xmin:199) = COMMITTED ^ Status(t_xmax:200) = IN_PROGRESS ^ t_xmax:200 != current_txid:201 => Visible
    • 规则4 : (Tuple2) => Status(t_xmin:200) = IN_PROGRESS ^ t_xmin:200 != current_txid:201 => Invisible Tuple2 正在被非当前事务(txid:200)插入,对于当前事务(txid:201)是不可见的。

    如果更新的元组在本事务提交之前能被别的事务看到,这种现象称为脏读,也称为写-读冲突。但如上所示,PG 中任何隔离级别都不会出现脏读。

  3. T7 的 SELECT 命令。

    1。如果 txid:201 事务处于 RC 隔离级别:

    由于 txid:200 已提交,所以在这个时间点 txid:201 获取的事务快照是 "201:201:", 此时根据规则10 Tuple1 对于 txid:201 不可见,根据规则6 Tuple2 对于 txid 可见, SELECT 命令 返回 “Hyde”。

    • 规则10(Tuple1) => Status(t_xmin:199) = COMMITED ^ Status(t_xmax:200) = COMMITTED ^ Snapshot(t_xmax:200) != active => Invisible
    • 规则6:(Tuple2) => Status(t_xmin:200) = COMMITTED ^ ( t_xmax = INVALID) => Visible

    这里注意到,txid:201 的 SELECT 命令,在 txid:200 事务提交前后的三个时刻返回结果是不一样的,这种现象称之为 不可重复读。

    2。如果 txid:201 事务处于 RR 隔离级别:

    由于 RR 隔离级别下,事务只在执行第一条 SQL 时获取快照,所以可见性检查的结果必然和 RC 级别下不一样。根据规则9,Tuple1 可见。 根据 规则5, Tuple2 不可见。所以返回结果是“Jekyll”。

    • 规则9:(Tuple1) => Status(t_xmin:199) = COMMITTED ^ Status(t_xmax:200) = COMMITTED ^ Snapshot(t_xmax:200) = active => Visible
    • 规则5: (Tuple2) => Status(t_xmin:200) = COMMITTED ^ Snapshot(t_xmin:200) = active => Invisible

    请注意,在 RR 和 SERIALIZABLE 隔离级别下,不会发生可重复读。

提示位(Hint Bits):

PG 在内部提供了三个函数 TransactionIdIsInProgress、TransactionIdDidCommit 和 TransactionDidAbort,用于获取事务的状态。这些函数被设计为尽可能地减少对 CLOG 的频繁访问。尽管如此,如果在检查每条元组时都执行这些函数,那么很可能产生一个性能瓶颈。

为解决这个问题,PG 使用了 提示位(Hint bits),如下所示:

#define HEAP_XMIN_COMMITTED     0x0100  /* 元组 xmin 对应事务已提交 */
#define HEAP_XMIN_INVALID       0x0200  /* 元组 xmin 对应事务无效 */
#define HEAP_XMAX_COMMITTED     0x0400  /* 元组 xmax 对应事务已提交 */
#define HEAP_XMAX_INVALID       0x0800  /* 元组 xmax 对应事务无效/终止 */

在读取或写入元组时, PG 会择机将提示位设置到元组的 t_informask 字段中。如果设置了提示位,则不再需要调用函数来获取事务的状态。因此 PG 能高效地检查每个元组 t_xmin 和 t_xmax 对应事务地状态。

8.2 PG 的 RR 级别可以防止幻读

PG 由于使用 SI 实现 MVCC,在原则上,快照隔离不允许出现幻读。

假设两个事务 Tx_A 和 Tx_B 同时运行,它们的隔离级别分别是 RC 和 RR, txid 分别是 100 和 101。两个事务一前一后接连开始,首先 Tx_A 插入一条元组,并提交。接着 Tx_B 执行 SELECT 命令,根据规则5:(new Tuple) => Status(t_xmin:100) = COMMITTED ^ Snapshot(t_xmin:100) = active => Invisible, Tx_A 插入的元组 对于Tx_B 而言是不可见的。这是因为 Tx_B 获取的事务快照是”100:100:“, 在 Tx_B 的事务快照中, txid:100 是活跃的,因此新插入的元组(其t_xmin=100)对于 Tx_B 不可见。

就这样,PG 的 RR 隔离级别通过上面的机制防止了幻读。

9. 防止丢失更新

丢失更新,又称 写-写 冲突,是事务并发更新同一行时所发生的异常, RR 和 SERIALIZABLE 隔离级别必须阻止该异常的出现。本节介绍 PG 是如何防止丢失更新的。

9.1 并发 UPDATE 的行为

执行 update 时,PG 内部实际调用了 ExecUpdate 函数,该函数主要为每个待更新的目标行执行更新操作。对于每一行:

  1. 如果目标行正在被另一个事务更新。

    这种情况下当前事务必须等待更新目标行的事务结束。因为 PG 的 SI 实现采用已先更新者为准的方案。例如,假设 Tx_A 和 Tx_B 同时运行,且 Tx_B 尝试更新一行,但 Tx_A 已更新了这一行,且仍在进行中,这种情况下, Tx_B 会等待 Tx_A 的结束。

    Tx_A 结束后,Tx_B 结束等待,继续进行,如果当前事务(Tx_B)处于 RC 隔离级别,则会立即更新目标行;若处于 RR 隔离级别 或 SERIALIZABLE 隔离级别,当前事务会立即终止,以防止更新丢失。

  2. 如果目标行已经被另一个并发事务所更新。

    当前事务视图更新某一行,但是另一个并发事务已经更新了该行并提交。这种情况下,如果当前事务处于 RC 隔离级别,则会立即更新目标行。否则会立即终止当前事务,以防止目标更新。

  3. 没有冲突,当前事务可以直接更新目标行。

以先更新者为准 / 以先提交者为准

PG 基于 SI 的并发控制机制采用以先更新者为准的方案。

PG 的 SSI 实现使用以先提交者为准的方案。

10. 可串行化快照隔离

从 9.1 开始,可串行化快照隔离( SSI )已内嵌到快照隔离( SI )中,以实现真正的可串行化隔离(SERIALIZABLE)等级。

10.1 SSI 实现的基本策略

如果前趋图中存在环,则会出现串行化异常。这里用一种最简单的异常来解释,即写偏差。

写偏差的调度.jpg

​ 图 4 写偏差的调度

上图描述了一种调度方式:Transaction A 读取 Tuple_B, Transaction B 读取了 Tuple_A, 然后 Transaction A 写 Tuple_A, Transaction B 写 Tuple_B。这时会出现两个写-读冲突,它们在该调度的前趋图中构成了一个环: Tx_A ----> Tx_B -----> Tx_A。

从概念讲,存在三种冲突:写-读冲突(脏读)、写-写冲突(丢失更新)、读-写冲突。在 SSI 出现前,PG 可以防止前两个冲突,SSI 的出现解决得了读-写冲突。

PG 的 SSI 实现采用了以下策略:

  • 使用 SIREAD 锁记录事务访问的所有对象(元组、页面和关系)
  • 当写入任何堆元组 / 索引元组时,使用 SIREAD 锁检测读-写冲突
  • 如果从 读-写 冲突中检测出串行化异常,则中止事务

10.2 PG 的 SSI 实现

为了实现上述策略,PostgreSQL实现了很多数据结构与函数。但这里我们只会使用两种数据结构:SIREAD锁与读-写冲突来描述SSI机制。它们都存储在共享内存中。

为简单起见,本文省略了一些重要的数据结构,例如 SERIALIZABLEXACT。因此对CheckTargetForConflictOut 、 CheckTargetForConflictIn 和PreCommit_CheckForSerialization Failure等函数的解释也极为简化。比如本文虽然指出哪些函数能检测到冲突,但并没有详细解释如何检测冲突。如果读者想了解详细信息,请参阅源代码:predicate.c。

SIREAD 锁:

SIREAD 锁,在内部又称之为 谓词锁,是一个由对象与(虚拟)事务标识构成的二元组;这个二元组存储着哪些事务访问了哪些对象的相关信息。

在 SERIALIZABLE 隔离级别下,只要执行 DML 命令,就会通过 CheckTargetForConflictsOut 函数创建出 SIREAD 锁。举个例子,如果 txid:100 的事务读取给定表的 Tuple1, 则会创建一个 SIREAD 锁 {Tuple1, {100}} 。如果其它事务,例如 txid:101 读取了 Tuple1, 那么 SIREAD 锁便会被更新为 {Tuple1, {100,101}}。 注意,读取索引页也会创建 SIREAD 锁,因为使用 Heap Only Idex Scan 时,数据库只会读取索引页,而不读取表页。

SIREAD 锁有元组、页、关系三个级别。如果单个页面上所有元组的 SIREAD 锁都被创建,会聚合为该页上的 SIREAD 锁,原有相关元组上的SIREAD锁都会被释放(删除),以减少内存空间占用。对读取的页面也是如此。

当为索引创建SIREAD锁时,一开始会创建页级别的SIREAD锁。当使用顺序扫描时,无论是否存在索引,是否存在WHERE子句,一开始都会创建关系级别的SIREAD锁。注意,在某些情况下,这种实现可能会导致串行化异常的误报,即假阳性,具体细节将在xx节解释。

读-写 冲突:

读-写冲突是一个三元组,由 SIREAD 锁和两个分别读写该 SIREAD 锁的事务的txid 构成。当在可串行化模式下执行INSERT、UPDATE 或DELETE 命令时,函数CheckTargetForConflictsIn 会被调用,并检查SIREAD锁来检测是否存在冲突,如果有就创建一个读-写冲突。

举个例子,假设 txid=100 的事务读取了 Tuple_1,然后 txid=101 的事务更新了 Tuple_1。在这种情况下,txid=101 的事务中的 UPDATE 命令会调用 CheckTargetForConflictsIn 函数,并检测到在Tuple_1上存在txid=100,101之间的读-写冲突,并创建rw-conflict {r= 100, w = 101, {Tuple_1}}。

CheckTargetForConflictOut、CheckTargetForConflictIn函数,以及在可串行化模式中执行COMMIT命令会触发的PreCommit_CheckForSerializationFailure函数,都会使用创建的读-写冲突来检查串行化异常。如果它们检测到异常,则只有先提交的事务会真正提交,其他事务才会中止。

10.3 SSI 的原理

本节将描述 SSI 如何解决写偏差异常,下面将使用一个简单的表tbl为例。

create table tbl(id int primary key, flag bool default false);
insert into tbl(id) select generate_series(1,2000); 
analyze;

事务 Tx_A 和 Tx_B 执行以下命令,如下图所示。

可见性检查一例.jpg

​ 图 5 写偏差一例

假设所有命令都使用索引扫描。当执行命令时,它们会同时读取堆元组与索引页,每个索引页都包含指向相应堆元组的索引元组,如图6所示:

[图片上传失败...(image-b9a58b-1597735166627)]

​ 图 6 索引和表的关系

索引和表的关系如下:

T1:Tx_A 执行 SELECT 命令,该命令读取堆元组 Tuple_2000,以及包含主键的索引页 Pkey_2。

T2:Tx_B 执行 SELECT 命令,该命令读取堆元组 Tuple_1,以及包含主键的索引页 Pkey_1。

T3:Tx_A 执行 UPDATE 命令,更新 Tuple_1。

T4:Tx_B 执行 UPDATE 命令,更新 Tuple_2000。

T5:Tx_A 提交。

T6:Tx_B 提交,然而由于写偏差异常而被中止。

图 7 展示了 PostgreSQL 如何检测和解决上述场景中描述的写偏差异常。

SIREAD锁与读-写冲突及调度.png

​ 图 7 PostgreSQL 如何检 测和解决上述场景中描述的写偏差异常

T1:执行 Tx_A 的 SELECT 命令时,CheckTargetForConflictsOut 会创建 SIREAD 锁。在本例中该函数会创建两个SIREAD锁:L1 与 L2。L1 和 L2 分别与 Pkey_2 和 Tuple_2000 相关联。

T2:执行 Tx_B 的 SELECT 命令时,CheckTargetForConflictsOut 会创建两个 SIREAD 锁:L3 和 L4。L3 和 L4分别与Pkey_1 和 Tuple_1 相关联。

T3:执行 Tx_A 的 UPDATE 命令时,CheckTargetForConflictsOut 和 CheckTargetForConflictsIN 会分别在 ExecUpdate 执行前后被调用。在本例中,CheckTarget ForConflictsOut什么都不做。CheckTargetForConflictsIn 则会创建读-写冲突 C1,这是 Tx_B 和 Tx_A 在 Pkey_1 和 Tuple_1 上的冲突,因为Pkey_1 和 Tuple_1 都由 Tx_B 读取并被 Tx_A 写入。

T4:执行 Tx_B 的 UPDATE 命令时,CheckTargetForConflictsIn 会创建读-写冲突 C2,这是 Tx_A 与 Tx_B 在Pkey_2 和 Tuple_2000 上的冲突。在这种情况下,C1 和 C2 在前趋图中构成一个环,因此 Tx_A 和 Tx_B 处于不可串行化状态。但事务Tx_A和Tx_B都尚未提交,因此 CheckTargetForConflictsIn 不会中止 Tx_B 。注意,这是因为 PostgreSQL 的 SSI 实现采用先提交者为准方案。

T5:当 Tx_A 尝试提交时,将调用 PreCommit_CheckForSerializationFailure。此函数可以检测串行化异常,并在允许的情况下执行提交操作。因为Tx_B 仍在进行中,所以 Tx_A 成功提交。

T6:当 Tx_B 尝试提交时,PreCommit_CheckForSerializationFailure 检测到串行化异常,且 Tx_A 已经提交,因此 Tx_B 被中止。此外,如果在 Tx_A 提交之后( T5 时刻),Tx_B 执行了 UPDATE 命令,则 Tx_B 会立即中止。因为 Tx_B 的 UPDATE 命令会调用 CheckTargetForConflictsIn,并检测到串行化异常。

如果 Tx_B 在 T6 时刻执行 SELECT 命令而不是 COMMIT 命令,则 Tx_B也会立即中止。因为 Tx_B 的 SELECT 命令调用的 CheckTargetForConflictsOut 会检测到串行化异常。

10.4 假阳性的串行化异常

在可串行化模式下,因为永远不会检测到假阴性(发生异常但未检测到)串行化异常, PostgreSQL 能始终完全保证并发事务的可串行性。但是在某些情况下,可能会检测到假阳性异常(没有发生异常但误报发生),用户在使用 SERIALIZABLE 模式时应牢记这一点。下文会描述 PostgreSQL 检测到假阳性异常的情况。

图8 展示了发生假阳性串行化异常的场景。

发生假阳性串行化异常的场景.png

​ 图 8 假阳性串行化异常一例

当使用顺序扫描时,如 SIREAD 锁的解释中所述,PostgreSQL 创建了一个关系级的 SIREAD 锁。图9(1)展示了 PostgreSQL 使用顺序扫描时的 SIREAD 锁和读-写冲突。在这种情况下,产生了与 tbl 表上 SIREAD 锁相关联的读-写冲突:C1 和 C2,它们在前趋图中构成了一个环。因此会检测到假阳性的写偏差异常,如图9(2)所示。虽然实际上没有冲突,但是 Tx_A 与 Tx_B 两者之一也将被中止。

假阳性异常(1)——使用顺序扫描.png

​ (1) SIREAD 锁和读-写冲突 (2)假阳性的写偏差调度

​ 图 9 假阳性串行化异常下的SIREAD 锁和读-写冲突以及写偏差调度

即使使用索引扫描,如果事务 Tx_A 和 Tx_B 都获取了相同的索引 SIREAD 锁,PostgreSQL 也会误报假阳性异常。图 10 展示了这种情况。假设索引页 Pkey_1 包 含两条索引项,其中一条指向 Tuple_1,另一条指向 Tuple_2。当 Tx_A 和 Tx_B 执行相应的 SELECT 和 UPDATE 命令时,Pkey_1 同时被 Tx_A 和 Tx_B 读取与写入。这时候会产生与 Pkey_1 相关联的读-写冲突:C1 和 C2,并在前趋图中构成一个环,因而检测到假阳性写偏差异常。如果 Tx_A 和 Tx_B 获取不同索引页上的 SIREAD 锁,则不会误报,并且两个事务都可以提交。

假阳性异常(2)——使用相同索引页的索引扫描.png

​ (1) 表和索引之间的关系 (2)SIREAD 锁 和 读-写冲突

​ 图 10 假阳性异常(2)——使用相同索引页的索引扫描

11. 需要的维护进程

PostgreSQL的并发控制机制需要以下维护过程。

  1. 删除死元组及指向死元组的索引元组。

  2. 移除提交日志中非必要的部分。

  3. 冻结旧的事务标识。

  4. 更新 FSM、VM 及统计信息。

第 3 和第 4 节分别介绍了为什么需要第一个和第二个过程。第三个过程与事务标识回卷问题有关,本小节将概述事务标识回卷问题。在PostgreSQL中,清理(VACUUM)过程负责这些过程。清理过程将在下一章中介绍。

在第 2 节中简要介绍了事务回卷的问题。

为了解决这个问题,PostgreSQL 引入了一个冻结事务标识的概念,并实现了一个名为FREEZE的过程。

在 PostgreSQL中定义了一个冻结的 txid,它是一个特殊的保留值 txid = 2,在参与事务标识大小比较时,它总是比所有其他txid 都旧。换句话说,冻结的 txid 始终处于非活跃状态,且其结果对其他事务始终可见。

清理( VACUUM )过程会调用冻结( FREEZE )过程。冻结过程将扫描所有表文件,如果元组的 t_xmin 比当前 txid- vacuum_freeze_min_age(默认值为5000万)更旧,则将该元组的 t_xmin 重写为冻结事务标识。在下一章中会有更详细的解释。

举个例子,如图 11 所示,当前txid 为 5000万,此时通过 VACUUM 命令调用冻结过程。在这种情况下,Tuple_1 和 Tuple_2的 t_xmin 都被重写为 2。在版本 9.4 或更高版本中使用元组 t_infomask 字段中的XMIN_FROZEN 标记位来标识冻结元组,而不是将元组的 t_xmin 重写为冻结的 txid,如图11(2)所示。

冻结过程.png

​ 图 11 冻结过程

你可能感兴趣的:(PostgreSQL的并发控制)