我有两台cabinet服务器,master-master类型的,使用B+ tree。有两次服务器宕机之后,重新启动cabinet,发现数据丢失,即通过接口无法读取到数据,但是tcb文件还是很大。

在本地虚拟机上模拟时,kill关闭数据不会丢失,而kill -9则会丢失数据,经过多次惨痛的教训说明,cabinet不是那么稳定,一定要做好它宕机以后的恢复和应急准备。

锁的问题(非根本问题,不是很重要)

摘录一台服务器的cabinet.log如下:

2011-10-22T22:04:02+08:00       SYSTEM  --------- logging started [1561] --------
2011-10-22T22:04:02+08:00       SYSTEM  process ID configuration: path=/home/admin/apps/tokyo/data/ttserver.pid pid=1561
2011-10-22T22:04:02+08:00       SYSTEM  server configuration: host=192.168.80.49 port=19999
2011-10-22T22:04:02+08:00       SYSTEM  maximum connection: 1048575
2011-10-22T22:04:02+08:00       SYSTEM  opening the database: /home/admin/apps/tokyo/data/shorturl.tcb#lmemb=1024#nmemb=2048#bnum=10000000
2011-10-22T22:04:02+08:00       SYSTEM  update log configuration: path=/home/admin/apps/tokyo/data/ limit=134217728 async=0 sid=49
2011-10-22T22:04:02+08:00       SYSTEM  replication configuration: host=192.168.80.27 port=19999 ropts=0
2011-10-22T22:04:02+08:00       SYSTEM  service started: 1561
2011-10-22T22:04:02+08:00       SYSTEM  listening started
2011-10-22T22:24:35+08:00       SYSTEM  --------- logging started [1492] --------
2011-10-22T22:24:35+08:00       SYSTEM  process ID configuration: path=/home/admin/apps/tokyo/data/ttserver.pid pid=1492
2011-10-22T22:24:35+08:00       SYSTEM  server configuration: host=192.168.80.49 port=19999
2011-10-22T22:24:35+08:00       SYSTEM  maximum connection: 1048575
2011-10-22T22:24:35+08:00       SYSTEM  opening the database: /home/admin/apps/tokyo/data/shorturl.tcb#lmemb=1024#nmemb=2048#bnum=10000000
2011-10-22T22:24:35+08:00       SYSTEM  update log configuration: path=/home/admin/apps/tokyo/data/ limit=134217728 async=0 sid=49
2011-10-22T22:24:35+08:00       SYSTEM  replication configuration: host=192.168.80.27 port=19999 ropts=0
2011-10-22T22:24:35+08:00       SYSTEM  service started: 1492
2011-10-22T22:24:35+08:00       SYSTEM  listening started
2011-10-23T11:40:04+08:00       SYSTEM  --------- logging started [1476] --------
2011-10-23T11:40:04+08:00       SYSTEM  process ID configuration: path=/home/admin/apps/tokyo/data/ttserver.pid pid=1476
2011-10-23T11:40:04+08:00       SYSTEM  server configuration: host=192.168.80.49 port=19999
2011-10-23T11:40:04+08:00       SYSTEM  maximum connection: 1048575
2011-10-23T11:40:04+08:00       SYSTEM  opening the database: /home/admin/apps/tokyo/data/shorturl.tcb#lmemb=1024#nmemb=2048#bnum=10000000
2011-10-23T11:40:04+08:00       SYSTEM  update log configuration: path=/home/admin/apps/tokyo/data/ limit=134217728 async=0 sid=49
2011-10-23T11:40:04+08:00       SYSTEM  replication configuration: host=192.168.80.27 port=19999 ropts=0
2011-10-23T11:40:04+08:00       SYSTEM  service started: 1476
2011-10-23T11:40:04+08:00       SYSTEM  listening started

可以看到这里没有logging finish的字样。而正常的log应该如下:
2011-10-27T16:19:57+08:00       SYSTEM  --------- logging started [15223] --------
2011-10-27T16:19:57+08:00       SYSTEM  process ID configuration: path=/var/ttserver/ttserver.pid pid=15223
2011-10-27T16:19:57+08:00       SYSTEM  server configuration: host=127.0.0.1 port=19999
2011-10-27T16:19:57+08:00       SYSTEM  maximum connection: 1048575
2011-10-27T16:19:57+08:00       SYSTEM  opening the database: /var/ttserver/shorturl49.tcb#lmemb=1024#nmemb=2048#bnum=10000000
2011-10-27T16:19:57+08:00       SYSTEM  update log configuration: path=/var/ttserver/ limit=134217728 async=0 sid=1
2011-10-27T16:19:57+08:00       SYSTEM  service started: 15223
2011-10-27T16:19:57+08:00       SYSTEM  listening started
2011-10-27T17:14:26+08:00       SYSTEM  listening finished
2011-10-27T17:14:26+08:00       SYSTEM  closing the database
2011-10-27T17:14:43+08:00       SYSTEM  --------- logging started [16864] --------

猜测数据没有丢失,只是由于某些原因无法读取。

在虚拟机的故障中,cabinet.log中发现如下ERROR,这时我在尝试用tcrmgr list获取其中的数据(共3次),tcrmgr返回的错误是“error: 9999: miscellaneous error”。

2011-10-27T17:16:35+08:00       ERROR   do_iterinit: operation failed
2011-10-27T17:16:38+08:00       ERROR   do_iterinit: operation failed
2011-10-27T17:16:50+08:00       ERROR   do_iterinit: operation failed

过了几分钟之后,tcrmgr不再报错误,但是返回为空。而这时telnet上去stats可以看到total_items和curr_items都不为空。

以上的do_iterinit: operation failed,经过查看tokyocabinet-1.4.47/tcbdb.c中的tcbdbcurfirst函数,推测应该是由于无法获取到文件锁而导致的。因为之前的ttserver非正常退出,文件锁还未释放完全。通过lsof命令可以看到当前该tcb文件锁被ttserver进程持有。

二进制文件损坏问题

参考该文可知,cabinet在非常退出情况下,确实会丢失数据:

http://hi.baidu.com/ah__fu/blog/item/309462946d463415d31b7058.html

我的想法是,由于cabinet使用二进制作为文件存储格式,在B+ tree的情况下,每次获取,需要先tcbdbcurfirst,即找到第一条记录。如果在异常情况下,导致部分数据没有完成写入,从而使二进制的格式损坏,则无法获取文件中的数据了!虽然这个时候,文件还是很大。

可以看到,在空数据的情况下,初始生成的tcb文件就有41m,其中必然含有一些头信息。

(有空的时候,可以看看它的存储格式,是否有可以改进以有利于文件容错性的地方。)

我们使用的是master-master双机cabinet,当一台cabinet服务器故障之后,可以由另外一台进行恢复。

考虑到几种恢复方式:

  1. 直接从无故障的机器,cp tcb文件覆盖至有故障的机器。(抛弃:因为cp时 tcb文件可能还有不完整的写入)注意:这种方式可能导致丢失大量数据!!!!!
  2. 利用无故障机器的ulog,及tyrant提供的tcrmgr restore命令进行恢复。(抛弃:不太好确定ulog的时间戳,顾不好确定restore之后同步的基准时间戳;restore 1.4G的ulog,在8g内存、4核cpu、linux2.6的机器上,耗时2分钟)
  3. 每当本机有新的ulog生成时,将上一个ulog restore至备份的cabinet文件(可以本机,也可以其他机器);故障时,仅需要将备份cabinet文件覆盖,并restore最新的一个ulog即可。(抛弃:因为本机的ulog可能也是不完整的)
  4. 利用master-master架构,将故障机从集群里摘下,设置rts为0,从master里做完整的恢复(待确定时间)。

最终参考http://www.pin5i.com/showtopic-25438.html,做如下的恢复方式:

  • 从无故障机器tcrmgr copy -port 11211 masterhost ‘@/data/backup/backup.sh’  (如果tcrmgr copy命令中,参数以@开始,那么后面的字符串作为命令行执行;copy 1.2G的文件,1分钟之内完成)(copy的时候,masterhost可读,但不可写)
  • 将生成的备份文件 old-file-name.xxxx(xxxx为时间戳)scp至待恢复的机器,并改名为 old-file-name
  • 将xxxx时间戳写入到 待恢复机器的rts文件(作为replication time stamp的基准时间)
  • 启动待恢复机器的ttserver,使用该文件作为数据文件
  • 检查,copy之后的数据,应该已从无故障机器同步至待恢复机器了

附backup.sh:

#! /bin/sh
srcpath="$1"
destpath="$1.$2"
rm -f "$destpath"
cp -f "$srcpath" "$destpath"

最终实践中的备份方式,由于tcrmgr copy会对数据文件加锁,所以单独使用一台ttserver的机器做备份:

  • online的ttserver服务器可以有2台(A、B),备份的ttserver服务器2台(C、D)
  • 两台online的ttserver互为master-master,备份ttserverC以A服务器为master,D以B服务器为master
  • 每隔一段时间,比如6小时,在备份ttserver上,运行tokyo/tyrant/bin/tcrmgr copy -port 19999 192.168.80.27 ‘@/home/admin/apps/tokyo/tyrant/bin/backup.sh’ 进行全量备份;并删除几天前的旧备份文件
  • 一旦online server出问题,比如是A出问题,立刻将问题serverA以及对应的备份C摘下。以B、D建立online的ttserver。
  • 待A恢复完毕之后,以A、C作为备份机
    • 从C上scp一份最新的备份文件,并且以文件名中的时间,写入到A的rts文件中,作为同步的起始时间
    • 删除A上的ulog
    • 以该备份文件,覆盖A的数据文件,启动ttserver,以B为master,则备份文件之后的数据会从B同步到A
    • 以同样的方式,建立C和D之间的备份关系
cabinet的一些常见故障
  • 千万别kill -9,否则文件格式可能被损坏
  • 使用php的TokyoTyrant扩展连接cabinet,并且使用persistent方式时,如果之前已连接上,再关闭cabinet进程,则connect成功,但是get、set等操作都失败。使用非persistent时,可抛出异常:connect refused
  • 直接使用cp文件方式获取的B+tree数据文件,会导致:
    • 数据不完整
    • 若作为其他ttserver的master,会导致其他ttserver进程的崩溃

Leave a Reply