全部產品
Search
文件中心

Elastic Compute Service:為MySQL建立應用一致性快照最佳實務(Linux)

更新時間:Apr 30, 2025

在建立應用一致性快照時,系統會暫停正在寫入的資料,確保快照捕獲到的資料的完整性和一致性。通過應用一致性快照恢複資料時,可以減少資料損毀和丟失的風險,可以確保應用(例如MySQL)能夠正常啟動,且資料與建立快照時的狀態一致。本文以在CentOS 7.9執行個體上部署MySQL 8.0資料庫為例,驗證應用一致性快照的資料備份效果。

前提條件

  • ECS執行個體的雲端硬碟類型是ESSD雲端硬碟,且雲端硬碟未開啟多重掛載功能

  • ECS執行個體處於運行中且雲助手運行正常。如何查看雲助手狀態請參見查看雲助手狀態及異常狀態處理

  • 您已安裝MySQL資料庫,並知悉資料庫的登入名稱及密碼。具體操作,請參見手動部署MySQL資料庫(Linux)

  • 已為ECS執行個體配置應用一致性快照相關的RAM角色以及自訂權限原則。具體操作,請參見建立RAM角色並授予給ECS執行個體

    說明

    建立應用一致性快照時,需要調用雲助手訪問ECS執行個體並執行命令,因此需要通過RAM角色授予雲助手相應的許可權。

    • RAM角色:自訂,例如AppSnapshotRoleName。

    • 自訂權限原則:策略內容如下,表示在建立快照過程中具有查詢快照、建立快照、設定標籤和查詢雲端硬碟資訊等相關許可權。

      {
          "Version": "1",
          "Statement": [
              {
                  "Effect": "Allow",
                  "Action": [
                      "ecs:DescribeSnapshot*",
                      "ecs:CreateSnapshot*",
                      "ecs:TagResources",
                      "ecs:DescribeDisks"
                  ],
                  "Resource": [
                      "*"
                  ],
                  "Condition": {}
              }
          ]
      }

操作思路

本實踐通過如下操作來驗證,如何通過建立應用一致性快照來確保在建立快照的瞬間,資料庫狀態與建立快照時的狀態一致,從而保證應用一致性。操作流程如下:

  1. 步驟一:建立prescript.sh和postscript.sh指令碼

    建立暫停(prescript.sh)和恢複(postscript.sh)資料庫寫操作的指令碼,用於暫停(建立快照前)和恢複(快照建立後)資料庫的寫操作,在建立應用一致性快照時需要使用這兩個指令碼。

  2. 步驟二:準備資料庫驗證環境

    建立資料庫表和預存程序,用於類比資料寫入操作,以便後續驗證應用一致性快照的效果。並在資料庫中調用預存程序(TestPIT)類比資料插入操作,後續建立應用一致性快照時,方便查看資料庫的暫停效果和復原資料後的效果。

  3. 步驟三:通過控制台建立應用一致性快照

    為MySQL資料庫所在的Linux執行個體建立應用一致性快照並應用暫停和恢複資料庫寫操作指令碼。建立應用一致性快照時,資料庫的寫操作會暫時停止,確保資料一致性。

  4. 步驟四:驗證應用一致性快照是否建立成功

    通過雲助手命令執行結果確認快照是否建立成功,並查看暫停和恢複資料庫寫操作的時間點。

  5. 步驟五:驗證通過應用一致性快照恢複資料的效果

    先通過應用一致性快照復原雲端硬碟,然後將MySQL資料庫最後寫入時間與資料庫暫停時間(prescript.sh指令碼執行時間)做對比,來判斷應用一致性快照的資料恢複是否正確。

    • 如果MySQL資料最後寫入時間早於prescript.sh指令碼執行時間:說明資料庫在暫停期間沒有寫入操作,資料恢複後MySQL資料最後寫入時間反映了暫停前的最後狀態,資料庫狀態與快照建立時的狀態一致。

    • 如果MySQL資料最後寫入時間等於或晚於prescript.sh指令碼執行時間:說明資料庫在暫停期間仍有寫入操作,應用一致性快照的效果未達到預期。

操作步驟

步驟一:建立prescript.sh和postscript.sh指令碼

重要

本文中使用的prescript.sh和postscript.sh指令碼內容僅為驗證樣本使用。如果您需要為自己的業務應用建立應用一致性快照,請根據實際業務編寫prescript.sh和postscript.sh指令碼。關於指令碼的更多介紹,請參見建立應用一致性快照

  1. 使用root使用者遠端連線ECS執行個體。

    具體操作,請參見使用Workbench工具以SSH協議登入Linux執行個體

  2. 建立/tmp/prescript.sh指令碼並寫入指令碼內容。

    1. 使用root使用者建立/tmp/prescript.sh

      vim /tmp/prescript.sh
    2. 輸入i,進入編輯模式。

    3. 在指令碼中根據應用自訂prescript.sh指令碼內容。

      展開查看prescript.sh指令碼內容:

      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

      vim /tmp/postscript.sh
    2. 輸入i,進入編輯模式。

    3. 在指令碼中根據應用自訂postscript.sh指令碼內容。

      展開查看postscript.sh指令碼內容:

      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目錄查看指令碼許可權是否正確。

    cd /tmp
    ls -l

    結果如下所示,表示指令碼許可權正確。

    image

步驟二:準備資料庫驗證環境

  1. 建立測試指令碼(/root/test.sql)。

    1. 建立並開啟測試指令碼(/root/test.sql)。

      vim /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,然後按斷行符號鍵,退出並儲存內容。

  2. 登入MySQL資料庫。

    輸入以下命令,按斷行符號鍵,並根據介面提示輸入MySQL密碼。

    mysql -u <mysqlUserName> -p

    其中<mysqlUserName>需替換為您的MySQL使用者名稱。

  3. 建立新的資料庫AdventureWorks。

    CREATE DATABASE AdventureWorks;
  4. 執行測試指令碼。

    source /root/test.sql
  5. 調用預存程序(TestPIT)。

    CALL TestPIT;
    重要

    在驗證過程中,您需要在TestPIT運行(大概3分鐘左右)完成前建立應用一致性快照,否則無法驗證應用一致性快照的效果。

步驟三:通過控制台建立應用一致性快照

  1. 訪問ECS控制台-快照一致性組

  2. 在頁面左側頂部,選擇目標資源所在的資源群組和地區。地區

  3. 快照一致性組頁簽中單擊建立快照一致性組

  4. 建立快照對話方塊中,設定快照一致性組參數。

    1. 資源類型預設選中執行個體

    2. 選擇單台執行個體及執行個體中的ESSD類型雲端硬碟。

    3. 展開進階配置,設定應用一致性快照。

      1. 選中啟用應用一致性快照

      2. 設定prescript.sh指令碼和postscript.sh指令碼的路徑與步驟一建立的指令碼路徑一致。

      3. 設定檔案系統IO暫停與恢複時間長度。

  5. 單擊確認

    建立後會返回雲助手命令執行ID,您可以根據命令執行ID查看建立結果。

步驟四:驗證應用一致性快照是否建立成功

  1. 單擊雲助手命令執行ID,在雲助手頁面查看執行結果。

    image

    如上圖所示,ExitCode傳回值為0,表示雲助手上建立應用一致性快照執行成功,此時回顯資訊中顯示建立應用一致性快照和快照一致性組ID。

    說明

    如果ExitCode傳回值不為0,請根據ExitCode錯誤碼資訊排查相關問題。更多資訊,請參見錯誤碼資訊

  2. 在雲助手的返回資訊中,查看資料庫的暫停和恢復點。

    在返回資訊中,找到prescript.sh指令碼開始時間和postscript.sh指令碼完成時間。

    • prescript.sh指令碼執行時間為2024-08-27 15:27:55,表示資料庫在該時間點暫停寫操作。image

    • postscript.sh指令碼執行時間為2024-08-27 15:27:57,表示資料庫在該時間點恢複寫操作。

      image

  3. 查看快照一致性組和雲端硬碟快照資訊。

    1. 訪問ECS控制台-快照一致性組

    2. 快照一致性組頁簽中找到已建立的快照一致性組,單擊快照一致性組ID查看快照詳情。

    3. 快照資訊地區,根據快照的標籤資訊,檢查是否成功建立應用一致性快照。

      樣本中雲端硬碟快照的標籤顯示APPConsistent:True,表示建立的是應用一致性快照。

      image

  4. 串連MySQL資料庫,查看資料暫停提交時間。

    1. 遠端連線ECS執行個體。

      具體操作,請參見使用Workbench工具以SSH協議登入Linux執行個體

    2. 登入MySQL資料庫。

      輸入以下命令,按斷行符號鍵,並根據介面提示輸入MySQL密碼。

      mysql -u <mysqlUserName> -p

      其中<mysqlUserName>需替換為您的MySQL使用者名稱。

    3. 查詢資料庫表PointInTime的內容。

      USE AdventureWorks;
      SELECT * FROM PointInTime;
    4. 在查詢結果中,查看資料庫的暫停時間點。

      您可以發現在2024-08-27 15:27:55~2024-08-27 15:27:58時間段內沒有插入資料。

      image

步驟五:驗證通過應用一致性快照恢複資料的效果

  1. 通過已建立的快照一致性組復原雲端硬碟。

  2. 重新登入MySQL資料庫並查詢資料庫表PointInTime的內容。

    1. 遠端連線ECS執行個體。

      具體操作,請參見使用Workbench工具以SSH協議登入Linux執行個體

    2. 登入MySQL資料庫。

      輸入以下命令,按斷行符號鍵,並根據介面提示輸入MySQL密碼。

      mysql -u <mysqlUserName> -p

      其中<mysqlUserName>需替換為您的MySQL使用者名稱。

    3. 查詢資料庫表PointInTime的內容。

      USE AdventureWorks;
      SELECT * FROM PointInTime;
    4. 在查詢結果中,查看恢複資料後資料庫最後一條記錄的時間點。

      您可以探索資料庫最後一條插入資料的時間點為2024-08-27 15:27:54,早於步驟四中查詢的暫停時間點2024-08-27 15:27:55,因此證明關於MySQL的應用一致性快照備份的結果是正確的。

      image