在创建应用一致性快照时,系统会暂停正在写入的数据,确保快照捕获到的数据的完整性和一致性。通过应用一致性快照回滚云盘,数据不会损坏和丢失,可以确保应用(例如MySQL)处于一致性的启动状态。本文以在Linux实例部署MySQL数据库为例,验证应用一致性快照的数据备份效果。
前提条件
ECS实例的操作系统是CentOS 7.6及以上版本、Ubuntu 18.04及以上版本或Alibaba Cloud Linux 2。
ECS实例的云盘类型是ESSD云盘,且云盘未开启多重挂载功能。
ECS实例已安装云助手Agent。具体操作,请参见安装云助手Agent。
您已安装MySQL数据库,并知悉数据库的登录名及密码。具体操作,请参见部署MySQL数据库(Linux)。
操作步骤
本操作以CentOS 7.9操作系统安装的MySQL 8.0来验证应用一致性快照的数据备份效果为例,请您根据实际环境进行操作。
步骤一:准备数据库验证环境
准备数据库验证环境(数据库表、存储过程等),用于后续验证应用程序与数据库的交互是否正常。
使用root用户远程连接ECS实例。
具体操作,请参见通过密码或密钥认证登录Linux实例。
创建测试脚本(/root/test.sql)。
创建并打开测试脚本(/root/test.sql)。
vim /root/test.sql
输入
i
,进入编辑模式。编写验证的SQL脚本。
SQL脚本中内容包含创建数据库表(PointInTime)及验证存储过程(TestPIT),具体内容如下所示。
USE AdventureWorks; CREATE TABLE PointInTime(id int, t datetime); DELIMITER $$ CREATE PROCEDURE `TestPIT`() BEGIN DECLARE i int; SET i=1; WHILE i < 180 DO INSERT INTO PointInTime VALUES(i, now()); SELECT SLEEP(1); SET i=i+1; END WHILE; END $$ DELIMITER ;
按
Esc
键,并输入:wq
,然后按回车键,退出并保存内容。
登录MySQL数据库。
输入以下命令,按回车键,并根据界面提示输入MySQL密码。
mysql -u <mysqlUserName> -p
其中
<mysqlUserName>
需替换为您的MySQL用户名。创建新的数据库AdventureWorks。
CREATE DATABASE AdventureWorks;
执行测试脚本。
source /root/test.sql
退出数据库。
exit
步骤二:创建prescript.sh和postscript.sh脚本
此操作介绍如何为MySQL创建prescript.sh和postscript.sh脚本,在创建应用一致性快照时需要使用这两个脚本。
本文中使用的prescript.sh和postscript.sh脚本,脚本内容仅为验证示例使用。如果您需要为自己的业务应用创建应用一致性快照,需要根据业务场景编写对应的prescript.sh和postscript.sh脚本。
使用root用户远程连接ECS实例。
具体操作,请参见通过密码或密钥认证登录Linux实例。
创建/tmp/prescript.sh脚本并写入脚本内容。
使用root用户创建/tmp/prescript.sh。
vim /tmp/prescript.sh
输入
i
,进入编辑模式。在脚本中根据应用自定义脚本内容。
本文中使用以下脚本内容:
TIMESTAMP=`date +%s` MYSQL_TEMP_FILE_NAME="/tmp/mysqlfreeze${TIMESTAMP}.tmp" LOG_FILE_NAME="/tmp/mysqlfreeze${TIMESTAMP}.log" # 设置您的MySQL用户名 export MYSQL_USER="$MYSQL_USER" # 设置您的MySQL密码 export MYSQL_PWD="$MYSQL_PASSWORD" function Log() { echo "$1" echo "$1" >> ${LOG_FILE_NAME} } function ExitWithResult() { Log "[INFO]:mysql freeze result is $1." exit $1 } function Main() { Log "*********************************************************************" Log "[INFO]:Begin to freeze mysql." which mysql if [ $? -ne 0 ] then Log "[INFO]:mysql is not installed." ExitWithResult 0 fi systemctl status mysqld.service | grep "inactive (dead)" if [ $? -ne 1 ] then Log "[ERROR]:mysql is not running." ExitWithResult 0 fi mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e "show processlist;" > "${MYSQL_TEMP_FILE_NAME}" 2>&1 if [ $? -ne 0 ] then cat ${MYSQL_TEMP_FILE_NAME} >>"${LOG_FILE_NAME}" [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:Show process list failed." ExitWithResult 1 fi process_id=`cat ${MYSQL_TEMP_FILE_NAME} | grep "select 1 and sleep(25)" | awk -F " " '{print $1}'` if [ "$process_id" != "" ] then cat ${MYSQL_TEMP_FILE_NAME} >>"${LOG_FILE_NAME}" [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:MySQL already been freezed " ExitWithResult 1 fi cat ${MYSQL_TEMP_FILE_NAME} Log "[INFO]:Try to execute flush tables command" echo "flush tables with read lock;select 1 and sleep(25);" | nohup mysql -u$MYSQL_USER >> "${LOG_FILE_NAME}" 2>&1 & if [ $? -ne 0 ] then Log "[ERROR]:Freeze mysql failed." ExitWithResult 1 fi Log "[INFO]:Flush tables command execute success" checkTime=0 while [ 1 ] do mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e "show processlist;" > "${MYSQL_TEMP_FILE_NAME}" 2>&1 if [ $? -ne 0 ] then cat ${MYSQL_TEMP_FILE_NAME} >>"${LOG_FILE_NAME}" [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:Show process list failed." ExitWithResult 1 fi cat ${MYSQL_TEMP_FILE_NAME} process_id=`cat ${MYSQL_TEMP_FILE_NAME} | grep "select 1 and sleep(25)" | awk -F " " '{print $1}'` if [ "$process_id" = "" ] then checkTime=`expr $checkTime + 1` Log "[INFO]:Mysql is not freeze. checkTime is ${checkTime}" sleep 1 else Log "[INFO]:Found sleep command in processlist,freeze success" break fi if [ $checkTime -eq 10 ] then cat "${MYSQL_TEMP_FILE_NAME}" >>"${LOG_FILE_NAME}" 2>&1 freeze_id=`cat ${MYSQL_TEMP_FILE_NAME} | grep "flush tables with read lock" | awk -F " " '{print $1}'` mysql -u$MYSQL_USER -e "kill $freeze_id;" >> "${LOG_FILE_NAME}" 2>&1 if [ $? -ne 0 ] then Log "[ERROR]:Thaw mysql failed." fi [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:Mysql is not freeze. Will return error" ExitWithResult 1 fi done [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[INFO]:Finish freeze mysql." ExitWithResult 0 } Main
在脚本中,您需要修改以下参数信息:
$MYSQL_USER
:修改为MySQL用户名。$MYSQL_PASSWORD
:修改为MySQL密码。
按
Esc
键,并输入:wq
,然后按回车键,退出并保存内容。为脚本设置仅root用户读、写和执行权限。
重要为了保证脚本的执行安全,请确保脚本仅root用户具有读、写及执行权限,即权限为700,否则执行脚本时会判断失败。
chmod 700 /tmp/prescript.sh
创建/tmp/postscript.sh脚本并写入脚本内容。
使用root用户创建/tmp/postscript.sh。
vim /tmp/postscript.sh
输入
i
,进入编辑模式。在脚本中根据应用自定义脚本内容。
本文中使用以下脚本内容:
TIMESTAMP=`date +%s` MYSQL_TEMP_FILE_NAME="/tmp/mysqlthaw${TIMESTAMP}.tmp" LOG_FILE_NAME="/tmp/mysqlthaw${TIMESTAMP}.log" # 设置您的MySQL用户名 export MYSQL_USER="$MYSQL_USER" # 设置您的MySQL密码 export MYSQL_PWD="$MYSQL_PASSWORD" function Log() { echo "$1" echo "$1" >> ${LOG_FILE_NAME} } function ExitWithResult() { Log "[INFO]:mysql unfreeze result is $1." exit $1 } function Main() { Log "*********************************************************************" Log "[INFO]:Begin to thaw mysql." which mysql if [ $? -ne 0 ] then Log "[INFO]:mysql is not installed." ExitWithResult 0 fi systemctl status mysqld.service | grep "inactive (dead)" if [ $? -ne 1 ] then Log "[ERROR]:mysql is not running." ExitWithResult 0 fi mysql -u$MYSQL_USER -e "show processlist;" > "${MYSQL_TEMP_FILE_NAME}" 2>&1 if [ $? -ne 0 ] then cat ${MYSQL_TEMP_FILE_NAME} >>"${LOG_FILE_NAME}" [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:show process list failed." ExitWithResult 1 fi Log "[INFO]:show process list success." cat ${MYSQL_TEMP_FILE_NAME} process_ids=`cat ${MYSQL_TEMP_FILE_NAME} | grep "select 1 and sleep(25)" | awk -F " " '{print $1}'` if [ "$process_ids" = "" ] then [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:Get freeze process_id failed." ExitWithResult 1 fi cat ${MYSQL_TEMP_FILE_NAME} | grep "select 1 and sleep(25)" | awk -F " " '{print $1}'| while read pid do Log "[INFO]:Try to stop sql process ${pid}." mysql -u$MYSQL_USER -e "kill $pid;" >> "${LOG_FILE_NAME}" 2>&1 if [ $? -ne 0 ] then [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[ERROR]:Thaw mysql failed.PIDs is ${process_ids}" ExitWithResult 1 fi Log "[INFO]:Stop sql process ${pid} success." done [ -f ${MYSQL_TEMP_FILE_NAME} ] && rm -rf ${MYSQL_TEMP_FILE_NAME} Log "[INFO]:Finish thaw mysql." ExitWithResult 0 } Main
在脚本中,您需要修改以下参数信息:
$MYSQL_USER
:修改为MySQL用户名。$MYSQL_PASSWORD
:修改为MySQL密码。
按
Esc
键,并输入:wq
,然后按回车键,退出并保存内容。为脚本设置仅root用户的读、写和执行权限。
重要为了保证脚本的执行安全,请确保脚本仅root用户具有读、写及执行权限,权限为700,否则执行脚本时会判断失败。
chmod 700 /tmp/postscript.sh
进入/tmp目录查看脚本权限是否正确。
cd /tmp ls -l
结果如下所示,表示脚本权限正确。
步骤三:为ECS实例授予RAM角色
开启应用一致性快照前,必须先为ECS实例配置相关的RAM角色,赋予ECS实例查询快照、创建快照等相关权限。
登录RAM控制台。
创建应用一致性快照相关的RAM角色、为RAM角色授权并授予ECS实例。具体操作,请参见创建ECS实例RAM角色并授予给ECS。其中:
RAM角色:示例名称为AppSnapshotRoleName
可信实体类型:阿里云服务
自定义权限策略:示例名称为AppSnapshotPolicy,内容如下:
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "ecs:DescribeSnapshot*", "ecs:CreateSnapshot*", "ecs:TagResources", "ecs:DescribeDisks" ], "Resource": [ "*" ], "Condition": {} } ] }
该策略具有查询快照相关信息、创建快照、设置标签和查询云盘信息等权限。
步骤四:在数据库中调用存储过程(TestPIT)
应用一致性快照能够暂停数据库状态,在创建快照前调用数据库存储过程用于模拟数据插入操作,后续创建应用一致性快照时,方便查看数据库的暂停效果和回滚数据后的效果。
登录MySQL数据库。
输入以下命令,按回车键,并根据界面提示输入MySQL密码。
mysql -u <mysqlUserName> -p
其中
<mysqlUserName>
需替换为您的MySQL用户名。切换到AdventureWorks数据库。
USE AdventureWorks;
调用存储过程(TestPIT)。
CALL TestPIT;
重要在验证过程中,您需要在TestPIT运行(大概3分钟左右)完成前创建应用一致性快照,否则不好验证应用一致性快照的效果。
步骤五:通过控制台创建应用一致性快照
此操作介绍如何在控制台为MySQL数据库所在的Linux实例创建应用一致性快照。
进入ECS实例列表页面。
登录ECS管理控制台。
在左侧导航栏,选择 。
在顶部菜单栏处,选择目标ECS实例所在地域。
找到目标实例,在操作列选择
。在创建快照一致性组对话框中,设置参数。
选择需要创建快照一致性组的ESSD云盘并设置其他快照参数。
设置应用一致性快照。
选中启用应用一致性快照,并设置prescript.sh脚本和postscript.sh脚本的路径信息。脚本路径信息需要和前面步骤创建的脚本路径一致。具体脚本信息,请参见步骤二:创建prescript.sh和postscript.sh脚本。
单击确定。
创建后会返回云助手命令ID和命令执行ID,您可以根据命令执行ID查看创建结果。
步骤六:验证是否成功创建应用一致性快照
此操作介绍如何在ECS管理控制台查看应用一致性快照创建结果,并在数据库中查看数据暂停提交的效果。
在云助手页面,查看命令执行结果。
在云助手的返回信息中,查看应用暂停时间。
在返回信息中,找到prescript.sh脚本开始时间和postscript.sh脚本完成时间。
prescript.sh脚本开始时间为
2024-08-27 15:27:55
。postscript.sh脚本结束执行时间为
2024-08-27 15:27:57
。
在快照页面查看快照一致性组和云盘快照信息。
在左侧导航栏,选择 。
单击快照一致性组页签,找到已创建的快照一致性组,单击快照一致性组ID查看快照详情。
在快照信息区域,根据快照的标签信息,检查是否成功创建应用一致性快照。
示例中云盘快照的标签显示
APPConsistent:True
,表示创建的是应用一致性快照。
连接MySQL数据库,查看数据暂停提交时间。
远程连接ECS实例。
具体操作,请参见通过密码或密钥认证登录Linux实例。
登录MySQL数据库。
输入以下命令,按回车键,并根据界面提示输入MySQL密码。
mysql -u <mysqlUserName> -p
其中
<mysqlUserName>
需替换为您的MySQL用户名。查询数据库表PointInTime的内容。
USE AdventureWorks; SELECT * FROM PointInTime;
查询结果如下所示,在prescript.sh脚本和postscript.sh脚本执行过程中(
2024-08-27 15:27:55
~2024-08-27 15:27:57
),数据库写入暂停提交。说明应用一致性快照可以确保在快照创建瞬间,应用程序与数据库数据的一致性。
步骤七:验证通过应用一致性快照恢复数据的效果
先通过应用一致性快照恢复数据,然后将MySQL数据最后写入时间与prescript.sh脚本执行的时间做对比,来判断应用一致性快照的数据恢复是否正确。
在ECS管理控制台,通过上面创建的快照一致性组回滚ECS实例。
具体操作,请参见通过快照一致性组回滚云盘。
登录MySQL并查询数据库表PointInTime的内容。
远程连接ECS实例。
具体操作,请参见通过密码或密钥认证登录Linux实例。
登录MySQL数据库。
输入以下命令,按回车键,并根据界面提示输入MySQL密码。
mysql -u <mysqlUserName> -p
其中
<mysqlUserName>
需替换为您的MySQL用户名。查询数据库表PointInTime的内容。
USE AdventureWorks; SELECT * FROM PointInTime;
查询结果如下所示。
数据库暂停成功之前会停止插入数据。使用应用一致性的快照一致性组恢复数据后,最后一条数据对应的时间为
2024-08-27 15:27:54
,早于步骤六中查询的暂停时间点2024-08-27 15:27:55
。因此证明关于MySQL的应用一致性快照备份的结果是正确的。