云下及他云數(shù)據(jù)庫備份管理
數(shù)據(jù)災(zāi)備(DBS)除支持阿里云數(shù)據(jù)庫和阿里云ECS自建數(shù)據(jù)庫的災(zāi)備外,還支持對云下及其他云平臺(tái)數(shù)據(jù)庫進(jìn)行災(zāi)備。
注意事項(xiàng)
若備份庫表存在表結(jié)構(gòu)不合理、大表、大字段等情況,備份實(shí)例的規(guī)格過小可能會(huì)導(dǎo)致后續(xù)備份實(shí)例資源不足,從而引發(fā)備份異常。因此,建議您在創(chuàng)建時(shí)選擇較高規(guī)格的備份實(shí)例,以免后續(xù)備份出現(xiàn)異常。
操作步驟
產(chǎn)品自動(dòng)備份
購買備份實(shí)例(邏輯備份)
- 登錄數(shù)據(jù)管理DMS 5.0。
在頂部菜單欄中,選擇
。說明若您使用的是極簡模式的控制臺(tái),請單擊控制臺(tái)左上角的圖標(biāo),選擇
。在上方選擇地域,在
頁簽下,單擊目標(biāo)數(shù)據(jù)源ID進(jìn)入數(shù)據(jù)源詳情頁。在備份策略頁面中,單擊配置備份策略。
在選擇備份計(jì)劃頁面單擊購買備份計(jì)劃,進(jìn)入DBS售賣頁。
配置如下參數(shù),單擊頁面右下角的立即購買。
配置項(xiàng)
說明
商品類型
請選擇備份實(shí)例(包年包月)。
備份實(shí)例地域
選擇要存放備份數(shù)據(jù)的地域。
說明請確保備份實(shí)例所在地域與ECS實(shí)例所在地域相同。
數(shù)據(jù)源類型
當(dāng)前僅支持MySQL。
規(guī)格
規(guī)格越高,備份與恢復(fù)的性能越高,支持的規(guī)格為:serverless(僅MySQL邏輯備份支持)、micro(入門型)、small(低配型)、medium(中配型)、large(高配型)、xlarge(高配型-無流量上限)。
說明如果數(shù)據(jù)庫實(shí)例(例如生產(chǎn)環(huán)境的數(shù)據(jù)庫)需要高性能的備份實(shí)例快速執(zhí)行備份與恢復(fù)任務(wù),建議選擇xlarge或large規(guī)格,獲取更高的備份恢復(fù)性能。
對備份恢復(fù)性能(速度)要求不高,您可以通過計(jì)算,選擇性價(jià)比最高的備份實(shí)例規(guī)格。更多信息,請參見如何選擇備份實(shí)例規(guī)格。
若備份庫表存在表結(jié)構(gòu)不合理、大表、大字段等情況,備份實(shí)例的規(guī)格過小可能會(huì)導(dǎo)致后續(xù)備份實(shí)例資源不足,從而引發(fā)備份異常。因此,建議您在創(chuàng)建時(shí)選擇較高規(guī)格的備份實(shí)例,以免后續(xù)備份出現(xiàn)異常。
備份方式
請選擇邏輯備份。
存儲(chǔ)空間
您創(chuàng)建時(shí)無需選擇容量,后續(xù)根據(jù)實(shí)際存入DBS內(nèi)置存儲(chǔ)中的數(shù)據(jù)量計(jì)費(fèi)。計(jì)費(fèi)詳情,請參見存儲(chǔ)費(fèi)用。
資源組
配置資源組。選擇默認(rèn)或自定義的資源組,方便備份實(shí)例管理。
購買數(shù)量
按需選擇購買數(shù)量,多個(gè)數(shù)據(jù)庫實(shí)例需要?jiǎng)?chuàng)建多個(gè)備份實(shí)例,例如您希望備份數(shù)據(jù)庫實(shí)例A與數(shù)據(jù)庫實(shí)例B,需要購買2個(gè)備份實(shí)例。
購買時(shí)長
選擇該備份實(shí)例的購買時(shí)長。
在確認(rèn)訂單頁面,確認(rèn)訂單信息,閱讀并選中服務(wù)協(xié)議,單擊去支付。
支付成功后,請返回步驟5的選擇備份計(jì)劃頁面,單擊已完成支付,即可查看到已創(chuàng)建的備份實(shí)例。
配置備份策略
在選擇備份計(jì)劃頁面選中待配置的備份實(shí)例,并單擊下一步。
在選擇庫表頁面,選中需要備份的庫或表,單擊移動(dòng)到已選擇對象框中,單擊提交。
提交成功后,您可以在備份策略頁面的邏輯備份頁簽下,單擊啟動(dòng)按鈕啟動(dòng)備份。
單擊啟動(dòng)后系統(tǒng)會(huì)立即發(fā)起一次全量和增量備份任務(wù)。
說明如果您期望先完成一些其他操作(例如修改備份策略等),您也可以選擇當(dāng)前暫不啟動(dòng)備份,但后續(xù)系統(tǒng)會(huì)根據(jù)備份策略在備份時(shí)間自動(dòng)啟動(dòng)備份。
用戶自動(dòng)備份
目前僅支持MySQL 5.5版本的數(shù)據(jù)源。
目前僅支持華東1(杭州)地域。
配置備份源以及上傳備份文件
- 登錄數(shù)據(jù)管理DMS 5.0。
在頂部菜單欄中,選擇
。說明若您使用的是極簡模式的控制臺(tái),請單擊控制臺(tái)左上角的圖標(biāo),選擇
。在上方選擇地域,在云下及他云數(shù)據(jù)庫頁簽下,根據(jù)數(shù)據(jù)源類型選擇新增入口。
單擊新增數(shù)據(jù)源,在彈出的對話框中,配置如下信息,并選中目標(biāo)備份計(jì)劃,單擊頁面右下角下一步。
配置項(xiàng)
說明
數(shù)據(jù)源名稱
建議配置具有業(yè)務(wù)意義的名稱,便于后續(xù)識(shí)別。
引擎類型
數(shù)據(jù)庫引擎類型,當(dāng)前僅支持MySQL。
引擎版本
選擇數(shù)據(jù)庫引擎的版本。
引擎參數(shù)
{"lower_case_table_names":1}
若沒有可選備份計(jì)劃,單擊購買備份計(jì)劃,進(jìn)入DBS售賣頁,購買備份計(jì)劃。
說明
商品類型
請選擇備份實(shí)例(包年包月),不支持按量付費(fèi)。
備份實(shí)例地域
選擇要存放備份數(shù)據(jù)的地域。
數(shù)據(jù)源類型
選擇MySQL。
規(guī)格
選擇xmicro。該規(guī)格提供的免費(fèi)數(shù)據(jù)量額度等,請參見備份計(jì)劃規(guī)格。
備份方式
選擇物理備份。
存儲(chǔ)空間
您創(chuàng)建時(shí)無需選擇容量,后續(xù)根據(jù)實(shí)際存入DBS內(nèi)置存儲(chǔ)中的數(shù)據(jù)量計(jì)費(fèi)。計(jì)費(fèi)詳情,請參見存儲(chǔ)費(fèi)用。
資源組
配置資源組。選擇默認(rèn)或自定義的資源組,方便備份實(shí)例管理。
購買數(shù)量
按需選擇購買數(shù)量,多個(gè)數(shù)據(jù)庫實(shí)例需要?jiǎng)?chuàng)建多個(gè)備份實(shí)例,例如您希望備份數(shù)據(jù)庫實(shí)例A與數(shù)據(jù)庫實(shí)例B,需要購買2個(gè)備份實(shí)例。
購買時(shí)長
選擇該備份實(shí)例的購買時(shí)長。
將備份集上傳到指定Bucket中。上傳方法,請參見用戶自動(dòng)備份數(shù)據(jù)上傳指南。
上傳完成后,單擊確定。
查看備份信息
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源ID。
配置備份策略
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源操作列的查看備份策略。
配置完成之后,單擊確定。
查看和下載備份數(shù)據(jù)
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源ID。
單擊左側(cè)導(dǎo)航欄中的備份數(shù)據(jù)。
說明用戶執(zhí)行上傳數(shù)據(jù)腳本并完成數(shù)據(jù)源新建后,當(dāng)用戶新產(chǎn)生一個(gè)備份集數(shù)據(jù),數(shù)據(jù)備份管理系統(tǒng)會(huì)同步這個(gè)備份數(shù)據(jù)到備份數(shù)據(jù)頁面。
單擊目標(biāo)備份集操作列的下載按鈕獲取下載鏈接,將備份集下載到本地。
創(chuàng)建恢復(fù)任務(wù)
恢復(fù)任務(wù)要求數(shù)據(jù)備份頁面上必須存在已生成的備份集,并且該備份集的狀態(tài)需為完成。
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源ID。
單擊左側(cè)導(dǎo)航欄中的備份數(shù)據(jù),單擊創(chuàng)建恢復(fù)任務(wù),手動(dòng)配置恢復(fù)參數(shù)。
參數(shù)名稱
說明
恢復(fù)任務(wù)名稱
請輸入本次恢復(fù)任務(wù)的名稱。建議配置具有業(yè)務(wù)意義的名稱,便于后續(xù)識(shí)別。
恢復(fù)位置
選擇恢復(fù)實(shí)例位置,默認(rèn)為恢復(fù)到RDS新實(shí)例。
數(shù)據(jù)庫所在位置
選擇數(shù)據(jù)庫所在位置,默認(rèn)為RDS。
實(shí)例地區(qū)
選擇實(shí)例所在地區(qū),目前僅支持華東1(杭州)地區(qū)。
VPC
選擇創(chuàng)建的專有網(wǎng)絡(luò)。
VSwitch
選擇創(chuàng)建的交換機(jī)。
實(shí)例系列
選擇恢復(fù)的實(shí)例系列。
實(shí)例規(guī)格
選擇恢復(fù)的實(shí)例規(guī)格。
存儲(chǔ)空間
選擇所需存儲(chǔ)空間。
恢復(fù)方式
選擇恢復(fù)方式,目前僅支持按時(shí)間點(diǎn)恢復(fù)。
還原時(shí)間
選擇還原時(shí)間,可選擇恢復(fù)時(shí)間范圍請參考恢復(fù)方式參數(shù)后的數(shù)據(jù)。
配置完成之后單擊提交,會(huì)生成一個(gè)按所選還原時(shí)間點(diǎn)恢復(fù)的恢復(fù)任務(wù),該任務(wù)信息將以列表的形式展示在恢復(fù)任務(wù)頁面。
查看恢復(fù)任務(wù)
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源ID。
單擊左側(cè)導(dǎo)航欄中的
。點(diǎn)擊恢復(fù)結(jié)果列中的實(shí)例ID,可跳轉(zhuǎn)至恢復(fù)的RDS實(shí)例基本信息頁面。
查看恢復(fù)演練
在用戶自動(dòng)備份頁簽,單擊目標(biāo)數(shù)據(jù)源ID。
單擊左側(cè)導(dǎo)航欄中的恢復(fù)演練。
恢復(fù)演練概覽指標(biāo)
查看恢復(fù)演練概覽,包含恢復(fù)任務(wù)成功率、恢復(fù)時(shí)長均值、數(shù)據(jù)備份演練覆蓋率、日志備份演練覆蓋率。
名稱
說明
恢復(fù)任務(wù)成功率
在恢復(fù)時(shí)間點(diǎn)在篩選時(shí)間范圍內(nèi)的恢復(fù)任務(wù)成功率。
恢復(fù)時(shí)長均值
在篩選時(shí)間范圍內(nèi)恢復(fù)成功任務(wù)的平均耗時(shí)。
數(shù)據(jù)備份演練覆蓋率
在篩選時(shí)間范圍內(nèi)數(shù)據(jù)備份在開源MySQL或RDS MySQL上執(zhí)行過恢復(fù)演練的覆蓋率。
日志備份演練覆蓋率
在篩選時(shí)間范圍內(nèi)的日志備份在開源MySQL或RDS MySQL上執(zhí)行過恢復(fù)演練的覆蓋率。
恢復(fù)演練時(shí)間軸
通過時(shí)間軸展示在篩選時(shí)間范圍內(nèi)的各個(gè)時(shí)間點(diǎn)的恢復(fù)演練詳情,在時(shí)間軸上點(diǎn)擊鼠標(biāo)左鍵可以查看當(dāng)前時(shí)間點(diǎn)的演練信息。
備份數(shù)據(jù)恢復(fù)演練詳情
單擊數(shù)據(jù)備份,查看數(shù)據(jù)備份恢復(fù)演練詳情列表。單擊演練結(jié)果列實(shí)例ID,進(jìn)入恢復(fù)的rds 實(shí)例的基本信息頁面。
單擊日志備份,可以查看日志備份恢復(fù)演練詳情列表。
用戶自動(dòng)備份數(shù)據(jù)上傳指南
準(zhǔn)備工作
提前配置數(shù)據(jù)源,獲得數(shù)據(jù)源ID,如何配置數(shù)據(jù)源請參見添加數(shù)據(jù)源。
創(chuàng)建一個(gè)RAM用戶并授予管理對應(yīng)產(chǎn)品的權(quán)限,準(zhǔn)備好AccessKeyId和AccessKeySecret信息。具體操作,請參見創(chuàng)建RAM用戶及為RAM用戶授權(quán)。
環(huán)境依賴
命令工具:Bash、Python3。
Python相關(guān):oss2、alibabacloud_openapi_util、alibabacloud_tea_openapi、alibabacloud_tea_util。
## 安裝阿里云OpenAPI SDK
pip3 install --upgrade pip
pip3 install -i https://mirrors.aliyun.com/pypi/simple/ oss2 alibabacloud_openapi_util alibabacloud_tea_openapi alibabacloud_tea_util
完整上傳腳本
備份數(shù)據(jù)上傳腳本分為兩部分:
腳本執(zhí)行處理入口,按需替換實(shí)際執(zhí)行的參數(shù)。
boot_backup.sh:負(fù)責(zé)模擬本地xtrabackup全量備份的流程。
#!/bin/bash
## 阿里云賬號(hào)AccessKey擁有所有API的訪問權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM用戶進(jìn)行API訪問或日常運(yùn)維
## 強(qiáng)烈建議不要把 AccessKey 和 AccessKeySecret 保存到代碼里,會(huì)存在密鑰泄漏風(fēng)險(xiǎn),您可以根據(jù)業(yè)務(wù)需要,保存到配置文件里
AK=<ALIBABA_CLOUD_ACCESS_KEY_ID>
SK=<ALIBABA_CLOUD_ACCESS_KEY_SECRET>
DBS_API=dbs-api.<您的數(shù)據(jù)源所在地域ID,例如cn-hangzhou>.aliyuncs.com
DATASOURCE_ID=<您的數(shù)據(jù)源ID>
## 獲取腳本當(dāng)前路徑
BASE_PATH=$(cd `dirname $0`; pwd)
TS=`date +%Y%m%d_%H%M%S`
XTRA_DATA_DIR=~/tool/xtrabackup_data/$TS
mkdir -p $XTRA_DATA_DIR
## 執(zhí)行xtrabackup備份命令,將錯(cuò)誤日志輸出的到xtrabackup.log文件
~/innobackupex --defaults-file=/etc/my.cnf --user='root' --password='root' --host='localhost' --port=3306 --socket='/var/lib/mysql/mysql.sock' --parallel=4 $XTRA_DATA_DIR/ 2>$XTRA_DATA_DIR/xtrabackup.log
## 獲取當(dāng)前xtrabackup備份的目錄
backup_dir=`ls $XTRA_DATA_DIR | grep -v xtrabackup.log | head -n1`
echo -e "\033[33mexecute innobackupex success, backup_dir: $backup_dir" && echo -n -e "\033[0m" && chmod 755 $XTRA_DATA_DIR/$backup_dir
## 打包成tar.gz文件
cd $XTRA_DATA_DIR/$backup_dir && tar -czvf ../$backup_dir.tar.gz . && file ../$backup_dir.tar.gz
echo -e "\033[33mpackage to $backup_dir.tar.gz" && echo -n -e "\033[0m" && sleep 2
## 調(diào)用Python腳本進(jìn)行上傳備份數(shù)據(jù),并注冊備份集元數(shù)據(jù)
python3 $BASE_PATH/upload_and_register_backup_set.py --access_key_id $AK --access_key_secret $SK --endpoint $DBS_API --datasource_id $DATASOURCE_ID --region_code=cn-hangzhou --data_type=FullBackup --file_path=$XTRA_DATA_DIR/$backup_dir.tar.gz --xtrabackup_log_path=$XTRA_DATA_DIR/xtrabackup.log
upload_and_register_backup_set.py:用于上傳全量和日志備份數(shù)據(jù),解析對應(yīng)的元數(shù)據(jù),并進(jìn)行備份集元數(shù)據(jù)的注冊。
# -*- coding: utf-8 -*- # This file is auto-generated, don't edit it. Thanks. import os import argparse import re import time import json from datetime import datetime import oss2 from alibabacloud_openapi_util.client import Client as OpenApiUtilClient from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_tea_openapi.client import Client as OpenApiClient from alibabacloud_tea_util import models as util_models import xtrabackup_info_parser import xtrabackup_log_parser def init_command_args(): parser = argparse.ArgumentParser(description="A sample command-line parser.") parser.add_argument("--access_key_id", help="Aliyun AccessKeyId.") parser.add_argument("--access_key_secret", help="Aliyun AccessKeySecret.") parser.add_argument("--endpoint", help="Aliyun API Endpoint.") parser.add_argument("--region_code", help="Aliyun DataSource RegionCode.") parser.add_argument("--datasource_id", help="Aliyun DataSourceId.") parser.add_argument("--data_type", help="BackupSet DataType: FullBackup | LogBackup.") parser.add_argument("--file_path", help="BackupSet File Path.") parser.add_argument("--xtrabackup_info_path", help="Xtrabackup Info Path.") parser.add_argument("--xtrabackup_log_path", help="Xtrabackup Log Path.") parser.add_argument("--begin_time", help="Binlog Begin Time.") parser.add_argument("--end_time", help="Binlog End Time.") args = parser.parse_args() if args.access_key_id: print(f"access_key_id: ************") if args.access_key_secret: print(f"access_key_secret: ************") if args.endpoint: print(f"endpoint: {args.endpoint}") if args.region_code: print(f"region_code: {args.region_code}") if args.datasource_id: print(f"datasource_id: {args.datasource_id}") if args.data_type: print(f"data_type: {args.data_type}") if args.file_path: print(f"file_path: {args.file_path}") if args.xtrabackup_info_path: print(f"xtrabackup_info_path: {args.xtrabackup_info_path}") if args.xtrabackup_log_path: print(f"xtrabackup_log_path: {args.xtrabackup_log_path}") if args.begin_time: print(f"begin_time: {args.begin_time}") if args.end_time: print(f"end_time: {args.end_time}") print('\n') return args def date_to_unix_timestamp(date_str): dt_obj = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") # 使用.time()方法得到時(shí)間元組,然后用time.mktime轉(zhuǎn)換為秒級(jí)時(shí)間戳 timestamp_seconds = time.mktime(dt_obj.timetuple()) return int(timestamp_seconds) * 1000 def create_oss_client(params): # 阿里云OSS認(rèn)證信息 access_key_id = params['AccessKeyId'] access_key_secret = params['AccessKeySecret'] security_token = params['SecurityToken'] bucket_name = params['BucketName'] endpoint = params['OssEndpoint'] # 初始化OSS客戶端 auth = oss2.StsAuth(access_key_id, access_key_secret, security_token) return oss2.Bucket(auth, endpoint, bucket_name) def upload_oss_file(oss_client, file_path, object_key): """ 分片上傳大文件到OSS :param oss_client: :param file_path: 本地文件路徑 :param object_key: OSS中的對象鍵,即文件名 """ # 設(shè)置分片大小,單位為字節(jié),默認(rèn)1MB part_size = 1024 * 1024 * 5 # 初始化分片上傳 upload_id = oss_client.init_multipart_upload(object_key).upload_id # 打開文件并讀取內(nèi)容 with open(file_path, 'rb') as file_obj: parts = [] while True: data = file_obj.read(part_size) if not data: break # 上傳分片 result = oss_client.upload_part(object_key, upload_id, len(parts) + 1, data) parts.append(oss2.models.PartInfo(len(parts) + 1, result.etag)) # 完成分片上傳 oss_client.complete_multipart_upload(object_key, upload_id, parts) class OssUploader: def __init__(self, access_key_id, access_key_secret, endpoint, region_code, datasource_id): self.access_key_id = access_key_id self.access_key_secret = access_key_secret self.endpoint = endpoint self.region_code = region_code self.datasource_id = datasource_id config = open_api_models.Config(access_key_id, access_key_secret) # Endpoint 請參考 https://api.aliyun.com/product/Rds config.endpoint = endpoint self.client = OpenApiClient(config) """ 注冊備份集元數(shù)據(jù) """ def configure_backup_set_info(self, req_param): params = open_api_models.Params( # 接口名稱, action='ConfigureBackupSetInfo', # 接口版本, version='2021-01-01', # 接口協(xié)議, protocol='HTTPS', # 接口 HTTP 方法, method='POST', auth_type='AK', style='RPC', # 接口 PATH, pathname='/', # 接口請求體內(nèi)容格式, req_body_type='json', # 接口響應(yīng)體內(nèi)容格式, body_type='json' ) # runtime options runtime = util_models.RuntimeOptions() request = open_api_models.OpenApiRequest( query=OpenApiUtilClient.query(req_param) ) # 返回值為 Map 類型,可從 Map 中獲得三類數(shù)據(jù):響應(yīng)體 body、響應(yīng)頭 headers、HTTP 返回的狀態(tài)碼 statusCode。 print(f"ConfigureBackupSetInfo request: {req_param}") data = self.client.call_api(params, request, runtime) print(f"ConfigureBackupSetInfo response: {data}") return data['body']['Data'] """ 獲取oss上傳信息 """ def describe_bak_datasource_storage_access_info(self, req_param): params = open_api_models.Params( # 接口名稱, action='DescribeBakDataSourceStorageAccessInfo', # 接口版本, version='2021-01-01', # 接口協(xié)議, protocol='HTTPS', # 接口 HTTP 方法, method='POST', auth_type='AK', style='RPC', # 接口 PATH, pathname='/', # 接口請求體內(nèi)容格式, req_body_type='json', # 接口響應(yīng)體內(nèi)容格式, body_type='json' ) # runtime options runtime = util_models.RuntimeOptions() request = open_api_models.OpenApiRequest( query=OpenApiUtilClient.query(req_param) ) # 返回值為 Map 類型,可從 Map 中獲得三類數(shù)據(jù):響應(yīng)體 body、響應(yīng)頭 headers、HTTP 返回的狀態(tài)碼 statusCode。 print(f"DescribeBakDataSourceStorageAccessInfo request: {req_param}") data = self.client.call_api(params, request, runtime) print(f"DescribeBakDataSourceStorageAccessInfo response: {data}") return data['body']['Data'] def _fetch_oss_access_info(self, params): info = self.describe_bak_datasource_storage_access_info({ 'RegionId': params['RegionId'], 'DataSourceId': params['DataSourceId'], 'RegionCode': params['RegionCode'], 'BackupType': params['BackupType'], 'BackupSetId': params['BackupSetId'] }) return info['OssAccessInfo'] def upload_and_register_backup_set(self, file_path, data_type, extra_meta): filename = os.path.basename(file_path) params = {'BackupMode': 'Automated', 'BackupMethod': 'Physical', 'RegionId': self.region_code, 'RegionCode': self.region_code, 'DataSourceId': self.datasource_id, 'BackupSetName': filename, 'ExtraMeta': extra_meta, 'BackupType': data_type, 'UploadStatus': 'WaitingUpload'} # 首次注冊備份集,返回備份集ID data = self.configure_backup_set_info(params) params['BackupSetId'] = data['BackupSetId'] print(f"------ configure_backup_set_info success: {file_path}, {data_type}, {params['BackupSetId']}, WaitingUpload\n") # 上傳數(shù)據(jù)到oss oss_info = self._fetch_oss_access_info(params) oss_client = create_oss_client(oss_info) upload_oss_file(oss_client, file_path, oss_info['ObjectKey']) print(f"------ upload_oss_file success: {file_path}, {data_type}, {params['BackupSetId']}\n") # 標(biāo)記備份集上傳完成 params['UploadStatus'] = 'UploadSuccess' self.configure_backup_set_info(params) print(f"------ configure_backup_set_info success: {file_path}, {data_type}, {params['BackupSetId']}, UploadSuccess\n") if __name__ == '__main__': args = init_command_args() uploader = OssUploader(args.access_key_id, args.access_key_secret, args.endpoint, args.region_code, args.datasource_id) # 全量和日志備份分別通過不同方式構(gòu)造extraMeta extra_meta = '{}' if args.data_type == 'FullBackup': obj = {} if args.xtrabackup_log_path is not None: obj = xtrabackup_log_parser.analyze_slave_status(logpath=args.xtrabackup_log_path) elif args.xtrabackup_info_path is not None: parser = xtrabackup_info_parser.ExtraMetaParser(file_path=args.xtrabackup_info_path) obj = parser.get_extra_meta() extra_meta = {'BINLOG_FILE': obj.get('BINLOG_FILE'), 'version': obj.get("SERVER_VERSION"), 'dataBegin': date_to_unix_timestamp(obj.get("START_TIME")), 'dataEnd': date_to_unix_timestamp(obj.get("END_TIME")), 'consistentTime': int(date_to_unix_timestamp(obj.get("END_TIME")) / 1000)} extra_meta = json.dumps(extra_meta) elif args.data_type == 'LogBackup': obj = {'dataBegin': date_to_unix_timestamp(args.begin_time), 'dataEnd': date_to_unix_timestamp(args.end_time)} extra_meta = json.dumps(obj) print(f"get extra meta json string: {extra_meta}") # 上傳數(shù)據(jù),并注冊備份集元信息 uploader.upload_and_register_backup_set(file_path=args.file_path, data_type=args.data_type, extra_meta=extra_meta)
xtrabackup_info_parser.py:使用備份文件xtrabackup_info進(jìn)行元數(shù)據(jù)解析。
# -*- coding: utf-8 -*- # This file is auto-generated, don't edit it. Thanks. import re import json class ExtraMetaParser: def __init__(self, file_path): self.file_path = file_path pass def _parse_xtrabackup_info(self): config_data = {} with open(self.file_path, 'r') as file: for line in file: line = line.strip() if line and not line.startswith('#'): key, value = line.split('=', 1) config_data[key.strip()] = value.strip() return config_data def get_extra_meta(self): config_data = self._parse_xtrabackup_info() print(f"xtrabackup_info: {config_data}") binlog_pos = config_data.get("binlog_pos") pattern = f"filename '(.*)', position (.*)" match = re.search(pattern, binlog_pos) return {'BINLOG_FILE': match.group(1), 'SERVER_VERSION': config_data.get("server_version"), 'START_TIME': config_data.get("start_time"), 'END_TIME': config_data.get("end_time")}
xtrabackup_log_parser.py:使用備份產(chǎn)生的日志文件xtrabackup.log進(jìn)行元數(shù)據(jù)解析。
#!/usr/bin/python # coding:utf8 import io import re import sys from datetime import datetime from six import binary_type, text_type def parse_date_part(date_part, time_part): """ 根據(jù)給定的日期部分和時(shí)間部分,解析并返回完整的日期時(shí)間字符串。 """ # 獲取當(dāng)前年份的前兩位 current_century = datetime.now().strftime("%Y")[:2] year_short = date_part[:2] # 組合完整年份 year_full = current_century + year_short date_full = year_full + date_part[2:] datetime_str = date_full + " " + time_part dt = datetime.strptime(datetime_str, "%Y%m%d %H:%M:%S") formatted_datetime = dt.strftime("%Y-%m-%d %H:%M:%S") return text_type(formatted_datetime) def analyze_slave_status(logpath=None): slave_status = {} start_time_pattern = ( r"(\d{6}) (\d{2}:\d{2}:\d{2}) .*Connecting to MySQL server host:" ) """ 240925 17:46:58 completed OK! 240925 02:22:58 innobackupex: completed OK! 240925 02:22:58 xtrabackup: completed OK! """ end_time_pattern = r"(\d{6}) (\d{2}:\d{2}:\d{2}) .*completed OK!" with io.open(logpath, "rb") as fp: lines = fp.read().splitlines() for i in reversed(range(len(lines))): line = lines[i] if isinstance(line, binary_type): line = line.decode("utf-8") m = re.search(start_time_pattern, line) if m: # 提取日期和時(shí)間部分 date_part = m.group(1) time_part = m.group(2) slave_status["START_TIME"] = parse_date_part(date_part, time_part) continue m = re.search(r"Using server version (\S*)", line) if m: slave_status["SERVER_VERSION"] = text_type(m.group(1)) continue m = re.search("MySQL binlog position:", line) if m: binlog_line = line m = re.search(r"filename '(\S*)'", binlog_line) if m: slave_status["BINLOG_FILE"] = text_type(m.group(1)) m = re.search(r"position (\d+)", binlog_line) m2 = re.search(r"position '(\d+)'", binlog_line) if m: try: slave_status["BINLOG_POS"] = int(m.group(1)) except ValueError: pass elif m2: try: slave_status["BINLOG_POS"] = int(m2.group(1)) except ValueError: pass continue m = re.search("consistent_time (\d+)", line) if m: try: slave_status["CONSISTENT_TIME"] = int(m.group(1)) except ValueError: pass continue m = re.search(end_time_pattern, line) if m: date_part = m.group(1) time_part = m.group(2) slave_status["END_TIME"] = parse_date_part(date_part, time_part) continue return slave_status if __name__ == "__main__": logpath = sys.argv[1] slave_status = analyze_slave_status(logpath) print(slave_status)
Bash腳本處理流程
配置阿里云AccessKeyId和AccessKeySecret、DBS OpenAPI Endpoint,以及對應(yīng)的數(shù)據(jù)源ID。
#!/bin/bash ## 阿里云賬號(hào)AccessKey擁有所有API的訪問權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM用戶進(jìn)行API訪問或日常運(yùn)維 ## 強(qiáng)烈建議不要把 AccessKey 和 AccessKeySecret 保存到代碼里,會(huì)存在密鑰泄漏風(fēng)險(xiǎn),您可以根據(jù)業(yè)務(wù)需要,保存到配置文件里 AK=<ALIBABA_CLOUD_ACCESS_KEY_ID> SK=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> DBS_API=dbs-api.<您的數(shù)據(jù)源所在地域ID,例如cn-hangzhou>.aliyuncs.com DATASOURCE_ID=<您的數(shù)據(jù)源ID>
執(zhí)行xtrabackup備份命令,將備份數(shù)據(jù)保存到指定的目錄,同時(shí)將錯(cuò)誤日志輸出到xtrabackup.log文件中。
## 獲取腳本當(dāng)前路徑 BASE_PATH=$(cd `dirname $0`; pwd) TS=`date +%Y%m%d_%H%M%S` XTRA_DATA_DIR=~/tool/xtrabackup_data/$TS mkdir -p $XTRA_DATA_DIR ## 執(zhí)行xtrabackup備份命令,將錯(cuò)誤日志輸出的到xtrabackup.log文件 ~/innobackupex --defaults-file=/etc/my.cnf --user='<您的數(shù)據(jù)庫賬號(hào),例如root>' --password='<您的數(shù)據(jù)庫密碼,例如root>' --host='<您的數(shù)據(jù)庫IP,例如localhost>' --port=<您的數(shù)據(jù)庫端口號(hào),例如3306> --socket='/var/lib/mysql/mysql.sock' --parallel=4 $XTRA_DATA_DIR/ 2>$XTRA_DATA_DIR/xtrabackup.log
后續(xù)將解析xtrabackup.log文件獲得全量備份集的元數(shù)據(jù)信息:備份開始時(shí)間、備份結(jié)束時(shí)間、一致性時(shí)間點(diǎn)、對應(yīng)Binlog名稱。
限制說明:
當(dāng)前執(zhí)行xtrabackup備份命令,暫不支持壓縮和加密參數(shù)(--compress --compress-threads和--encrypt --encrypt-key-file --encrypt-threads),使用xtrabackup備份命令時(shí)請先刪除對應(yīng)的參數(shù)。
將文件目錄形式的全量備份數(shù)據(jù)進(jìn)行g(shù)zip壓縮和打包成tar.gz格式的文件。
## 獲取當(dāng)前xtrabackup備份的目錄 backup_dir=`ls $XTRA_DATA_DIR | grep -v xtrabackup.log | head -n1` echo -e "\033[33mexecute innobackupex success, backup_dir: $backup_dir" && echo -n -e "\033[0m" && chmod 755 $XTRA_DATA_DIR/$backup_dir ## 打包成tar.gz文件 cd $XTRA_DATA_DIR/$backup_dir && tar -czvf ../$backup_dir.tar.gz . && file ../$backup_dir.tar.gz echo -e "\033[33mpackage to $backup_dir.tar.gz" && echo -n -e "\033[0m" && sleep 2
限制說明:當(dāng)前僅支持文件格式的備份數(shù)據(jù)上傳,支持如下格式:
tar:目錄文件進(jìn)行tar打包。
tar.gz:目錄文件進(jìn)行tar打包,然后進(jìn)行g(shù)zip壓縮。
注意事項(xiàng):
使用tar命令打包前,需要chmod 755命令修改文件目錄權(quán)限。
需進(jìn)入文件夾根目錄,進(jìn)行tar命令的打包和壓縮。
調(diào)用Python腳本upload_and_register_backup_set.py進(jìn)行備份數(shù)據(jù)的上傳,同時(shí)注冊備份集元數(shù)據(jù)。
## 調(diào)用Python腳本進(jìn)行上傳備份數(shù)據(jù),并注冊備份集元數(shù)據(jù) python3 $BASE_PATH/upload_and_register_backup_set.py --access_key_id $AK --access_key_secret $SK --endpoint $DBS_API --datasource_id $DATASOURCE_ID --region_code=cn-hangzhou --data_type=FullBackup --file_path=$XTRA_DATA_DIR/$backup_dir.tar.gz --xtrabackup_log_path=$XTRA_DATA_DIR/xtrabackup.log
參數(shù)
說明
--access_key_id
阿里云AccessKeyId。
--access_key_secret
阿里云AccessKeySecret。
--endpoint
DBS OpenAPI對應(yīng)的Endpoint。請參見服務(wù)接入點(diǎn)。
--datasource_id
DBS對應(yīng)的數(shù)據(jù)源ID。
--region_code
對應(yīng)地域信息。
--data_type
備份數(shù)據(jù)類型,全量備份集:FullBackup,日志備份:LogBackup。
--file_path
全量備份數(shù)據(jù)路徑。
--xtrabackup_log_path
全量備份xtrabackup命令產(chǎn)生的xtrabackup.log文件路徑。
Python腳本處理流程
main函數(shù)主入口:
根據(jù)data_type傳參為FullBackug或者LogBackup,分別構(gòu)造元數(shù)據(jù)注冊依賴的extra_meta信息:
說明BINLOG_FILE:對應(yīng)Binlog名稱。
dataBegin:備份開始時(shí)間,毫秒級(jí)別時(shí)間戳。
dataEnd:備份結(jié)束時(shí)間,毫秒級(jí)別時(shí)間戳。
consistentTime:一致性時(shí)間點(diǎn),秒級(jí)時(shí)間戳。
全量備份extra_meta格式:
{ 'BINLOG_FILE':'mysql-bin.001', 'version':'5.5', 'dataBegin':17274********, 'dataEnd':17274********, 'consistentTime':17274******** }
日志備份extra_meta格式:
{ 'dataBegin':17274********, 'dataEnd':17274******** }
調(diào)用OssUploader.upload_and_register_backup_set方法進(jìn)行備份數(shù)據(jù)上傳和元數(shù)據(jù)注冊。
if __name__ == '__main__': args = init_command_args() uploader = OssUploader(args.access_key_id, args.access_key_secret, args.endpoint, args.region_code, args.datasource_id) # 全量和日志備份分別通過不同方式構(gòu)造extraMeta extra_meta = '{}' if args.data_type == 'FullBackup': obj = {} if args.xtrabackup_log_path is not None: obj = xtrabackup_log_parser.analyze_slave_status(logpath=args.xtrabackup_log_path) elif args.xtrabackup_info_path is not None: parser = xtrabackup_info_parser.ExtraMetaParser(file_path=args.xtrabackup_info_path) obj = parser.get_extra_meta() extra_meta = {'BINLOG_FILE': obj.get('BINLOG_FILE'), 'version': obj.get("SERVER_VERSION"), 'dataBegin': date_to_unix_timestamp(obj.get("START_TIME")), 'dataEnd': date_to_unix_timestamp(obj.get("END_TIME")), 'consistentTime': int(date_to_unix_timestamp(obj.get("END_TIME")) / 1000)} extra_meta = json.dumps(extra_meta) elif args.data_type == 'LogBackup': obj = {'dataBegin': date_to_unix_timestamp(args.begin_time), 'dataEnd': date_to_unix_timestamp(args.end_time)} extra_meta = json.dumps(obj) print(f"get extra meta json string: {extra_meta}") # 上傳數(shù)據(jù),并注冊備份集元信息 uploader.upload_and_register_backup_set(file_path=args.file_path, data_type=args.data_type, extra_meta=extra_meta)
OssUploader.upload_and_register_backup_set方法備份數(shù)據(jù)上傳流程。
class OssUploader: def __init__(self, access_key_id, access_key_secret, endpoint, region_code, datasource_id): self.access_key_id = access_key_id self.access_key_secret = access_key_secret self.endpoint = endpoint self.region_code = region_code self.datasource_id = datasource_id config = open_api_models.Config(access_key_id, access_key_secret) # Endpoint 請參考 https://api.aliyun.com/product/Rds config.endpoint = endpoint self.client = OpenApiClient(config) """ 注冊備份集元數(shù)據(jù) """ def configure_backup_set_info(self, req_param): params = open_api_models.Params( # 接口名稱, action='ConfigureBackupSetInfo', # 接口版本, version='2021-01-01', # 接口協(xié)議, protocol='HTTPS', # 接口 HTTP 方法, method='POST', auth_type='AK', style='RPC', # 接口 PATH, pathname='/', # 接口請求體內(nèi)容格式, req_body_type='json', # 接口響應(yīng)體內(nèi)容格式, body_type='json' ) # runtime options runtime = util_models.RuntimeOptions() request = open_api_models.OpenApiRequest( query=OpenApiUtilClient.query(req_param) ) # 返回值為 Map 類型,可從 Map 中獲得三類數(shù)據(jù):響應(yīng)體 body、響應(yīng)頭 headers、HTTP 返回的狀態(tài)碼 statusCode。 print(f"ConfigureBackupSetInfo request: {req_param}") data = self.client.call_api(params, request, runtime) print(f"ConfigureBackupSetInfo response: {data}") return data['body']['Data'] """ 獲取oss上傳信息 """ def describe_bak_datasource_storage_access_info(self, req_param): params = open_api_models.Params( # 接口名稱, action='DescribeBakDataSourceStorageAccessInfo', # 接口版本, version='2021-01-01', # 接口協(xié)議, protocol='HTTPS', # 接口 HTTP 方法, method='POST', auth_type='AK', style='RPC', # 接口 PATH, pathname='/', # 接口請求體內(nèi)容格式, req_body_type='json', # 接口響應(yīng)體內(nèi)容格式, body_type='json' ) # runtime options runtime = util_models.RuntimeOptions() request = open_api_models.OpenApiRequest( query=OpenApiUtilClient.query(req_param) ) # 返回值為 Map 類型,可從 Map 中獲得三類數(shù)據(jù):響應(yīng)體 body、響應(yīng)頭 headers、HTTP 返回的狀態(tài)碼 statusCode。 print(f"DescribeBakDataSourceStorageAccessInfo request: {req_param}") data = self.client.call_api(params, request, runtime) print(f"DescribeBakDataSourceStorageAccessInfo response: {data}") return data['body']['Data'] def _fetch_oss_access_info(self, params): info = self.describe_bak_datasource_storage_access_info({ 'RegionId': params['RegionId'], 'DataSourceId': params['DataSourceId'], 'RegionCode': params['RegionCode'], 'BackupType': params['BackupType'], 'BackupSetId': params['BackupSetId'] }) return info['OssAccessInfo'] def upload_and_register_backup_set(self, file_path, data_type, extra_meta): filename = os.path.basename(file_path) params = {'BackupMode': 'Automated', 'BackupMethod': 'Physical', 'RegionId': self.region_code, 'RegionCode': self.region_code, 'DataSourceId': self.datasource_id, 'BackupSetName': filename, 'ExtraMeta': extra_meta, 'BackupType': data_type, 'UploadStatus': 'WaitingUpload'} # 首次注冊備份集,返回備份集ID data = self.configure_backup_set_info(params) params['BackupSetId'] = data['BackupSetId'] print(f"------ configure_backup_set_info success: {file_path}, {data_type}, {params['BackupSetId']}, WaitingUpload\n") # 上傳數(shù)據(jù)到oss oss_info = self._fetch_oss_access_info(params) oss_client = create_oss_client(oss_info) upload_oss_file(oss_client, file_path, oss_info['ObjectKey']) print(f"------ upload_oss_file success: {file_path}, {data_type}, {params['BackupSetId']}\n") # 標(biāo)記備份集上傳完成 params['UploadStatus'] = 'UploadSuccess' self.configure_backup_set_info(params) print(f"------ configure_backup_set_info success: {file_path}, {data_type}, {params['BackupSetId']}, UploadSuccess\n")