WAL日志文件名称格式详解

Posted on 2015-10-08 22:00:47 by osdba


PostgreSQL的WAL日志文件在pg_xlog目录下,一般情况下,每个文件为16M大小:


osdba-mac:~ osdba$ ls -l  $PGDATA/pg_xlog
total 262144
-rw-------  1 osdba  osdba  16777216 Oct  8 10:57 0000000100000000000000B6
-rw-------  1 osdba  osdba  16777216 Jun 17 22:12 0000000100000000000000B7
-rw-------  1 osdba  osdba  16777216 Jun 17 22:12 0000000100000000000000B8
-rw-------  1 osdba  osdba  16777216 Jun 17 22:12 0000000100000000000000B9
-rw-------  1 osdba  osdba  16777216 Jun 17 22:12 0000000100000000000000BA
-rw-------  1 osdba  osdba  16777216 Jun 17 22:12 0000000100000000000000BB
-rw-------  1 osdba  osdba  16777216 Jun 17 22:13 0000000100000000000000BC
-rw-------  1 osdba  osdba  16777216 Jul 23 19:01 0000000100000000000000BD
drwx------  2 osdba  osdba        68 Mar  9  2015 archive_status


文件名称为16进制的24个字符组成,每8个字符一组,每组的意义如下:


    00000001 00000000 000000BC
    -------- -------- --------
     时间线    LogId    LogSeg

  • 时间线:英文为timeline,是以1开始的递增数字,如1,2,3...
  • LogId:32bit长的一个数字,是以0开始递增的数字,如0,1,2,3...
  • LogSeg:32bit长的一个数字,是以0开始递增的数字,如0,1,2,3...


然而需要注意的是,数据库刚建好时,LogSeg第一次是从1开始的数字,到达一个最大的数字后,会重头开始,但以后再从头开始时,不再从1开始,而是从0开始。为什么这样呢?下面我们会讲解到这个问题。

WAL日志的位置是一个无限长的位置,数据库一建立后,不断的开始写WAL日志,此位置就不断的增加,即使数据库重启后,此位置也不会重新开始,只会一直增加,所以这个位置值显然如果用32bit的一个数字是不够的,32bit最多表示4GB的日志,所以大家很容易可以想到用一个64bit的数字来表示这个WAL日志的位置,这当然就足够了(因为要写4G*4G的数据才会用完64bit的长度)。

这个WAL日志的位置称之为LSN,即Log Sequence Number。

看起来,LogId+LogSeg好象刚好组成一个64bit的LSN,是否是LogId是这个64bit的LSN中的高32bit字节,而LogSeg就是低32bit的字节呢?LogId确实是LSN的高32字节,但LogSeg却不是低32bit的字节,另我们可以观察到LogSeg的值是从00000000到000000FF后,就重新从00000000开始了,并不会出现00000100这样的数值,也就是LogSeg的8个字符中,前6个字符始终是0,这是为什么?

原来LogSeg是按文件递增,每增加一个文件,LogSeg就增加1,而每个WAL文件的大小是16M,LogSeg是LSN的低32bit的字节的值再除以16M的结果,这样4G/16M结果是256,所以LogSeg最大的值为256,即16进制的0xFF,这样就解释了LogSeg的前6字节都是零的原因。

另原本LSN起始位置可以从0开始,但为了表示一些无效的位置,LSN的起始值并不是从0开始,也不是从1开始,而是跳过了一个WAL文件大小,即16M的位置开始,这样第一次时LogSeg是从1开始的,而不是从0开始了。

我们知道了WAL日志文件名中的意义,但很多时候我们会发现在PostgreSQL中会用两个十六进制的数字中间用斜杠“/”分隔表示WAL日志位置,即表示LSN,如函数pg_current_xlog_location、pg_current_xlog_insert_locatione及pg_start_backup的返回结果,如下所示:


postgres=# select pg_current_xlog_location(), pg_current_xlog_insert_location();
 pg_current_xlog_location | pg_current_xlog_insert_location
--------------------------+---------------------------------
 0/12B00B48               | 0/12B00B48
(1 row)
  
postgres=# select pg_start_backup('osdba_backup201510082130');
 pg_start_backup
-----------------
 0/14000100
(1 row)


同时我们在主库上通过查询视图pg_stat_replication来看replication的状态时,一些字段也显示成这样的格式:


postgres=# select sent_location, write_location, flush_location, replay_location, sync_state from pg_stat_replication;
 sent_location | write_location | flush_location | replay_location | sync_state
---------------+----------------+----------------+-----------------+------------
 0/1177A9F0    | 0/1177A9F0     | 0/1177A320     | 0/11779818      | async
(1 row)

以上这些表示WAL日志位置的方法,就是用两个32bit的数字来表示LSN,前一个数字LSN的高32bit,而另一个数字则是LSN的低32bit。在PostgreSQL9.4之后,增加了一个专门的类型pg_lsn用于表示WAL日志的位置,在PostgreSQL9.4之前,则用字符串来表示这个位置。

象上面的例子中,函数pg_current_xlog_location返回0/12B00B48,如果当前timeline为1,那么当前的WAL日志文件名是什么呢?

  • logId就是“0/12B00B48”中的第一个数字,即0
  • logSeg就是“0/12B00B48”中的第二个数字除以16M的大小,即12B00B48除以16M,而16M相当于2的24次方,相当于十六进制数“12B00B48”右移6位,即“12B00B48”中的最高两位“12”
  • 那么根据WAL文件的格式timeline+logId+logSeg,则相当于:“00000001”+“00000000”+“00000012”,即为:“000000010000000000000012”
  • 写的位置是在文件“000000010000000000000012”中的偏移量是多少呢?实际上是在“0/12B00B48”中第二个数字“12B00B48”后六位“B00B48”,换算成十进制为“11537224”。


当然PostgreSQL已准备了函数pg_xlogfile_name_offset帮我们做以上的转换,如下所示:


postgres=# select pg_xlogfile_name_offset('0/12B00B48');
       pg_xlogfile_name_offset
-------------------------------------
 (000000010000000000000012,11537224)
(1 row)