PostgreSQL的存储系统二:REDOLOG文件存储结构二

前端之家收集整理的这篇文章主要介绍了PostgreSQL的存储系统二:REDOLOG文件存储结构二前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

REDOLOG@H_301_3@@H_301_3@文件里的用户数据和数据文件里的用户数据存储结构相同@H_301_3@

@H_301_3@

几个月前同事给台湾一家公司培训《pg9 ad admin@H_301_3@》时,有个学员提及WAL@H_301_3@里记录的内容Query@H_301_3@时的sql@H_301_3@语句(比如insert@H_301_3@等),同事告知WAL@H_301_3@里记录的tuple@H_301_3@信息,而非sql@H_301_3@,该学员坚持里面是sql@H_301_3@或sql+tuple@H_301_3@,并说oracle@H_301_3@的redo@H_301_3@日志里记录的是sql@H_301_3@(不知到这个从哪里知道的,也许是日志挖掘出来sql@H_301_3@的缘由吧)。便看了一下源码(还是开源的好)。

@H_301_3@前面我写过一篇文章Postgresql@H_301_3@的存储系统二:REDOLOG@H_301_3@文件存储结构》,见地址http://beigang.iteye.com/blog/1565121@H_301_3@或http://blog.csdn.net/beiigang/article/details/7680905@H_301_3@,其中提到Pg XLOG@H_301_3@文件的存储格式大致如下:

<@H_301_3@PageHeaderData@H_301_3@>

<XLogRecord>@H_301_3@

<rmgr-specific data>@H_301_3@

<BkpBlock>@H_301_3@

<XLogRecData>@H_301_3@里面包括<CheckPoint>@H_301_3@等

<BkpBlock>@H_301_3@

<XLogRecData>@H_301_3@

<BkpBlock>@H_301_3@

<XLogRecData>@H_301_3@

……

用户相关的数据写在XLogRecData@H_301_3@结构(定义见下面)的buffer@H_301_3@成员里,但具体写成什么样子没有提及,正好这儿再深入讨论一下。

typedef@H_301_3@struct@H_301_3@ XLogRecData@H_301_3@

{@H_301_3@

@H_301_3@char@H_301_3@ @H_301_3@ *data@H_301_3@; @H_301_3@/* @H_301_3@资源管理器包含数据的开始@H_301_3@ */@H_301_3@

@H_301_3@uint32@H_301_3@ @H_301_3@len@H_301_3@; @H_301_3@/* @H_301_3@资源管理器包含数据的长度@H_301_3@ */@H_301_3@

@H_301_3@Buffer@H_301_3@ @H_301_3@buffer@H_301_3@; @H_301_3@/* @H_301_3@有相应数据的@H_301_3@buffer@H_301_3@,如果有的话@H_301_3@ */@H_301_3@

@H_301_3@bool@H_301_3@ @H_301_3@buffer_std@H_301_3@; @H_301_3@/* buffer@H_301_3@是否有标准@H_301_3@pd_lower/pd_upper@H_301_3@头@H_301_3@ */@H_301_3@

@H_301_3@struct@H_301_3@ XLogRecData *@H_301_3@next@H_301_3@; @H_301_3@/* @H_301_3@链里的下一个结构@H_301_3@ */@H_301_3@

} @H_301_3@XLogRecData@H_301_3@;@H_301_3@

@H_301_3@

@H_301_3@为了说清楚这个问题,跑了个例子如下:

@H_301_3@INSERT INTO TABLE1(ID,GNAME) VALUES(18,’GangBei’);

@H_301_3@看这个例子涉及的调用流程前,先回顾一下pg@H_301_3@服务进程的调用流程,一切就绪后进入无限循环,等候客户端指令,


@H_301_3@

Postgres服务进程调用流程图

这个例子的调用流程和《Postgresql服务过程中的那些事二:Pg服务进程处理简单查询》系列博文中的流程大致相同,也是调用exec_simple_query方法,和前面《Postgresql服务过程中的那些事二:Pg服务进程处理简单查询》中select例子不同的是,本节中insert的例子在portalrun方法调用了执行器的ExecInsert方法,最终调用了heap_insert方法,在这个方法里完成了记录写入数据文件,并调用了XLogInsert方法,完成了XLOG的WAL日志写入。更具体的方法调用流程参见下面的调用流程图,其他和《Postgresql服务过程中的那些事二:Pg服务进程处理简单查询》基本相同的部分略去。



Insert sql @H_301_3@语句调用流程@H_301_3@

在heap_insert方法里,组装好tuple,调用RelationGetBufferForTuple方法找到shmem里缓存数据文件块的buffer,调用RelationPutHeapTuple方法,把组装好的元组放到合适的buffer中合适的位置;然后组装XLogRecData类型变量rdata,把buffer赋给XLogRecData的成员buffer,接着调用XLogInsert方法,并传入rdata,在XLogInsert方法里,用memcpy方法把@H_301_3@rdata@H_301_3@写入@H_301_3@shmem@H_301_3@对应的@H_301_3@cache@H_301_3@里,最后@H_301_3@pg@H_301_3@都是通过操作系统接口@H_301_3@I/O@H_301_3@接口把@H_301_3@WAL@H_301_3@日志和数据写入对应的文件。@H_301_3@

既然@H_301_3@XLOG@H_301_3@里写的@H_301_3@ Insert@H_301_3@的@H_301_3@wal@H_301_3@日志@H_301_3@里的用户数据和数据文件中的一样,那我们简单看一下@H_301_3@pg@H_301_3@中数据文件里的@H_301_3@tuple@H_301_3@,@H_301_3@tuple@H_301_3@存放在堆中,一个@H_301_3@tuple@H_301_3@就是一行表记录,在数据文件的页里存放的结构如下图:@H_301_3@



数据文件页面布局图@H_301_3@




元组结构图@H_301_3@

@H_301_3@@H_301_3@

元组头结构和其字段表示意义见下面:@H_301_3@

typedef@H_301_3@struct@H_301_3@ HeapTupleHeaderData@H_301_3@@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@union@H_301_3@

@H_301_3@ {@H_301_3@

@H_301_3@@H_301_3@HeapTupleFields@H_301_3@t_heap@H_301_3@;@H_301_3@

@H_301_3@@H_301_3@DatumTupleFields@H_301_3@t_datum@H_301_3@;@H_301_3@

@H_301_3@ } @H_301_3@t_choice@H_301_3@;@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@ItemPointerData@H_301_3@t_ctid@H_301_3@; @H_301_3@/* current TID of this or newer tuple */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/* Fields below here must match MinimalTupleData! */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@uint16@H_301_3@ @H_301_3@t_infomask2@H_301_3@; @H_301_3@/* number of attributes + varIoUs flags */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@uint16@H_301_3@ @H_301_3@t_infomask@H_301_3@; @H_301_3@/* varIoUs flag bits,see below */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@uint8@H_301_3@ @H_301_3@t_hoff@H_301_3@; @H_301_3@/* sizeof@H_301_3@ header incl@H_301_3@. bitmap,padding */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/* ^ - 23 bytes - ^ */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@bits8@H_301_3@ @H_301_3@t_bits@H_301_3@[1]; @H_301_3@/* bitmap of NULLs -- VARIABLE LENGTH */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/* MORE DATA FOLLOWS AT END OF STRUCT */@H_301_3@

} @H_301_3@HeapTupleHeaderData@H_301_3@;@H_301_3@@H_301_3@

@H_301_3@@H_301_3@

typedef@H_301_3@struct@H_301_3@ HeapTupleFields@H_301_3@@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@TransactionId@H_301_3@t_xmin@H_301_3@; @H_301_3@/* inserting xact@H_301_3@ ID */@H_301_3@

@H_301_3@ @H_301_3@TransactionId@H_301_3@t_xmax@H_301_3@; @H_301_3@/* deleting or locking xact@H_301_3@ ID */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@union@H_301_3@

@H_301_3@ {@H_301_3@

@H_301_3@@H_301_3@CommandId@H_301_3@ @H_301_3@t_cid@H_301_3@; @H_301_3@/* inserting or deleting command ID,or both */@H_301_3@

@H_301_3@@H_301_3@TransactionId@H_301_3@t_xvac@H_301_3@; @H_301_3@/* old-style VACUUM FULL xact@H_301_3@ ID */@H_301_3@

@H_301_3@ } @H_301_3@t_field3@H_301_3@;@H_301_3@

} @H_301_3@HeapTupleFields@H_301_3@;@H_301_3@@H_301_3@

@H_301_3@@H_301_3@

typedef@H_301_3@struct@H_301_3@ DatumTupleFields@H_301_3@@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@int32@H_301_3@ @H_301_3@datum_len_@H_301_3@; @H_301_3@/* varlena@H_301_3@ header (do not touch directly!) */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@int32@H_301_3@ @H_301_3@datum_typmod@H_301_3@; @H_301_3@/* -1,or identifier of a record type */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@Oid@H_301_3@ @H_301_3@datum_typeid@H_301_3@; @H_301_3@/* composite type OID,or RECORDOID */@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/*@H_301_3@

@H_301_3@ @H_301_3@* Note: field ordering is chosen with thought that Oid@H_301_3@ might someday

@H_301_3@ @H_301_3@* widen to 64 bits.

@H_301_3@ @H_301_3@*/

} @H_301_3@DatumTupleFields@H_301_3@;@H_301_3@@H_301_3@

@H_301_3@@H_301_3@

typedef@H_301_3@struct@H_301_3@ ItemPointerData@H_301_3@@H_301_3@@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@BlockIdData@H_301_3@ip_blkid@H_301_3@;@H_301_3@

@H_301_3@ @H_301_3@OffsetNumber@H_301_3@ip_posid@H_301_3@;@H_301_3@

}@H_301_3@@H_301_3@

@H_301_3@@H_301_3@

Postgresql@H_301_3@的元组头结构是MVCC@H_301_3@算法的基础。这个以后再说吧。

@H_301_3@

下面把@H_301_3@heap_insert@H_301_3@方法XLogInsert@H_301_3@@H_301_3@方法贴到了下面,为了突显主题,删掉了其余代码,并把@H_301_3@XLOG@H_301_3@@H_301_3@内容相关变量和方法置为红色,方便串读。@H_301_3@

@H_301_3@

Oid@H_301_3@@H_301_3@

heap_insert(Relation relation,HeapTuple @H_301_3@tup@H_301_3@,CommandId cid,@H_301_3@@H_301_3@

@H_301_3@@H_301_3@int@H_301_3@ options,BulkInsertState bistate)@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ TransactionId xid = GetCurrentTransactionId();@H_301_3@

@H_301_3@ HeapTuple @H_301_3@heaptup;

@H_301_3@ Buffer @H_301_3@buffer;

@H_301_3@ bool @H_301_3@all_visible_cleared = false;

@H_301_3@

/*1 @H_301_3@ @H_301_3@组装元组头信息@H_301_3@ */@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@tup@H_301_3@->t_data->t_infomask &= ~(HEAP_XACT_MASK);@H_301_3@

@H_301_3@ @H_301_3@tup@H_301_3@->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);@H_301_3@

@H_301_3@ @H_301_3@tup@H_301_3@->t_data->t_infomask |= HEAP_XMAX_INVALID;@H_301_3@

@H_301_3@ HeapTupleHeaderSetXmin(@H_301_3@tup@H_301_3@->t_data,xid);@H_301_3@

@H_301_3@ HeapTupleHeaderSetCmin(@H_301_3@tup@H_301_3@->t_data,cid);@H_301_3@

@H_301_3@ HeapTupleHeaderSetXmax(@H_301_3@tup@H_301_3@->t_data,0); @H_301_3@

@H_301_3@ @H_301_3@tup@H_301_3@->t_tableOid = RelationGetRelid(relation);@H_301_3@

@H_301_3@

@H_301_3@@H_301_3@heaptup@H_301_3@ = @H_301_3@ tup@H_301_3@;@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/*2 Find buffer to insert this tuple into */@H_301_3@

@H_301_3@ @H_301_3@buffer@H_301_3@ = RelationGetBufferForTuple(relation,@H_301_3@heaptup@H_301_3@->t_len,@H_301_3@

@H_301_3@ @H_301_3@InvalidBuffer,options,bistate);

@H_301_3@

@H_301_3@ @H_301_3@/*3@H_301_3@

@H_301_3@ @H_301_3@* We're about to do the actual insert -- check for conflict at the

@H_301_3@ @H_301_3@* relation or buffer level first,to avoid possibly having to roll back

@H_301_3@ @H_301_3@* work we've just done.

@H_301_3@ @H_301_3@*/

@H_301_3@ CheckForSerializableConflictIn(relation,NULL,@H_301_3@buffer@H_301_3@);@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/*4 NO EREPORT(ERROR) from here till changes are logged */@H_301_3@

@H_301_3@ START_CRIT_SECTION();@H_301_3@

@H_301_3@ RelationPutHeapTuple(relation,@H_301_3@buffer@H_301_3@,heaptup)@H_301_3@

@H_301_3@ MarkBufferDirty(@H_301_3@buffer@H_301_3@);@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/* XLOG stuff */@H_301_3@

@H_301_3@ @H_301_3@if@H_301_3@ (!(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation))@H_301_3@

@H_301_3@ {@H_301_3@

@H_301_3@xl_heap_insert xlrec;@H_301_3@

@H_301_3@ @H_301_3@xl_heap_header xlhdr;

@H_301_3@XLogRecPtr @H_301_3@recptr;

@H_301_3@XLogRecData rdata[3];@H_301_3@

@H_301_3@Page @H_301_3@page@H_301_3@ = BufferGetPage(@H_301_3@buffer@H_301_3@);@H_301_3@

@H_301_3@uint8 @H_301_3@info = XLOG_HEAP_INSERT;

@H_301_3@

@H_301_3@xlrec.all_visible_cleared = all_visible_cleared;@H_301_3@

@H_301_3@xlrec.target.node = relation->rd_node;@H_301_3@

@H_301_3@xlrec.target.tid = heaptup->t_self;@H_301_3@

@H_301_3@rdata[0].data = (@H_301_3@char@H_301_3@ *) &xlrec;@H_301_3@

@H_301_3@rdata[0].len = SizeOfHeapInsert;@H_301_3@

@H_301_3@rdata[0].buffer = InvalidBuffer;@H_301_3@

@H_301_3@rdata[0].next = &(rdata[1]);@H_301_3@

@H_301_3@

@H_301_3@xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;@H_301_3@

@H_301_3@xlhdr.t_infomask = heaptup->t_data->t_infomask;@H_301_3@

@H_301_3@xlhdr.t_hoff = heaptup->t_data->t_hoff;@H_301_3@

@H_301_3@

@H_301_3@@H_301_3@/*@H_301_3@

@H_301_3@@H_301_3@* note we mark rdata[1] as belonging to buffer; if XLogInsert decides

@H_301_3@@H_301_3@* to write the whole page to the xlog,we don't need to store

@H_301_3@@H_301_3@* xl_heap_header in the xlog.

@H_301_3@@H_301_3@*/

@H_301_3@rdata[1].data = (@H_301_3@char@H_301_3@ *) &xlhdr;@H_301_3@

@H_301_3@rdata[1].len = SizeOfHeapHeader;@H_301_3@

@H_301_3@rdata[1].buffer = buffer;@H_301_3@

@H_301_3@rdata[1].buffer_std = true;@H_301_3@

@H_301_3@rdata[1].next = &(rdata[2]);@H_301_3@

@H_301_3@

@H_301_3@@H_301_3@/* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */@H_301_3@

@H_301_3@rdata[2].data = (@H_301_3@char@H_301_3@ *) heaptup->t_data + offsetof(HeapTupleHeaderData,t_bits);@H_301_3@

@H_301_3@rdata[2].len = heaptup->t_len - offsetof(HeapTupleHeaderData,t_bits);@H_301_3@

@H_301_3@rdata[2].buffer = buffer;@H_301_3@

@H_301_3@rdata[2].buffer_std = true;@H_301_3@

@H_301_3@rdata[2].next = NULL;@H_301_3@

@H_301_3@

@H_301_3@recptr = @H_301_3@XLogInsert@H_301_3@(RM_HEAP_ID,info,@H_301_3@rdata@H_301_3@);@H_301_3@

@H_301_3@

@H_301_3@PageSetLSN(page,recptr);@H_301_3@

@H_301_3@PageSetTLI(page,ThisTimeLineID);@H_301_3@

@H_301_3@ }@H_301_3@

@H_301_3@

@H_301_3@ END_CRIT_SECTION();@H_301_3@

@H_301_3@

@H_301_3@ UnlockReleaseBuffer(buffer);@H_301_3@

@H_301_3@

@H_301_3@

@H_301_3@ pgstat_count_heap_insert(relation);@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@return@H_301_3@ HeapTupleGetOid(tup);@H_301_3@

}@H_301_3@@H_301_3@

@H_301_3@

@H_301_3@

XLogRecPtr@H_301_3@@H_301_3@

XLogInsert@H_301_3@(RmgrId rmid,uint8 info,@H_301_3@XLogRecData *rdata@H_301_3@)@H_301_3@@H_301_3@

{@H_301_3@@H_301_3@

@H_301_3@ XLogCtlInsert *Insert = &XLogCtl->Insert;@H_301_3@

@H_301_3@ XLogRecord *record;@H_301_3@

@H_301_3@ XLogContRecord *contrecord;@H_301_3@

@H_301_3@ XLogRecPtr @H_301_3@RecPtr;

@H_301_3@ XLogRecPtr @H_301_3@WriteRqst;

@H_301_3@ uint32 @H_301_3@freespace;

@H_301_3@ @H_301_3@int@H_301_3@ @H_301_3@curridx;

@H_301_3@ XLogRecData *rdt;@H_301_3@

@H_301_3@ Buffer @H_301_3@dtbuf[XLR_MAX_BKP_BLOCKS];

@H_301_3@ bool @H_301_3@dtbuf_bkp[XLR_MAX_BKP_BLOCKS];

@H_301_3@ BkpBlock @H_301_3@dtbuf_xlg[XLR_MAX_BKP_BLOCKS];

@H_301_3@ XLogRecPtr @H_301_3@dtbuf_lsn[XLR_MAX_BKP_BLOCKS];

@H_301_3@ XLogRecData dtbuf_rdt1[XLR_MAX_BKP_BLOCKS];@H_301_3@

@H_301_3@ XLogRecData dtbuf_rdt2[XLR_MAX_BKP_BLOCKS];@H_301_3@

@H_301_3@ XLogRecData dtbuf_rdt3[XLR_MAX_BKP_BLOCKS];@H_301_3@

@H_301_3@ pg_crc32 @H_301_3@rdata_crc;

@H_301_3@ uint32 @H_301_3@len,

@H_301_3@write_len;@H_301_3@

@H_301_3@ @H_301_3@unsigned@H_301_3@ @H_301_3@i;

@H_301_3@

@H_301_3@ TRACE_POSTGREsql_XLOG_INSERT(rmid,info);@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/*@H_301_3@

@H_301_3@ @H_301_3@* Here we scan the rdata chain,determine which buffers must be backed

@H_301_3@ @H_301_3@* up,and compute the CRC values for the data.

@H_301_3@ @H_301_3@*/

@H_301_3@

@H_301_3@ START_CRIT_SECTION();@H_301_3@

@H_301_3@ @H_301_3@/* Now wait to get insert lock */@H_301_3@

@H_301_3@ LWLockAcquire(WALInsertLock,LW_EXCLUSIVE);@H_301_3@

@H_301_3@ @H_301_3@/* Compute record's XLOG location */@H_301_3@

@H_301_3@ curridx = Insert->curridx;@H_301_3@

@H_301_3@ INSERT_RECPTR(RecPtr,Insert,curridx);@H_301_3@

@H_301_3@

@H_301_3@ @H_301_3@/*@H_301_3@

@H_301_3@ @H_301_3@* Append the data,including backup blocks if any

@H_301_3@ @H_301_3@*/

@H_301_3@ @H_301_3@/* @H_301_3@把@H_301_3@rdata@H_301_3@@H_301_3@中的数据写入@H_301_3@XLOG@H_301_3@ */@H_301_3@@H_301_3@

@H_301_3@ @H_301_3@while@H_301_3@ (write_len)@H_301_3@

@H_301_3@ {@H_301_3@

@H_301_3@@H_301_3@while@H_301_3@ (rdata->data == NULL)@H_301_3@

@H_301_3@rdata = rdata->next;@H_301_3@

@H_301_3@

@H_301_3@@H_301_3@if@H_301_3@ (freespace > 0)@H_301_3@

@H_301_3@{@H_301_3@

@H_301_3@@H_301_3@if@H_301_3@ (rdata->len > freespace)@H_301_3@

@H_301_3@{@H_301_3@

@H_301_3@memcpy(Insert->currpos,rdata->data,freespace);@H_301_3@

@H_301_3@rdata->data += freespace;@H_301_3@

@H_301_3@rdata->len -= freespace;@H_301_3@

@H_301_3@write_len -= freespace;@H_301_3@

@H_301_3@ @H_301_3@}

@H_301_3@@H_301_3@else@H_301_3@

@H_301_3@{@H_301_3@

@H_301_3@@H_301_3@ @H_301_3@memcpy(Insert->currpos,rdata->len);

@H_301_3@@H_301_3@ @H_301_3@freespace -= rdata->len;

@H_301_3@write_len -= rdata->len;@H_301_3@

@H_301_3@Insert->currpos += rdata->len;@H_301_3@

@H_301_3@rdata = rdata->next;@H_301_3@

@H_301_3@@H_301_3@continue@H_301_3@;@H_301_3@

@H_301_3@}@H_301_3@

@H_301_3@}@H_301_3@

@H_301_3@

@H_301_3@@H_301_3@/* Use next buffer */@H_301_3@

@H_301_3@updrqst = AdvanceXLInsertBuffer(false);@H_301_3@

@H_301_3@curridx = Insert->curridx;@H_301_3@

@H_301_3@@H_301_3@/* Insert cont-record header */@H_301_3@

@H_301_3@Insert->currpage->xlp_info |= XLP_FIRST_IS_CONTRECORD;@H_301_3@

@H_301_3@contrecord = (XLogContRecord *) Insert->currpos;@H_301_3@

@H_301_3@contrecord->xl_rem_len = write_len;@H_301_3@

@H_301_3@Insert->currpos += SizeOfXLogContRecord;@H_301_3@

@H_301_3@freespace = INSERT_FREESPACE(Insert);@H_301_3@

@H_301_3@ }@H_301_3@

@H_301_3@

@H_301_3@ LWLockRelease(WALInsertLock);@H_301_3@

@H_301_3@ XactLastRecEnd = RecPtr;@H_301_3@

@H_301_3@ END_CRIT_SECTION();@H_301_3@

@H_301_3@ @H_301_3@return@H_301_3@ RecPtr;@H_301_3@

}@H_301_3@@H_301_3@

@H_301_3@

下面这个图是WAL日志中存放的有关的INSERT、UPDATE、DELETE操作的内容,该图引自《Internals Of Postgresql Wal》



就到这儿吧。

@H_301_3@@H_301_3@@H_301_3@

-----------------

转载请著明出处: blog.csdn.net/beiigang beigang.iteye.com

猜你在找的Postgre SQL相关文章