PostgreSQL的xlog,即WAL(Write Ahead Log),位于pg_xlog目录下,是PostgreSQL 完全或者PITR(Point In-Time Recovery)恢复所依赖的日志。
为什么会有WAL日志呢?这个跟目前主流的数据库如Oracle,MySQL等一样,保证事务的持久性。即任何已经commit的事务,必须是持久的。
如果我把redo日志先于数据写出到磁盘,那么事务总是可以恢复的,虽然时间有时候可能会久点。为什么日志先写,主要是因为写日志到磁盘比写数据到磁盘要快。
日志是连续IO刷出,而数据文件则一般都是随机IO。
另外,细心的同学可能会发现PostgreSQL的日志文件名很长,而且大小是16M。目前PG默认xlog segment size是16M,这个只能在源码编译时设定,假设我设置成64M,
那么编译时configure带参数“--with-wal-segsize=64”即可。
另外xlog文件名可以分为二部分,以”000000010000000000000002”为例,分解为以下两个部分:
00000001,0000000000000002
其中第一个部分“00000001”,为TimeLineId,即通常所说的TLI,它用来标识数据库状态的,每做一次恢复后,都会增加,主要为了将恢复后的数据库跟之前的数据库区别开来,
如Point In-Time Recovery。通常的关闭和起动数据库,并不会改变此值。PG用一个32位来标示TimeLineId,源码定义如下:
这个有点类似于Oracle中Incarnation的概念。
第二部分为“0000000000000002”,为xlogsegno,用64位来表示。源码定义为:
这其实已经是64位系统所能表示的最大值了。这是理论上的xlogsgno最大值,即”FFFFFFFFFFFFFFFF”。事实上文件号到达不了这个值。下面我详细来说明。
首先让我们来理解日志流的内存寻址表示方法。在PostgreSQL中,日志其实是用内存偏移地址来表示的。源码定义为:
PG用了一个64位的地址表示日志的位置。很多同学认为,这很容易用完64位,那事实上是这样子吗?我们可以算一下:
假设我们一天的日志量产生为10T,即2^40次方;那么可以使用的年数为:2^64/2^24/365=4596.49。这显然是足够了。
讲到这里,很多同学已经急了,这跟日志的文件名有什么关系呢?
答案是肯定的。上面所讲的,日志文件第二部分xlogsegno还可以再拆分两个32位。高32位表示xlog的序列号,低32位来表示此xlog包含的segment号。
Xlog segment号源码定义如下:
文件名定义源码:
假设xlog的segment大小为16M。那么一个xlog所对应的segment个数为:2^32/2^24=2^8=256。所以最大的xlog文件名应该为“00000001FFFFFFFF000000FF”,而不是上面所提到的理论最大值“00000001FFFFFFFFFFFFFFFF”。
另外,PG数据库初始化时,xlog的第一个位置会在XLOG_SEG_SIZE逻辑偏移量处开始,所以在16M大小的情况下,最小文件号为” 00000001FFFFFFFF00000001”,而不是“00000001FFFFFFFF00000000”。从”0“处开始是非法的,以下为源码中定义非法 xlog record pointor。
下面我们可以一个实验,来验证上面的过程:
1. 首先将当前xlog位置重置到离最大值还差一个xlog
2.做checkpoint和switch logfile
首先报错,后面实例被终止,最后xlog被循环,从最小值开始。经历了FFFFFFFF000000FF-->0000000000000001
后台日志过程:
------------------------------------华丽丽的分割线------------------------------------
CentOS 6.3环境下yum安装PostgreSQL 9.3