RDS SQL Server提供了實例級別的數據庫遷移上云方案,支持將自建SQL Server的多庫或所有庫的全量數據遷移至阿里云RDS SQL Server。您只需先備份自建SQL Server的所有數據庫,并將完整備份文件上傳到OSS Bucket(存儲空間)的同一文件夾中,然后執行遷移上云腳本即可。
如果您的上云遷移級別為數據庫,即每次只需完成一個數據庫遷移上云,SQL Server提供了以下三種基于OSS的數據庫上云方案:
前提條件
源端數據庫需為自建SQL Server。
目標端RDS SQL Server實例需滿足如下條件:
實例版本為2008 R2、2012及以上。如需創建實例,請參見創建RDS SQL Server實例。
實例版本為RDS SQL Server 2008 R2時,需要在目標RDS實例中創建與待遷移數據庫名稱相同的數據庫,并保持數據庫為空。如何創建數據庫,請參見創建數據庫和賬號。
說明2012及以上版本的實例,無需執行本步驟。
RDS SQL Server實例擁有足夠的存儲空間。如果空間不足,請提前升級實例空間。具體操作,請參見變更配置。
已開通OSS服務。具體操作,請參見開通OSS服務。
如果通過RAM用戶登錄,則必須滿足以下條件:
RAM賬號具備AliyunOSSFullAccess權限和AliyunRDSFullAccess權限。如何為RAM用戶授權,請參見通過RAM對OSS進行權限管理和通過RAM對RDS進行權限管理。
阿里云賬號(主賬號)已授權RDS官方服務賬號可以訪問您OSS的權限。
前往RDS實例詳情頁備份恢復頁面,單擊OSS備份數據恢復上云按鈕。
在數據導入向導頁面單擊兩次下一步,進入3. 數據導入步驟。
若該頁面左下角顯示您已授權RDS官方服務賬號可以訪問您OSS的權限,則表示已授權。否則表示還未授權,單擊該頁面的授權地址同意授權即可。
所在阿里云賬號(主賬號)需手動創建權限策略,然后將權限添加到RAM賬號中。如何創建權限策略,請參見通過腳本編輯模式創建自定義權限策略。
{ "Version": "1", "Statement": [ { "Action": [ "ram:GetRole" ], "Resource": "acs:ram:*:*:role/AliyunRDSImportRole", "Effect": "Allow" } ] }
限制條件
本方案僅支持全量遷移上云,暫不支持增量遷移上云。
費用說明
本方案中僅會產生OSS的相關費用,詳情如下圖所示。
場景 | 費用說明 |
將本地數據備份文件上傳至OSS | 不產生費用。 |
備份文件存儲在OSS | 會產生OSS的存儲費用,計費詳情請參見OSS定價。 |
將備份文件從OSS遷移至RDS |
|
準備工作
安裝Python2.7.18版本,詳情請參見Python官網。
確認Python安裝成功并查看版本。
執行
c:\Python27\python.exe -V
查看Python版本,若輸出內容為Python 2.7.18
表示您已安裝成功。如果提示“不是內部或外部命令”,請在Path環境變量中增加Python的安裝路徑和pip命令的目錄。
執行
python -V
查看Python版本,若輸出內容為Python 2.7.18
表示您已安裝成功。選擇下述方法之一,安裝SDK依賴包:
方式一:使用pip安裝
pip install aliyun-python-sdk-rds pip install oss2
方式二:使用源碼安裝
# 克隆OpenAPI git clone https://github.com/aliyun/aliyun-openapi-python-sdk.git # 安裝阿里云SDK核心庫 cd aliyun-python-sdk-core python setup.py install # 安裝阿里云RDS SDK cd aliyun-python-sdk-rds python setup.py install # 克隆阿里云OSS SDK git clone https://github.com/aliyun/aliyun-oss-python-sdk.git cd aliyun-oss-python-sdk # 安裝阿里云OSS2 SDK python setup.py install
1. 備份自建SQL Server所有數據庫
為保障數據一致性,在執行全量備份期間,請勿寫入新的數據,請提前安排以免影響業務運行。
如果您不使用備份腳本來執行備份,備份文件必須按照
數據庫名稱_備份類型_備份時間.bak
的格式來命名,例如Testdb_FULL_20180518153544.bak
,否則會導致備份報錯。
下載備份腳本。
雙擊備份腳本,使用Microsoft SQL Server Management Studio(SSMS)客戶端打開。SSMS的連接方法,請參見官方文檔。
根據業務需求,修改如下參數。
SELECT /** * Databases list needed to backup, delimiter is : or , * empty('') or null: means all databases excluding system database * example: '[testdb]: TestDR, Test, readonly' **/ @backup_databases_list = N'[dtstestdata],[testdb]' @backup_type = N'FULL', -- Backup Type? FULL: FULL backup; DIFF: Differential backup; LOG: Log backup @backup_folder = N'C:\BACKUP' -- Backup folder to store backup files. @is_run = 1 -- Check or run? 1, run directly; 0, just check
配置項
說明
@backup_databases_list
需要備份的數據庫,多個數據庫以分號(;)或者半角逗號(,)分隔。
@backup_type
備份類型,取值如下:
FULL:全量備份。
DIFF:差異備份。
LOG:日志備份。
重要本方案中,取值需為FULL。
@backup_folder
備份文件所在的本地目錄。如不存在,會自動創建。
@is_run
是否執行備份,取值:
1:執行備份。
0:僅執行檢查,不執行備份。
運行備份腳本,數據庫將備份至指定的目錄中。
2. 上傳備份文件到OSS
創建存儲空間Bucket。
登錄OSS管理控制臺。
單擊Bucket列表,然后單擊創建Bucket。
配置如下關鍵參數,其他參數可以保持默認。
重要創建的存儲空間僅用于本次數據上云,且上云后不再使用,因此只需配置關鍵參數即可,為避免數據泄露及產生相關費用,上云完成后請及時刪除。
創建Bucket時請勿開啟數據加密。更多詳情,請參見數據加密。
參數
說明
取值示例
Bucket 名稱
存儲空間名稱,全局唯一,設置后無法修改。
命名規則:
只能包括小寫字母、數字和短劃線(-)。
必須以小寫字母或者數字開頭和結尾。
長度必須在3~63字符之間。
migratetest
地域
Bucket所屬的地域,如果您通過ECS內網上傳數據至Bucket中,且通過內網將數據恢復至RDS中,則需要三者地域保持一致。
華東1(杭州)
存儲類型
選擇標準存儲。本文上云操作不支持其他存儲類型的Bucket。
標準存儲
上傳備份文件到OSS。
說明當RDS實例和OSS的Bucket在同一地域時,二者可以通過內網互通,且數據上傳速度更快,并且不會產生外網流量費用。因此,在上傳備份文件時,建議將文件上傳至與目標RDS實例在同一地域的Bucket上。
本地數據庫備份完成后,需要將備份文件上傳到您的OSS Bucket中,您可以采用如下方法之一:
下載ossbrowser。
以Windows x64操作系統為例,解壓下載的
oss-browser-win32-x64.zip
壓縮包,雙擊運行oss-browser.exe
應用程序。使用AK登錄方式,配置參數AccessKeyId和AccessKeySecret,其他參數保持默認,然后單擊登入。
說明AccessKey用于身份驗證,確保數據安全,請妥善保管,如何創建及獲取,請參見創建AccessKey。
單擊目標Bucket,進入存儲空間。
單擊,選擇需要上傳的備份文件,然后單擊打開,即可將本地文件上傳至OSS中。
說明如果備份文件小于5 GB,建議您直接通過OSS控制臺上傳備份文件。
登錄OSS管理控制臺。
單擊Bucket列表,然后單擊目標Bucket名稱。
在文件列表中,單擊上傳文件。
您可以將備份文件拖拽至待上傳文件區域,也可以單擊掃描文件,選擇需要上傳的備份文件。
單擊頁面下方的上傳文件,即可將本地備份文件上傳至OSS中。
說明如果備份文件大于5 GB,建議您調用OSS API采用分片上傳的方式將備份文件上傳到OSS Bucket中。
本示例以Java項目為例,從環境變量中獲取訪問憑證代碼。運行本代碼示例之前,請先配置環境變量。如何配置訪問憑證,請參見配置訪問憑證。更多示例,請參見分片上傳。
import com.aliyun.oss.ClientException; import com.aliyun.oss.OSS; import com.aliyun.oss.common.auth.*; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSException; import com.aliyun.oss.internal.Mimetypes; import com.aliyun.oss.model.*; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) throws Exception { // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。 String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; // 從環境變量中獲取訪問憑證。運行本代碼示例之前,請確保已設置環境變量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); // 填寫Bucket名稱,例如examplebucket。 String bucketName = "examplebucket"; // 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。 String objectName = "exampledir/exampleobject.txt"; // 待上傳本地文件路徑。 String filePath = "D:\\localpath\\examplefile.txt"; // 填寫Bucket所在地域。以華東1(杭州)為例,Region填寫為cn-hangzhou。 String region = "cn-hangzhou"; // 創建OSSClient實例。 ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration(); clientBuilderConfiguration.setSignatureVersion(SignVersion.V4); OSS ossClient = OSSClientBuilder.create() .endpoint(endpoint) .credentialsProvider(credentialsProvider) .clientConfiguration(clientBuilderConfiguration) .region(region) .build(); try { // 創建InitiateMultipartUploadRequest對象。 InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName); // 如果需要在初始化分片時設置請求頭,請參考以下示例代碼。 ObjectMetadata metadata = new ObjectMetadata(); // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString()); // 指定該Object的網頁緩存行為。 // metadata.setCacheControl("no-cache"); // 指定該Object被下載時的名稱。 // metadata.setContentDisposition("attachment;filename=oss_MultipartUpload.txt"); // 指定該Object的內容編碼格式。 // metadata.setContentEncoding(OSSConstants.DEFAULT_CHARSET_NAME); // 指定初始化分片上傳時是否覆蓋同名Object。此處設置為true,表示禁止覆蓋同名Object。 // metadata.setHeader("x-oss-forbid-overwrite", "true"); // 指定上傳該Object的每個part時使用的服務器端加密方式。 // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION); // 指定Object的加密算法。如果未指定此選項,表明Object使用AES256加密算法。 // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_DATA_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION); // 指定KMS托管的用戶主密鑰。 // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, "9468da86-3509-4f8d-a61e-6eab1eac****"); // 指定Object的存儲類型。 // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard); // 指定Object的對象標簽,可同時設置多個標簽。 // metadata.setHeader(OSSHeaders.OSS_TAGGING, "a:1"); // request.setObjectMetadata(metadata); // 根據文件自動設置ContentType。如果不設置,ContentType默認值為application/oct-srream。 if (metadata.getContentType() == null) { metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName)); } // 初始化分片。 InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request); // 返回uploadId。 String uploadId = upresult.getUploadId(); // 根據uploadId執行取消分片上傳事件或者列舉已上傳分片的操作。 // 如果您需要根據uploadId執行取消分片上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后獲取uploadId。 // 如果您需要根據uploadId執行列舉已上傳分片的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前獲取uploadId。 // System.out.println(uploadId); // partETags是PartETag的集合。PartETag由分片的ETag和分片號組成。 List<PartETag> partETags = new ArrayList<PartETag>(); // 每個分片的大小,用于計算文件有多少個分片。單位為字節。 final long partSize = 1 * 1024 * 1024L; //1 MB。 // 根據上傳的數據大小計算分片數。以本地文件為例,說明如何通過File.length()獲取上傳數據的大小。 final File sampleFile = new File(filePath); long fileLength = sampleFile.length(); int partCount = (int) (fileLength / partSize); if (fileLength % partSize != 0) { partCount++; } // 遍歷分片上傳。 for (int i = 0; i < partCount; i++) { long startPos = i * partSize; long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize; UploadPartRequest uploadPartRequest = new UploadPartRequest(); uploadPartRequest.setBucketName(bucketName); uploadPartRequest.setKey(objectName); uploadPartRequest.setUploadId(uploadId); // 設置上傳的分片流。 // 以本地文件為例說明如何創建FIleInputstream,并通過InputStream.skip()方法跳過指定數據。 InputStream instream = new FileInputStream(sampleFile); instream.skip(startPos); uploadPartRequest.setInputStream(instream); // 設置分片大小。除了最后一個分片沒有大小限制,其他的分片最小為100 KB。 uploadPartRequest.setPartSize(curPartSize); // 設置分片號。每一個上傳的分片都有一個分片號,取值范圍是1~10000,如果超出此范圍,OSS將返回InvalidArgument錯誤碼。 uploadPartRequest.setPartNumber( i + 1); // 每個分片不需要按順序上傳,甚至可以在不同客戶端上傳,OSS會按照分片號排序組成完整的文件。 UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest); // 每次上傳分片之后,OSS的返回結果包含PartETag。PartETag將被保存在partETags中。 partETags.add(uploadPartResult.getPartETag()); } // 創建CompleteMultipartUploadRequest對象。 // 在執行完成分片上傳操作時,需要提供所有有效的partETags。OSS收到提交的partETags后,會逐一驗證每個分片的有效性。當所有的數據分片驗證通過后,OSS將把這些分片組合成一個完整的文件。 CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags); // 如果需要在完成分片上傳的同時設置文件訪問權限,請參考以下示例代碼。 // completeMultipartUploadRequest.setObjectACL(CannedAccessControlList.Private); // 指定是否列舉當前UploadId已上傳的所有Part。僅在Java SDK為3.14.0及以上版本時,支持通過服務端List分片數據來合并完整文件時,將CompleteMultipartUploadRequest中的partETags設置為null。 // Map<String, String> headers = new HashMap<String, String>(); // 如果指定了x-oss-complete-all:yes,則OSS會列舉當前UploadId已上傳的所有Part,然后按照PartNumber的序號排序并執行CompleteMultipartUpload操作。 // 如果指定了x-oss-complete-all:yes,則不允許繼續指定body,否則報錯。 // headers.put("x-oss-complete-all","yes"); // completeMultipartUploadRequest.setHeaders(headers); // 完成分片上傳。 CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest); System.out.println(completeMultipartUploadResult.getETag()); } catch (OSSException oe) { System.out.println("Caught an OSSException, which means your request made it to OSS, " + "but was rejected with an error response for some reason."); System.out.println("Error Message:" + oe.getErrorMessage()); System.out.println("Error Code:" + oe.getErrorCode()); System.out.println("Request ID:" + oe.getRequestId()); System.out.println("Host ID:" + oe.getHostId()); } catch (ClientException ce) { System.out.println("Caught an ClientException, which means the client encountered " + "a serious internal problem while trying to communicate with OSS, " + "such as not being able to access the network."); System.out.println("Error Message:" + ce.getMessage()); } finally { if (ossClient != null) { ossClient.shutdown(); } } } }
3. 執行遷移上云腳本將數據庫遷移至RDS
解壓后執行如下命令,了解該腳本需要傳入的參數信息。
python ~/Downloads/RDSSQLCreateMigrateTasksBatchly.py -h
結果如下:
RDSSQLCreateMigrateTasksBatchly.py -k <access_key_id> -s <access_key_secret> -i <rds_instance_id> -e <oss_endpoint> -b <oss_bucket> -d <directory>
參數說明如下:
參數
說明
access_key_id
目標RDS實例所屬的阿里云賬號的AccessKey ID。
access_key_secret
目標RDS實例所屬的阿里云賬號的AccessKey Secret。
rds_instance_id
目標RDS實例ID。
oss_endpoint
備份文件所屬的存儲空間的Endpoint地址。獲取方法,請參見存儲空間概覽。
oss_bucket
備份文件所屬的存儲空間名稱。
directory
備份文件在OSS存儲空間中的目錄。如果是根目錄,請傳入
/
。執行遷移上云腳本,完成遷移任務。
本示例以將OSS存儲空間(名稱為
testdatabucket
)的Migrationdata
目錄中所有滿足條件的備份文件,全量遷移到RDS SQL Server實例(實例ID為rm-2zesz5774ud8s****)為例。python ~/Downloads/RDSSQLCreateMigrateTasksBatchly.py -k LTAIQ**** -s BMkIUhroub******** -i rm-2zesz5774ud8s**** -e oss-cn-beijing.aliyuncs.com -b testdatabucket -d Migrationdata
在RDS控制臺查看遷移任務的執行進度。
訪問RDS實例列表,在上方選擇地域,然后單擊目標實例ID。
根據RDS實例的版本,選擇下述操作步驟:
RDS SQL Server 2008 R2
單擊左側導航欄的數據上云,您可以查看到所有提交的遷移上云任務。
說明可以單擊右上角的刷新來查看遷移上云任務的最新狀態。
RDS SQL Server 2012及以上版本
單擊左側導航欄的備份恢復,然后單擊備份數據上云記錄頁簽。
說明默認會展示最近一周的記錄,您可以選擇時間范圍來查看特定時間段內的上云恢復記錄。
視頻演示
常見錯誤
錯誤提示 | 原因 | 解決方法 |
| 調用OpenAPI時使用的AccessKey ID不正確。 | 傳入正確的AccessKey ID和AccessKey Secret,查看方法請參見訪問密鑰常見問題。 |
| 調用OpenAPI時使用的AccessKey Secret不正確。 | |
| 本方案僅支持RDS SQL Server,不支持其他引擎。 | 將RDS SQL Server作為遷移的目標實例。 |
| RDS實例ID不存在。 | 檢查傳入的RDS實例ID是否正確。 |
| Endpoint錯誤,導致連接失敗。 | 檢查傳入的Endpoint是否正確,獲取方法請參見存儲空間概覽。 |
| OSS Bucket(存儲空間)不存在。 | 檢查傳入的OSS Bucket是否正確。 |
| OSS Bucket中對應的文件夾不存在或文件夾中沒有滿足條件的數據庫備份文件。 | 檢查OSS Bucket中文件夾是否存在,同時檢查該文件夾中是否存在滿足條件的數據庫備份文件。 |
| 備份文件的名稱不符合規范。 | 如果您不使用備份腳本來執行備份,備份文件必須按照 |
| 子賬號權限不足。 | 需要為子賬號授予OSS和RDS的讀寫權限(即AliyunOSSFullAccess和AliyunRDSFullAccess權限)。關于授權操作方法,請參見為RAM用戶授權。 |
| 調用OpenAPI返回了錯誤信息。 | 根據錯誤碼和錯誤信息來分析具體原因,詳情請參見OpenAPI錯誤碼。 |
OpenAPI錯誤碼
HTTP Status Code | Error | Description | 說明 |
403 | InvalidDBName | The specified database name is not allowed. | 非法的數據庫名字,不允許使用系統數據庫名。 |
403 | IncorrectDBInstanceState | Current DB instance state does not support this operation. | RDS實例狀態不正確。例如,實例狀態為創建中。 |
400 | IncorrectDBInstanceType | Current DB instance type does not support this operation. | 不支持的引擎,該功能僅支持RDS SQL Server。 |
400 | IncorrectDBInstanceLockMode | Current DB instance lock mode does not support this operation. | 數據庫鎖定狀態不正確。 |
400 | InvalidDBName.NotFound | Specified one or more DB name does not exist or DB status does not support. | 數據庫不存在。
|
400 | IncorrectDBType | Current DB type does not support this operation. | 數據庫類型不支持該操作。 |
400 | IncorrectDBState | Current DB state does not support this operation. | 數據庫狀態不正確,例如,數據庫在創建中或者正在上云任務中。 |
400 | UploadLimitExceeded | UploadTimesQuotaExceeded: Exceeding the daily upload times of this DB. | 上云次數超過限制,每個實例每個庫每天不超過20次上云操作。 |
400 | ConcurrentTaskExceeded | Concurrent task exceeding the allowed amount. | 上云次數超過限制,每個實例每天上云總次數不超過500次。 |
400 | IncorrectFileExtension | The file extension does not support. | 備份文件的后綴名錯誤。 |
400 | InvalidOssUrl | Specified oss url is not valid. | 提供的OSS下載鏈接地址不可用。 |
400 | BakFileSizeExceeded | Exceeding the allowed bak file size. | 數據庫備份文件超過限制,最大不超過3TB。 |
400 | FileSizeExceeded | Exceeding the allowed file size of DB instance. | 還原備份文件后將超過當前實例的存儲空間。 |
相關API
API | 描述 |
將OSS上的備份文件還原到RDS SQL Server實例,創建數據上云任務。 | |
打開RDS SQL Server備份數據上云任務的數據庫。 | |
查詢RDS SQL Server實例備份數據上云任務列表。 | |
查詢RDS SQL Server備份數據上云任務的文件詳情。 |