基于SequoiaDB实现的离线灾备系统
1. 环境信息
2. 系统部署
图1. 系统部署图
主机1,2,3 部署SequoiaDB 的编目、协调和数据节点,数据节点的数据路径与上述图上的磁盘对应,编目和协调节点共享使用 disk1 即可。
数据组的主节点(P)均匀分布在各主机(如上图所示),可以通过配置节点的权值(weigth)实现,权值越高越能当选主节点。
数据组的备份节点(B)均匀分布在各主机(如上图所示),这些节点配置其数据备份路径(bkuppath)为对应该主机上的共享磁盘(disk4)。
关键指标计算
Ø 备份节点(B)的选取: 备份节点选各数据组的备节点优先
Ø 增量备份周期的选取:假设集群每小时总的数据变化量为 P(GB),分区组个数为N,则平均到每个节点的数据量为 P/N,对应节点日志的数据变化为 P/N * 2,假设日志大小为 Z(GB),则日志在不覆写情况下可以支撑的时间为:T = (N * Z) / (2* P) * 60 (分钟)。 增量备份周期的取值建议为:W < T/5。 在上述业务系统中 P = 10,Z = 5,因此 T = 45 (分钟), W取值为 5 (分钟)。
Ø 数据最大丢失时间:在整个数据库故障时,最大可能丢失数据的时间窗口为:L < 2 * W。 因此,也可以通过该指标来决定 W 的合理取值。
3. 灾备系统搭建
1) 全量 + 增量备份
在备份节点上,首先进行一次“全量”备份,然后每隔 W(分钟)进行一次增量备份:
backup.js文件
var g_backupName = "backup" ;
var g_backupInterval = 5 * 60 * 1000 ;
var g_hostName = "host1" ; /* 请修改为对应的机器 */
var g_svcname = "11850" ; /* 请修改为对应的节点 */
function doBackup( db ) {
varbackupInc = true ;
vartimes = 0 ;
while ( times
{
try {
++times ;
db.backupOffline( { Name:g_backupName, EnsureInc:backupInc } ) ;
break ;
} catch ( e ) {
if ( -241 == e )
{
backupInc = false ;
} else {
throw e ;
}
}
}
}
function backupMonitor( db ) {
vari = 0 ;
while( true ) {
doBackup( db ) ;
++i ;
println( "Backup times " + i + " succeed." ) ;
sleep( g_backupInterval ) ;
}
}
function main() {
db= new Sdb( g_hostName, g_svcname ) ;
backupMonitor( db ) ;
}
/* 执行入口 */
main() ;
backup.sh文件
#! /bin/bash
ROOTPATH="./"
SDB="${ROOTPATH}/bin/sdb"
BKFILE="backup.js"
function status()
{
#Return pid when process exist, otherwise return 0
out=`ps -ef|grep ${BKFILE} | grep -v grep `
ret=$?
if[ $ret -eq 0 ] ; then
local pid=`echo ${out} |awk '{print $2}'`
echo "Backup is started($pid)"
else
echo "Backup is not started"
fi
return $ret
}
function getpid()
{
#check
out=`status`
ret=$?
if[ $ret -eq 0 ] ; then
pid=`echo ${out} | awk -F '(' '{print $2}' | awk -F ')' '{print $1}'`
echo "${pid}"
return 0
else
return 1
fi
}
function start()
{
#check
pid=`getpid`
ret=$?
if[ $ret -eq 0 ] ; then
echo "Backup is already started(${pid})"
return 1
else
printf "Starting backup..."
fi
#start
nohup ${SDB} -f ${BKFILE} 1>&2 2>/dev/null &
if[ $? -eq 0 ] ; then
pid=`getpid`
echo "Succeed(${pid})"
return 0
else
echo "Failed"
return 1
fi
}
function stop()
{
pid=`getpid`
if[ $? -eq 0 ] ; then
printf "Stoping backup(${pid})..."
kill $pid >/dev/null
echo "Succeed"
return 0
else
echo "Backup is not running"
return 1
fi
}
# Parse command line parameters.
case $1 in
start) start || exit $?
;;
stop) stop || exit $?
;;
status) status || exit $?
;;
*) echo "Usage: $0{start|stop|status}" 1>&2
exit 1
;;
esac
exit 0
将上述文件拷贝至主机1,2,3的安装根目录下,并修改g_hostName 和g_svcname 为对应机器和对应备份节点,并在shell窗口使用sh backup.shstart 进行执行即可。
2) 持续构建离线灾备数据
主机4的磁盘通过网络文件映射的文件映射到主机1,2,3中,作为备份的存储磁盘,这样当主机1,2,3在持续的备份过程中,主机4就可以进行持续的灾备数据构建。
restore.js文件
var g_backupName = "backup" ;
var g_backupInterval = 1 * 60 * 1000 ;
var g_peerHostName = "host1" /* 请修改为对应备份的机器 */
var g_peerSvcName = "11830" /* 请修改为对应备份的节点 */
var g_bkpath ="/database/disk1/11830/bakfile" /* 请修改为对应节点的备份路径 */
varg_restoreDatapath="/database/disk1/11830/data" /* 请修改为对应节点的灾备数据路径 */
var g_restoreSvcName = "11830"; /* 请修改为对应的节点 */
function doRestore( db, cmd ) {
varmaxID = -1 ;
varcursorObj = db.listBackup( {Name:g_backupName}, {}, {}, {ID:-1} ).toArray() ;
if( cursorObj.length > 0 ) {
var obj = eval( '(' + cursorObj[0] + ')' ) ;
maxID = obj["ID"] ;
}
if( maxID >= 0 ) {
/* 开始恢复 */
var cmdStr = "bin/sdbrestore -p " + g_bkpath + " -n" + g_backupName ;
cmdStr += " --isSelf false --dbpath " + g_restoreDatapath ;
cmdStr += " --svc " + g_restoreSvcName + " -s true";
cmd.run( cmdStr ) ;
}
return maxID ;
}
function doRemove( db, maxID ) {
varrmNum = 0 ;
if( maxID >= 3 ) {
println( "Max ID is: " + maxID ) ;
var cursorObj = db.listBackup( {Name:g_backupName}, {ID:{$lte:maxID-3}},{}, {ID:1} ).toArray() ;
for ( var i = 0 ; i < cursorObj.length ; ++i ) {
var obj = eval( '(' + cursorObj + ')' ) ;
db.removeBackup( {Name:g_backupName, ID:obj["ID"]} ) ;
println( "Removed backup: " + obj["ID"] ) ;
++rmNum ;
}
}
return rmNum ;
}
function restoreMonitor( db, cmd ) {
vari = 0 ;
varmaxID = 0 ;
while( true ) {
maxID = doRestore( db, cmd ) ;
doRemove( db, maxID ) ;
++i ;
println( "Restore times " + i + " succeed." ) ;
sleep( g_backupInterval ) ;
}
}
function main() {
cmd= new Cmd() ;
db= new Sdb( g_peerHostName, g_peerSvcName ) ;
restoreMonitor( db, cmd ) ;
}
/* 执行入口 */
main() ;
将上述文件拷贝至主机4的安装根目录下(每一个灾备恢复需要拷贝一份,并改名为 restore_.js, svcname为对应的灾备节点),并修改g_peerHostName 和g_peerSvcName 为对应执行备份的机器和节点,修改g_bkpath、g_restoreDatapath和g_restoreSvcName为当前备份文件源路径,灾备的数据归档路径和灾备对应的节点,并在shell窗口对每一个灾备节点使用bin/sdb -frestore_.js 进行执行即可,也可以使用后台命令:nohup bin/sdb-f restore_.js 1>&2 2>/dev/null & 执行。
4. 原理和结论
通过上述“全量+增量”持续备份和 持续数据恢复 可以构建离线灾备数据,通过设置合理的增量备份周期W以实现数据对业务的最大满足度。
图2. 离线灾备系统原理图
1、 首先通过对节点进行一次全量备份;
2、 然后不断对该节点的变化数据进行增量备份;
3、 在数据恢复时,针对全量备份会在首次进行全量恢复;
4、 对于增量备份,会与之前的全量恢复数据进行重新合并,把变化的数据修改到文件中;
5、 在恢复完成后,可以清除之前的备份(只需要保留最近一次备份即可),以释放磁盘空间。
通过不断的增量备份和增量恢复,可以保证变化的数据都在恢复的离线文件中,一旦节点故障,则可以直接拷贝使用,以实现快速恢复。