文档

为MySQL创建应用一致性快照最佳实践(Linux)

更新时间:

通过应用一致性快照回滚云盘,数据不会损坏和丢失,可以避免数据库启动时日志回滚,确保应用(例如MySQL)处于一致性的启动状态。本文以MySQL数据库为例,介绍如何创建Linux实例的应用一致性快照,并验证应用一致性快照的数据备份效果。

前提条件

  • ECS实例的操作系统属于CentOS 7.6及以上版本、Ubuntu 18.04及以上版本或Alibaba Cloud Linux 2/3。

  • ECS实例中的云盘必须是ESSD PL0/1/2/3或者ESSD AutoPL类型,且文件系统为EXT3、EXT4、XFS或NTFS。同时,不支持网络文件系统和共享文件系统。

  • ECS实例已安装云助手Agent。具体操作,请参见安装云助手Agent

    说明

    2017年12月01日之后使用公共镜像创建的ECS实例,已默认预装云助手Agent

  • 您已经安装MySQL数据库。具体操作,请参见部署MySQL数据库(Linux)

背景信息

关于应用一致性快照概念介绍,请参见创建应用一致性快照

本文使用的验证环境信息如下所示。

环境

说明

ECS实例规格

ecs.g7.large

操作系统

CentOS 7.9 64位

云盘

ESSD云盘

应用

MySQL 5.7

操作步骤

步骤一:准备数据库验证环境

在操作之前,您已经安装好MySQL 5.7环境。本操作介绍如何准备数据库验证环境。

  1. 远程连接ECS实例。

    具体操作,请参见连接方式概述

  2. 创建测试脚本(/root/test.sql)。

    1. 创建并打开测试脚本(/root/test.sql)。

      vi /root/test.sql
    2. 输入i,进入编辑模式。

    3. 编写验证的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 ;
    4. Esc键,并输入:wq,然后按回车键,退出并保存内容。

  3. 登录MySQL数据库。

    输入以下命令,按回车键,并根据界面提示输入MySQL密码。

    mysql -u <mysqlUserName> -p

    其中<mysqlUserName>为您的MySQL用户名。

  4. 创建新的数据库AdventureWorks。

    CREATE DATABASE AdventureWorks;
  5. 执行测试脚本。

    source /root/test.sql
  6. 退出数据库。

    exit

步骤二:创建应用冻结脚本和应用解冻脚本

此操作介绍如何为MySQL创建应用冻结脚本和应用解冻脚本,在创建应用一致性快照时需要使用到这两个脚本。

说明

本文中使用的应用冻结脚本和应用解冻脚本,脚本内容仅为验证示例使用。如果您需要为自己的业务应用创建应用一致性快照,需要根据业务场景编写对应的应用冻结脚本和应用解冻脚本。

  1. 远程连接ECS实例。

    具体操作,请参见连接方式概述

  2. 创建应用冻结脚本(/tmp/prescript.sh)。

    1. 使用root用户创建/tmp/prescript.sh

      vi /tmp/prescript.sh
    2. 输入i,进入编辑模式。

    3. 在脚本中根据应用自定义脚本内容。

      本文中使用以下脚本内容:

      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密码。

    4. Esc键,并输入:wq,然后按回车键,退出并保存内容。

    5. 为脚本设置仅root用户读、写和执行权限。

      说明

      为了保证脚本的执行安全,请确保脚本仅root用户具有读、写及执行权限,即权限为700,否则执行脚本时会判断失败。

      chmod 700 /tmp/prescript.sh
  3. 创建应用解冻脚本(/tmp/postscript.sh)。

    1. 使用root用户创建/tmp/postscript.sh

      vi /tmp/postscript.sh
    2. 输入i,进入编辑模式。

    3. 在脚本中根据应用自定义脚本内容。

      本文中使用以下脚本内容:

      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密码。

    4. Esc键,并输入:wq,然后按回车键,退出并保存内容。

    5. 为脚本设置仅root用户的读、写和执行权限。

      说明

      为了保证脚本的执行安全,请确保脚本仅root用户具有读、写及执行权限,权限为700,否则执行脚本时会判断失败。

      chmod 700 /tmp/postscript.sh
  4. 进入/tmp目录查看脚本权限是否正确。

    进入/tmp目录:

    cd /tmp

    查看脚本权限信息:

    ls -l

    结果如下所示。ls

步骤三:为ECS实例授予RAM角色

开启应用一致性快照前,必须先为ECS实例配置相关的RAM角色,赋予ECS实例查询快照、创建快照等相关权限。

  1. 使用阿里云账号登录RAM控制台

  2. 创建应用一致性快照相关的RAM角色。具体操作,请参见创建可信实体为阿里云服务的RAM角色

    示例为创建一个AppSnapshotRoleName的RAM角色。应用一致性ram角色

  3. 创建应用一致性快照相关权限策略。具体操作,请参见创建自定义权限策略

    快照权限

    创建一个AppSnapshotPolicy权限策略,具有查询快照相关信息、创建快照、设置标签和查询云盘信息等相关权限。您可以直接使用以下策略内容。

    {
        "Version": "1",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "ecs:DescribeSnapshot*",
                    "ecs:CreateSnapshot*",
                    "ecs:TagResources",
                    "ecs:DescribeDisks"
                ],
                "Resource": [
                    "*"
                ],
                "Condition": {}
            }
        ]
    }
  4. 为RAM角色(AppSnapshotRoleName)设置自定义权限(AppSnapshotPolicy)。具体操作,请参见为RAM角色授权

    授权

  5. 为目标实例设置RAM角色(AppSnapshotRoleName)。具体操作,请参见配置ECS实例RAM角色

步骤四:在数据库中调用存储过程(TestPIT)

此操作为创建快照前调用存储过程,方便后续操作时查看应用冻结效果和回滚数据后效果。

  1. 登录MySQL数据库。

    输入以下命令,按回车键,并根据界面提示输入MySQL密码。

    mysql -u <mysqlUserName> -p

    其中<mysqlUserName>为您的MySQL用户名。

  2. 切换到AdventureWorks数据库。

    USE AdventureWorks;
  3. 调用存储过程(TestPIT)。

    CALL TestPIT;
    重要

    在验证过程中,您需要在TestPIT运行完成前(大概3分钟左右)创建应用一致性快照,否则不好验证应用一致性快照的效果。

步骤五:通过控制台创建应用一致性快照

此操作介绍如何在控制台为MySQL数据库所在的Linux实例创建应用一致性快照。

  1. 进入ECS实例列表页面。

    1. 登录ECS管理控制台

    2. 在左侧导航栏,选择实例与镜像 > 实例

    3. 在顶部菜单栏处,选择目标ECS实例所在地域。image.png

  2. 找到目标实例,在操作列中选择更多 > 云盘与镜像 > 创建快照一致性组

  3. 创建快照一致性组对话框中,设置参数。

    1. 选择需要创建快照一致性组的ESSD云盘并设置其他快照参数。

    2. 设置应用一致性快照。

      选中启用应用一致性快照,并设置应用冻结脚本和应用解冻脚本的路径信息。脚本路径信息需要和前面步骤创建的脚本路径一致。具体脚本信息,请参见步骤二:创建应用冻结脚本和应用解冻脚本

  4. 单击确定

  5. 创建后会返回云助手命令ID和命令执行ID,您可以根据命令执行ID查看创建结果。命令执行id

步骤六:验证是否成功创建应用一致性快照

此操作介绍如何在ECS管理控制台查看应用一致性快照创建结果,并在数据库中查看数据暂停提交的效果。

  1. 云助手页面,查看命令执行结果。

    1. 登录ECS管理控制台

    2. 在左侧导航栏,选择运维与监控 > 发送命令/文件(云助手)

    3. 单击命令执行结果页签。

    4. 单击目标命令执行ID,查看执行结果。

      查询结果

      如上图所示,ExitCode返回值为0,表示云助手上创建应用一致性快照执行成功,此时回显信息中显示创建应用一致性快照和快照一致性组ID。

      说明

      如果ExitCode返回值不为0,请根据ExitCode错误码信息排查相关问题。更多信息,请参见错误码信息

  2. 在云助手的返回信息中,查看应用冻结时间。

    在返回信息中,找到应用冻结脚本开始时间和应用解冻脚本完成时间。

    • 应用冻结脚本开始执行时间为2021-08-03 18:58:41冻结脚本

    • 应用解冻脚本结束执行时间为2021-08-03 18:58:44冻结脚本结束时间

  3. 快照页面查看快照一致性组和云盘快照信息。

    1. 在左侧导航栏,选择存储与快照 > 快照

    2. 单击快照一致性组页签,找到已创建的快照一致性组,单击快照一致性组ID查看组内详情。

    3. 云盘快照区域,单击云盘快照ID。

      image.png

    4. 云盘快照页签,根据快照的标签信息,检查是否成功创建应用一致性快照。

      示例中云盘快照的标签显示APPConsistent:True,表示创建的是应用一致性快照。

      image.png

  4. 连接MySQL数据库,查看数据暂停提交时间。

    1. 远程连接ECS实例。

      具体操作,请参见连接方式概述

    2. 登录MySQL数据库。

      输入以下命令,按回车键,并根据界面提示输入MySQL密码。

      mysql -u <mysqlUserName> -p

      其中<mysqlUserName>为您的MySQL用户名。

    3. 查询数据库表PointInTime的内容。

      SELECT * FROM PointInTime;

      查询结果如下所示,在应用冻结脚本和应用解冻脚本执行过程中,数据库写入暂停提交。select

步骤七:验证通过应用一致性快照恢复数据的效果

通过应用一致性快照恢复数据后,检查MySQL数据最后写入时间是在应用冻结脚本执行前,因此可以判断应用一致性快照的数据恢复有效果。

  1. 在ECS管理控制台,通过上面创建的快照一致性组回滚ECS实例。

    具体操作,请参见通过快照一致性组回滚云盘

  2. 登录MySQL并查询数据库表PointInTime的内容。

    1. 远程连接ECS实例。

      具体操作,请参见连接方式概述

    2. 登录MySQL数据库。

      输入以下命令,按回车键,并根据界面提示输入MySQL密码。

      mysql -u <mysqlUserName> -p

      其中<mysqlUserName>为您的MySQL用户名。

    3. 查询数据库表PointInTime的内容。

      SELECT * FROM PointInTime;

      查询结果如下所示。恢复数据

      数据库冻结成功之前会停止插入数据。使用应用一致性的快照一致性组恢复数据后,最后一条数据对应的时间为2021-08-03 18:58:40,早于步骤五中查询的冻结时间点2021-08-03 18:58:41。因此证明关于MySQL的应用一致性快照备份的结果是正确的。

  • 本页导读 (1)
文档反馈