NILFS was merged into the Linux kernel 2.6.30. For the 2.6.30 kernel or later, you only need to download the utility package.
Userland utilities nilfs-utils-2.2.0.tar.bz2 Apr 8, 2014 JST.
Include SET_SUINFO ioctl support which helps to reduce unfruitful segment cleaning, and lssu command is enhanced to help analysys of the ratio of in-used blocks for every segment.
For changes from past versions, see ChangeLog.
The latest version of nilfs-utils 2.0 series is as follows:
- nilfs-utils-2.0.24.tar.bz2 Dec 5, 2011 JST.
引言:
我们知道在使用PostgreSQL的流复制做的高可用方案,即使使用同步的流复制,当主库由于操作系统或硬盘故障宕机时,当我们把备机提升为主库后,原主库通常无法转成备库。这是因为,在流复制的同步过程中,当有大量更新时,主库总是比备库超前一点,当切换后,备库提升为主库后,原主库就无法变成与新主库的Standby,如下图所示:
如果我们能把原主库回滚一段时间,让其与备库在同一个时间序列上,这时我们就可以把原主库变成新主库的备库了,如下图所示:
那么如何把原先的主库回滚到前一阶段呢?这就用到了本文讲的nilfs文件系统。有人说,不用nilfs文件系统,使用Linux下的LVM的快照功能也能实现这个回滚的功能,如每5分钟建一个快照,始终保持两个快照,这样我总是能把主库回滚到5分钟前的状态,这样也能实现这个回滚的功能。但LVM的快照功能会对性能有较大的影响。LVM的快照功能是通过COW(copy on write)来实现的,也就是当打开快照功能后,当写一个数据时,需要把旧数据拷贝到快照的空间中,这时相当于把写放大的一倍。于是有人就想如果每次写入新数据时,都不要覆盖旧数据,写到新的地方去,就象PostgreSQL表本身实现的多版本一样,这样就不能有这么大的性能损耗了。nilfs文件系统就是这样的一种文件系统。nilfs原本是为flash或SSD来设计的文件系统,对于SSD来说,重写代价比较大,所以每次写都写到新的地方,这样就能提高写的性能。这刚好能满足我们的这个需要。
本文后面就详细讲解如何使用nilfs实现如上的功能。
1. 本文所使用的测试环境
主库:192.168.56.11,db01
主库:192.168.56.12,db02
操作系统都是ubuntu12.04 server 数据库都安装在操作系统用户osdba下。
2. 初始化环境
在192.168.56.11和192.168.56.12上都做以下的操作。
为了使用nilfs,先安装nilfs的相关软件包:
sudo aptitude install nilfs-tools
我们的数据库数据目录在/home/osdba/pgdata下,我们格式化一个nilfs的文件系统,挂载到/home/osdba/pgdata下:
sudo mkfs -t nilfs2 -L pgdata /dev/sdb1
sudo mount /dev/sdb1 /home/osdba/pgdata
sudo chown -R osdba:osdba pgdata
sudo chmod 700 pgdata
sudo rm /home/osdba/pgdata/.nilfs
编译安装PostgreSQL,具体可见:Linux下的PostgreSQL简单安装手册
安装完后,在.bashrc后面设置了:
export LD_LIBRARY_PATH=/home/osdba/pgsql/lib:$LD_LIBRARY_PATH
export PATH=/home/osdba/pgsql/bin:$PATH
export PGDATA=/home/osdba/pgdata
alias pgstart='pg_ctl -D $PGDATA start'
alias pgstop='pg_ctl -D $PGDATA stop -m fast'
3. 建主库
执行下面的命令,把数据库建起来:
initdb -k
改动/home/osdba/pgdata/postgresql.conf的配置如下:
listen_addresses = '*'
wal_level = hot_standby
max_wal_senders = 5
hot_standby = on
logging_collector = on
改动/home/osdba/pgdata/pg_hba.conf的配置如下:
host all all 192.168.56.0/24 md5
host replication osdba 192.168.56.0/24 md5
启动数据库:
pgstart
在主库上把数据库超级用户osdba,设置一个密码,以便于后面建备库时使用密码连接主库:
osdba@db01:~$ psql postgres
psql (9.3.3)
Type "help" for help.
postgres=# alter user osdba password '123456';
ALTER ROLE
postgres=#
4. 建备库
使用pg_basebackup建备库,执行如下命令:
pg_basebackup -h 192.168.56.11 -U osdba -F p -P -x -R -D /home/osdba/pgdata -l osdbabackup
实际执行过程如下:
osdba@db02:~$ pg_basebackup -h 192.168.56.11 -U osdba -F p -P -x -R -D /home/osdba/pgdata -l osdbabackup
Password:
36002/36002 kB (100%), 1/1 tablespace
这样备库就建好了,后面我们配置备库,新建备库的/home/osdba/pgdata/recovery.conf文件,其内容如下:
recovery_target_timeline = 'latest'
standby_mode = 'on'
primary_conninfo = 'application_name=standby01 user=osdba password=123456 host=192.168.56.11 port=5432 sslmode=disable sslcompression=1'
启动备库:
osdba@db02:~$ pgstart
server starting
osdba@db02:~$ LOG: redirecting log output to logging collector process
HINT: Future log output will appear in directory "pg_log".
osdba@db02:~$ psql postgres
psql (9.3.3)
Type "help" for help.
postgres=# select pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
这样环境就完全搭建好了。
5. 做测试
为了让测试更真实,我们运行pgbench不断的产生一些读写,然后做切换。 先初始化pgbench:
pgbench -i -h 192.168.56.11 postgres
启动pgbench的压力测试:
pgbench -h 192.168.56.11 -c 10 -T 1800 postgres
这时我们按192.168.56.11的电源,让其关机,模拟这台机器出故障的情况。同时记录时间。我们关机的时间为:
osdba@osdba-laptop:~$ date
2014年 03月 03日 星期一 23:28:46 CST
我们然后把备库192.168.56.12提升为主库:
osdba@db02:~$ pg_ctl promote
server promoting
osdba@db02:~$ psql postgres
psql (9.3.3)
Type "help" for help.
postgres=# select pg_is_in_recovery();
pg_is_in_recovery
-------------------
f
(1 row)
可以看到192.168.56.12已变成主库了。
这时我们需要把原先的主库192.168.56.11变成192.168.56.12的备库。把192.168.56.11重新开机,并把nilfs再mount上来:
sudo mount /dev/sdb1 /home/osdba/pgdata
这时我们只需要把192.168.56.11上的文件系统回滚到刚才关机前的一个时间,这个时间的WAL日志已同步到192.168.56.12上就可以了,我们先看nilfs的checkpoint点,nilfs会自动产生很多个checkpoint点,每个checkpoint点都可以转换成快照,然后就可以把快照mount上来。因为nilfs没有提供直接把文件系统回滚到一个快照点的功能,把nilfs回滚到快照点的方法是把快照点mount上来,然后使用rsync把快照的数据同步到当前的文件系统中。
下面的操作在192.168.56.11机器上: 我们先查看一下我们要回滚到文件系统的哪个checkpoint点,使用nilfs的lscp命令,如下所示:
osdba@db01:~$ lscp
CNO DATE TIME MODE FLG NBLKINC ICNT
1 2014-03-03 23:06:21 cp - 11 2
2 2014-03-03 23:06:55 cp - 8 2
......................
......................
9935 2014-03-03 23:28:40 cp - 19 806
9936 2014-03-03 23:28:41 cp - 19 806
9937 2014-03-03 23:28:41 cp - 19 806
.....................
估计回滚到9935这个checkpoint应该是可以的,执行下面命令把这个checkpoint转换到snapshot
sudo chcp ss /dev/sdb1 9935
然后挂载上来:
mkdir /home/osdba/snap
sudo mount -t nilfs2 -o ro,cp=9935 /dev/sdb1 /home/osdba/snap
注意挂载snapshot的方法是"-o ro,cp=9935",“ro”表示只读,“cp=9935”表示挂载哪个snapshot。
把快照的数据同步到当前的文件系统中:
rsync -axv --delete /home/osdba/snap/ /home/osdba/pgdata/
把192.168.56.11上的数据库转成备库,建/home/osdba/pgdata/recovery.conf,内容如下:
recovery_target_timeline = 'latest'
standby_mode = 'on'
primary_conninfo = 'application_name=standby01 user=osdba password=123456 host=192.168.56.12 port=5432 sslmode=disable sslcompression=1'
启动192.168.56.11上的数据库,这样就完成了切换。这时192.168.56.11转换成了192.168.56.12的备库了。我们可以到192.168.56.12上看同步的情况 :
postgres=# select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | state | sent_
location | write_location | flush_location | replay_location | sync_priority | sync_state
------+----------+---------+------------------+---------------+-----------------+-------------+-------------------------------+-----------+------
---------+----------------+----------------+-----------------+---------------+------------
1267 | 10 | osdba | standby01 | 192.168.56.11 | | 40594 | 2014-03-03 23:47:59.920158+08 | streaming | 0/51D
1E70 | 0/51D1E70 | 0/51D1E70 | 0/51D1E70 | 0 | async
(1 row)
上面的信息可以看出192.168.56.11上的备库已经连接到192.168.56.12上,同步也正常。