2014年11月

Mysql备份和恢复的一种可行方案---Xtrabackup(转)

这几天使用Xtrabackup实现了下mysql的全库备份和恢复,这里和大家分享下实现的思路。

关于Xtrabackup(又或innobackupex)的介绍这里就不啰嗦了,感兴趣的同学请移步官方文档,这里只要知道它提供了mysql备份和恢复的功能就可以了。

备份思路

Xtrabackup提供了全量备份和增量备份两种方式,全量就不解释了,增量是指其可以只备份指定位置后的新增数据。本文介绍的增量备份方法使用到了LSN(Log Sequence Number),可以从备份成功文件夹的xtrabackup_checkpoints文件中获取,其中to_lsn就是下次增量备份的起始位置。
结合两者,我们可以得到下图的备份思路。
1.jpg

解释如下:

由于全量备份和增量备份的命令参数不同,所以首先要判断是否需要全量备份。

如果是全量备份,那么便执行命令,备份成功后,记录xtrabackup_checkpoints文件中的to_lsn,以便后面进行增量备份。

如果是增量备份,那么首先读取上次备份时记录下的to_lsn,如果读取失败,报错并结束。否则执行相应命令,备份成功后记录to_lsn

一个简单的实现脚本,我们叫它backup.sh,如下所示:

#!/bin/sh

# xtrabackup的相关配置
INNOBACKUPEX="innobackupex "
MY_CNF="/home/config/mysql/3307.backup.cnf"
MY_USER="xtrabackup"
MY_PASSWORD="xtrabackup"
MY_SOCKET="/home/socket/mysql/3307.sock"

# 远程备份机 文件名配置
REMOTE_HOST="dbbackup"
REMOTE_DIR="/home/backup/mysql/test"
LOCAL_LSN_FILE="~/.to_lsn_important"
DATE_NAME=`date +%Y-%m-%d-%H-%M-%S`
REMOTE_FILE=$DATE_NAME.tar.gz
LOCK_FILE="~/.mysql.backup.lock"
LOCAL_BACKUP_DIR="/home/backup/mysql/test/$DATE_NAME"

# 输出帮助信息
function usage()
{
    echo "Usage:"
    echo "-f db will be backuped fully with this parameter. If not , incrementally. "
}

#防止同时执行两个备份命令,发生冲突
if [ -f $LOCK_FILE ] ;then
    echo 'Mysql backup lockfile is locked!'
    exit 0
fi

full=0

while getopts "fh" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
        f)  
            full=1
            ;;
        h)  # 输出帮助信息
            usage
            exit 0
            ;;
    esac
done

echo "backup dir is $REMOTE_DIR/$REMOTE_FILE"

# backup up db to remote host
echo "start to backup db!"`date +%Y-%m-%d-%H-%M-%S`
if [ "$full"x = "1"x ] ;then
    # 全量备份
    echo '1' > $LOCK_FILE
    $INNOBACKUPEX --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET ./ --stream=tar | gzip | ssh $REMOTE_HOST "cat - > $REMOTE_DIR/FULL-$REMOTE_FILE"
    ssh $REMOTE_HOST "cd $REMOTE_DIR;rm -f xtrabackup_checkpoints;tar zxfi $REMOTE_DIR/FULL-$REMOTE_FILE xtrabackup_checkpoints "
    toLSN=$( ssh $REMOTE_HOST "cat $REMOTE_DIR/xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,\"\",\$2);print \$2}'" )
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
    else
        echo 'no lsn from remote host!please check!'
    fi
else
    # 增量备份
    if [ -f $LOCAL_LSN_FILE ] ;then
        toLSN=`cat $LOCAL_LSN_FILE`
    fi

    if [ ! $toLSN ] ;then
        echo 'last LSN is not set !please check!'
        exit 0
    fi

    echo '1' > $LOCK_FILE
    mkdir -p $LOCAL_BACKUP_DIR
    echo "last to lsn is "$toLSN
    $INNOBACKUPEX --parallel=6 --defaults-file=$MY_CNF --user=$MY_USER --password=$MY_PASSWORD --socket=$MY_SOCKET --incremental --incremental-lsn=$toLSN $LOCAL_BACKUP_DIR 2>/tmp/innobackexLog
    toLSN=$( cd $LOCAL_BACKUP_DIR/*; cat xtrabackup_checkpoints|grep to_lsn|awk -F= '{gsub(/ /,"",$2);print $2}' )
    echo "new to lsn is "$toLSN;
    if [ $toLSN ] ;then
        echo $toLSN > $LOCAL_LSN_FILE
        cd $LOCAL_BACKUP_DIR/*;tar zc .|ssh $REMOTE_HOST "cat - > $REMOTE_DIR/$REMOTE_FILE"
        echo "save file to $REMOTE_HOST @ $REMOTE_DIR/$REMOTE_FILE"
    else
        echo 'no lsn from local backup file!delete remote backup file!'$LOCAL_BACKUP_DIR/$REMOTE_FILE
    fi
    echo "remove $LOCAL_BACKUP_DIR"
    rm -rf $LOCAL_BACKUP_DIR
fi

rm -f $LOCK_FILE
echo "end to backup db!"`date +%Y-%m-%d-%H-%M-%S`

将上述脚本配置好后,放到mysql主机上,执行 sh backup.sh -f进行全量备份,之后将sh backup.sh添加到crontab中每小时增量备份一次就可以了。

注意--parallel=6使用是有条件的,参见文档。

下面是备份文件夹的效果图。
2.png

恢复思路

一旦线上数据库发生宕机之类的灾难性事故,备份数据就要立马发挥作用了。所谓养兵千日,用兵一时。打开备份文件夹一看,如上面的图,每小时一个压缩文件,时间久了,茫茫多,上百个压缩文件,简直是一定的,这必须得来个脚本自动化恢复数据才成。思路很简单,见下图:
3.jpg

解释如下:

先解压,没啥说的。
找到全量备份的数据,因为增量备份也是以此为基准进行的。上面备份的时候,全量备份文件命名以FULL开头便是为了此处便于查找。
先恢复全量备份数据,然后按照时间顺序恢复增量备份的数据。
将数据恢复到mysql中
启动mysql,连上看看有没问题。
一个简单的实现脚本,我们叫它restore.sh,如下所示:

#!/bin/sh
BACK_DIR="/home/backup/mysql/test"
RESTORE_DIR="/home/tmpback/test"

# xtrabackup恢复时使用的配置文件可能与数据库配置文件不同
RESTORE_MY_CNF="/home/config/mysql/3307.backup.cnf"

# 启动mysql需要的配置文件,按需设定
MY_CNF="/home/config/mysql/3307.cnf"
MY_DATA_DIR="/home/data/mysql/3307"
MY_SOCK_DIR="/home/socket/mysql/"
MY_LOG_DIR="/home/logs/mysqld/3307"

#extract all tar file
fullFileName=""
for file in $( ls $BACK_DIR | grep "tar.gz" )
do
    fileName=${file%.tar.gz}
    full=${file%%-*}
    if [ "${full}"x = "FULL"x ] ;then
        fullFileName=$fileName
    fi
    DEST_DIR="$RESTORE_DIR/$fileName"
    if [ -d $DEST_DIR ] ;then
        echo "$DEST_DIR exists!"
    else
        mkdir -p $DEST_DIR
        echo "start to extract file $file to $DEST_DIR"
        tar zxif $BACK_DIR/$file -C $RESTORE_DIR/$fileName
        echo "end to extract file "$file
    fi
done

echo "full backup dir is "$fullFileName

echo "**start to repare full backup dir"

 # 恢复全量数据
echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName"
innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName"

echo "**end to repare full backup dir"

 # 恢复增量数据
for file in $( ls $RESTORE_DIR|grep -v "FULL" )
do
    echo "**start to repare $file"
    echo "innobackupex --apply-log --redo-only --use-memory=4G $RESTORE_DIR/$fullFileName --incremental-dir=$RESTORE_DIR/$file"
    innobackupex --apply-log --redo-only --use-memory=4G "$RESTORE_DIR/$fullFileName" --incremental-dir="$RESTORE_DIR/$file"

    echo "**end to repare $file"

done

echo "**start to copy back data to mysql"
rm -rf $MY_DATA_DIR
mkdir $MY_DATA_DIR
mkdir $MY_SOCK_DIR
mkdir $MY_LOG_DIR

 #将数据恢复到mysql中
echo "innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName"
innobackupex --defaults-file=$RESTORE_MY_CNF --copy-back $RESTORE_DIR/$fullFileName
echo "**end to copy back data to mysql"

chown -R mysql:mysql $MY_DATA_DIR
chown -R mysql:mysql $MY_SOCK_DIR
chown -R mysql:mysql $MY_LOG_DIR

echo "All data has been restored! Try to start mysql now"

echo "mysqld_multi --defaults-file=$MY_CNF start 1"

使用方法很简单,配置好后,直接运行sh restore.sh就可以了。

git svn 命令集合

git svn [options] [arguments]

Available commands:

blame            Show what revision and author last modified   each line of a file  
                   --git-format 
branch           Create a branch in the SVN repository      
                   --commit-url <arg>
                   --destination, -d <arg>
                   --dry-run, -n 
                   --message, -m <arg>
                   --parents 
                   --tag, -t 
                   --username <arg>
clone            Initialize and fetch revisions
                   --add-author-from 
                   --authors-file, -A <arg>
                   --authors-prog <arg>
                   --branches, [email protected] 
                   --config-dir <arg>
                   --follow-parent, --follow! 
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --localtime 
                   --log-window-size <num>
                   --minimize-url, --m! 
                   --no-auth-cache 
                   --no-checkout 
                   --no-metadata 
                   --placeholder-filename <arg>
                   --prefix <arg>
                   --preserve-empty-dirs 
                   --quiet, --q+ 
                   --repack-flags, --repack-args, --repack-opts <arg>
                   --repack <num>
                   --revision, -r <arg>
                   --rewrite-root <arg>
                   --rewrite-uuid <arg>
                   --shared <arg>
                   --stdlayout, -s 
                   --tags, [email protected] 
                   --template <arg>
                   --trunk, -T <arg>
                   --use-log-author 
                   --use-svm-props 
                   --use-svnsync-props 
                   --username <arg>
commit-diff      Commit a diff between two trees
                   --copy-similarity, -C <num>
                   --edit, -e 
                   --file, -F <arg>
                   --find-copies-harder 
                   -l <num>
                   --message, -m <arg>
                   --revision, -r <arg>
                   --rmdir 
create-ignore    Create a .gitignore per svn:ignore
                   --revision, -r <num>
dcommit          Commit several diffs to merge with upstream
                   --add-author-from 
                   --authors-file, -A <arg>
                   --authors-prog <arg>
                   --commit-url <arg>
                   --config-dir <arg>
                   --copy-similarity, -C <num>
                   --dry-run, -n 
                   --edit, -e 
                   --fetch-all, --all 
                   --find-copies-harder 
                   --follow-parent, --follow! 
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --interactive, -i 
                   -l <num>
                   --localtime 
                   --log-window-size <num>
                   --mergeinfo <arg>
                   --merge, -m, -M 
                   --no-auth-cache 
                   --no-checkout 
                   --no-rebase 
                   --quiet, --q+ 
                   --repack-flags, --repack-args, --repack-opts <arg>
                   --repack <num>
                   --revision, -r <num>
                   --rmdir 
                   --strategy, -s <arg>
                   --use-log-author 
                   --username <arg>
                   --verbose, -v 
fetch            Download new revisions from SVN
                   --add-author-from 
                   --authors-file, -A <arg>
                   --authors-prog <arg>
                   --config-dir <arg>
                   --fetch-all, --all 
                   --follow-parent, --follow! 
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --localtime 
                   --log-window-size <num>
                   --no-auth-cache 
                   --no-checkout 
                   --parent, -p 
                   --quiet, --q+ 
                   --repack-flags, --repack-args, --repack-opts <arg>
                   --repack <num>
                   --revision, -r <arg>
                   --use-log-author 
                   --username <arg>
find-rev         Translate between SVN revision numbers and tree-ish
                   --after 
                   --before 
gc               Compress unhandled.log files in .git/svn and remove index files in .git/svn
info             Show info about the latest SVN revision
     on the current branch
                   --url 
init             Initialize a repo for tracking (requires URL argument)
                   --branches, [email protected] 
                   --config-dir <arg>
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --minimize-url, --m! 
                   --no-auth-cache 
                   --no-metadata 
                   --prefix <arg>
                   --rewrite-root <arg>
                   --rewrite-uuid <arg>
                   --shared <arg>
                   --stdlayout, -s 
                   --tags, [email protected] 
                   --template <arg>
                   --trunk, -T <arg>
                   --use-svm-props 
                   --use-svnsync-props 
                   --username <arg>
log              Show commit logs
                   --authors-file, -A <arg>
                   --color 
                   --incremental 
                   --limit <num>
                   --non-recursive 
                   --oneline 
                   --pager <arg>
                   --revision, -r <arg>
                   --show-commit 
                   --verbose, -v 
migrate          Migrate configuration/metadata/layout from
        previous versions of git-svn
                   --config-dir <arg>
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --minimize 
                   --no-auth-cache 
                   --username <arg>
mkdirs           recreate empty directories after a checkout
                   --revision, -r <num>
propget          Print the value of a property on a file or directory
                   --revision, -r <num>
proplist         List all properties of a file or directory
                   --revision, -r <num>
rebase           Fetch and rebase your working directory
                   --add-author-from 
                   --authors-file, -A <arg>
                   --authors-prog <arg>
                   --config-dir <arg>
                   --dry-run, -n 
                   --fetch-all, --all 
                   --follow-parent, --follow! 
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   --localtime 
                   --local, -l 
                   --log-window-size <num>
                   --merge, -m, -M 
                   --no-auth-cache 
                   --no-checkout 
                   --preserve-merges, -p 
                   --quiet, --q+ 
                   --repack-flags, --repack-args, --repack-opts <arg>
                   --repack <num>
                   --strategy, -s <arg>
                   --use-log-author 
                   --username <arg>
                   --verbose, -v 
reset            Undo fetches back to the specified SVN revision
                   --parent, -p 
                   --revision, -r <arg>
set-tree         Set an SVN repository to a git tree-ish
                   --add-author-from 
                   --authors-file, -A <arg>
                   --authors-prog <arg>
                   --config-dir <arg>
                   --copy-similarity, -C <num>
                   --edit, -e 
                   --find-copies-harder 
                   --follow-parent, --follow! 
                   --ignore-paths <arg>
                   --ignore-refs <arg>
                   --include-paths <arg>
                   -l <num>
                   --localtime 
                   --log-window-size <num>
                   --no-auth-cache 
                   --no-checkout 
                   --quiet, --q+ 
                   --repack-flags, --repack-args, --repack-opts <arg>
                   --repack <num>
                   --rmdir 
                   --stdin 
                   --use-log-author 
                   --username <arg>
show-externals   Show svn:externals listings
                   --revision, -r <num>
show-ignore      Show svn:ignore listings
                   --revision, -r <num>
tag              Create a tag in the SVN repository
                   --commit-url <arg>
                   --destination, -d <arg>
                   --dry-run, -n 
                   --message, -m <arg>
                   --parents 
                   --username <arg>

Web性能测试中的几个概念(转)

每秒查询率QPS:对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,即每秒请求数,即最大吞吐能力。

并发数:并发数和QPS是不同的概念,一般说QPS会说多少并发用户下QPS,当QPS相同时,并发用户数越大,网站并发处理能力越好。当并发用户数过大时,会造成进程(线程)频繁切换,反正真正用于处理请求的时间变少,每秒能够处理的请求数反而变少,同时用户的请求等待时间也会变大。 找到最佳线程数能够让web系统更稳定,效率更高。

通过QPS和pv计算部署服务器台数:
单台服务器每天PV计算
公式1:每天总PV = QPS * 3600 * 6
公式2:每天总PV = QPS * 3600 * 8
服务器计算
服务器数量 = ceil( 每天总PV / 单台服务器每天总PV )
峰值QPS和机器计算公式
原理:每天80%的访问集中在20%的时间里,这20%时间叫做峰值时间
公式:( 总PV数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间每秒请求数(QPS)
机器:峰值时间每秒QPS / 单台机器的QPS = 需要的机器
问:每天300w PV 的在单台机器上,这台机器需要多少QPS?
答:( 3000000 * 0.8 ) / (86400 * 0.2 ) = 139 (QPS)
问:如果一台机器的QPS是58,需要几台机器来支持?
答:139 / 58 = 3

/bin/rm: argument list too long 的解决方法

今天发现var目录又满了,进去用 du -smh * | sort -n一看,发现 /var/spool/mqueue 占的空间很大,从名字上来看,这个目录是存放系统邮件队列的,为什么队列里堆积了这么多的邮件呢?不清楚,没办法,直接删吧,rm -rf *结果报错了,bash: /bin/rm: Argument list too long,从字面意思上看也知道是参数列表过长,其实就是文件夹里的文件太多了,不能一下子删除,官方的解释是:因为 Linux 内核中所有 exec 系函数(execl, execlp, execle 等)最终调用的都是 execve() , 而execve 是通过一块 128k 的内存空间来保存用以传递给新进程的命令参数和环境变量。当像 rm 这些命令产生的命令行参数超过 128k 的时候, Linux kernel 就报 E2BIG 错误了。

解决方法是:用 find 命令先找出希望删除的文件名再用管道批次传递给 rm 去删除。比如要删除 sendmail 堆积起来的mqueue 目录下的文件:
find /var/spool/mqueue/ -type f -name '*' -print0 | xargs -0 rm
(-0 参数防止文件名中有空格导致被 rm 认作是两个不同的文件。)

又学到了一招。

what's heros say?

风暴之灵:Hunger is the best sauce. 饥饿是最好的调味品。

黑暗游侠:I am here, as always. 我一直都在。My bow is ready.箭已上弦。 Where shall my blood be spilt. 我应该为谁流血。 Will this ever end? 永无终结。

变体精灵:Fall like the leaves, in fall. 像秋天的叶子般凋零吧。

地卜师:The calm, before the storm. 暴风雨前的宁静。

幽鬼:Justice will be served! 公正终将得以伸张。

流浪剑客:I must feast on souls. 欢迎参加这场灵魂的盛宴。

仙女龙:The restless dead await. 无尽的死亡在等待。

鱼人守卫:Look before you leap. 三思而后行。

食尸鬼:History repeats itself. 历史会重演。

痛苦女王:Oh, that’s naughty. 哦~你真坏。

海军上将:I live to serve. I live to serve all believers. 我为服务信徒而生。

灵魂守卫:Easily. 放松。 Is that all? 仅此而已? Hardly a challenge. 轻而易举。 I'm blind but not deaf. 我失明,但仍能听见。

蛇发女妖:I stand ready. 我准备好了。 Time is short, mortal. 凡人,你的时间不多了。

娜迦海妖:My soul is yours. 我的灵魂是你的。

幻影长矛手:Taste the fury of the Warsong! 尝尝战歌氏的怒火吧!

地精修补匠:I'm all geared up. 我武装好了。

斧王:I came from the darkness, you son of a bitch! 我来自黑暗,你这***的。

炼金术士:Care for a cocktail? 想要鸡尾酒吗? Don't rush me. 不要催我。

死亡先知:You call to me? 你在呼唤我? If that is your wish. 如果那是你的意愿。

地狱领主:I am the darkness! 我就是黑暗! As you order! 随时候命!

兽王:I hear the call of the wild. 我听到了大自然的呼唤。 What lies before me? 前面是什么?

影魔:You’re’ere I live. 你来了,我便得到了生命。 Fire away. 退下。 Your soul is mine. 你的灵魂属于我。

地穴刺客:Make your choice. 做出选择吧。 The time is now! 是时候了!

德鲁伊:I’m awake, I’m awake! 我醒来了! Our time is short! 时间紧迫!

敌法师:I'm blind not deaf. 我失明,但仍能听见。 We must act! 我们必须行动了! At last. 终于来了。 Your blood is mine. 你的血是我的。 Run for your life. 逃命去吧。 At last, we should have revenge. 最终,我们要报仇了。

屠夫:Us hear and obey! 我们听从命令! What we do? 要我们做什么?(另有DotA唯一的全图吼声:Fresh meat!好新鲜的肉!)

死灵法师:The shadows beckon! 阴影在召唤! The restless dead await! 无尽的死亡在等待!

暗夜魔王:The night beckons! 黑夜在召唤! What is it now? 现在呢?

巫妖:Direct me! 请指示! So be it! 正是如此! My talent are yours. 运用我的智慧吧。

秀逗魔导师:Do you require aid, human? 需要帮助吗,人类? Help me, help you. 帮助我就是帮助你。 What a good idea. 好主意。

哥布林工程师:Oh my baby! 哦,我的孩子。

光之守卫:Well? 什么? What is it now? 现在是什么样?****! 妈的!(大法真粗俗…)

剑圣:I am yours! 我是你的! I hear and obey! 我听从命令!

撼地神牛:Death to the enemy! 部落的敌人去死吧! Oh no. 哦,不。 May my ancestors watch over me! 我的祖先在看着我!

元素法师:Child’s play. 小儿科。

复仇之魂:For the Lich King! 为了巫妖王!

隐形刺客:Delicious. 美味。

矮人火枪手:Locked and loaded. 上膛完毕。 Fire! 开火!

混沌骑士:Prefect. 完美。

圣骑士:Future is ours. 未来是我们的。