全部產品
Search
文件中心

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

更新時間:Aug 31, 2024

在建立應用一致性快照時,系統會暫停正在寫入的資料,確保快照捕獲到的資料的完整性和一致性。通過應用一致性快照復原雲端硬碟,資料不會損壞和丟失,可以確保應用(例如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來驗證應用一致性快照的資料備份效果為例,請您根據實際環境進行操作。

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

準備資料庫驗證環境(資料庫表、預存程序等),用於後續驗證應用程式與資料庫的互動是否正常。

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

  2. 建立測試指令碼(/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,然後按斷行符號鍵,退出並儲存內容。

  3. 登入MySQL資料庫。

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

    mysql -u <mysqlUserName> -p

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

  4. 建立新的資料庫AdventureWorks。

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

    source /root/test.sql
  6. 退出資料庫。

    exit

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

此操作介紹如何為MySQL建立prescript.sh和postscript.sh指令碼,在建立應用一致性快照時需要使用這兩個指令碼。

重要

本文中使用的prescript.sh和postscript.sh指令碼,指令碼內容僅為驗證樣本使用。如果您需要為自己的業務應用建立應用一致性快照,需要根據業務情境編寫對應的prescript.sh和postscript.sh指令碼。

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

    具體操作,請參見通過密碼或密鑰認證登入Linux執行個體

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

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

      vim /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

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

    cd /tmp
    ls -l

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

    image

步驟三:為ECS執行個體授予RAM角色

開啟應用一致性快照前,必須先為ECS執行個體配置相關的RAM角色,賦予ECS執行個體查詢快照、建立快照等相關許可權。

  1. 登入RAM控制台

  2. 建立應用一致性快照相關的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)

應用一致性快照能夠暫停資料庫狀態,在建立快照前調用資料庫預存程序用於類比資料插入操作,後續建立應用一致性快照時,方便查看資料庫的暫停效果和復原資料後的效果。

  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. 設定應用一致性快照。

      選中啟用應用一致性快照,並設定prescript.sh指令碼和postscript.sh指令碼的路徑資訊。指令碼路徑資訊需要和前面步驟建立的指令碼路徑一致。具體指令碼資訊,請參見步驟二:建立prescript.sh和postscript.sh指令碼

  4. 單擊確定

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

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

此操作介紹如何在ECS管理主控台查看應用一致性快照建立結果,並在資料庫中查看資料暫停提交的效果。

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

    1. 登入ECS管理主控台

    2. 在左側導覽列,選擇營運與監控 > 雲助手

    3. 單擊命令執行結果頁簽。

    4. 單擊目標命令執行ID,查看執行結果。

      image

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

      說明

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

  2. 在雲助手的返回資訊中,查看應用暫停時間。

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

    • prescript.sh指令碼開始時間為2024-08-27 15:27:55image

    • postscript.sh指令碼結束執行時間為2024-08-27 15:27:57

      image

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

    1. 在左側導覽列,選擇儲存與快照 > 快照

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

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

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

      image

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

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

      具體操作,請參見通過密碼或密鑰認證登入Linux執行個體

    2. 登入MySQL資料庫。

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

      mysql -u <mysqlUserName> -p

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

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

      USE AdventureWorks;
      SELECT * FROM PointInTime;

      查詢結果如下所示,在prescript.sh指令碼和postscript.sh指令碼執行過程中(2024-08-27 15:27:55~2024-08-27 15:27:57),資料庫寫入暫停提交。說明應用一致性快照可以確保在快照建立瞬間,應用程式與資料庫資料的一致性。

      image

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

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

  1. 在ECS管理主控台,通過上面建立的快照一致性組復原ECS執行個體。

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

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

      具體操作,請參見通過密碼或密鑰認證登入Linux執行個體

    2. 登入MySQL資料庫。

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

      mysql -u <mysqlUserName> -p

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

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

      USE AdventureWorks;
      SELECT * FROM PointInTime;

      查詢結果如下所示。

      image

      資料庫暫停成功之前會停止插入資料。使用應用一致性的快照一致性組恢複資料後,最後一條資料對應的時間為2024-08-27 15:27:54,早於步驟六中查詢的暫停時間點2024-08-27 15:27:55。因此證明關於MySQL的應用一致性快照備份的結果是正確的。