

# 将 RDS Custom for SQL Server 实例还原到某个时间点
<a name="custom-backup.pitr-sqs"></a>

您可以将数据库实例还原到某个特定时间点 (PITR)，以创建新数据库实例。要支持 PITR，您的数据库实例必须已启用备份保留期。

RDS Custom for SQL Server 数据库实例的最近可还原时间取决于多个因素，但通常为当前时间的 5 分钟内。要查看某个数据库实例的最近可还原时间，请使用 AWS CLI [describe-db-instances](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html) 命令，并查看该数据库实例的 `LatestRestorableTime` 字段中返回的值。要在 Amazon RDS 控制台中查看每个数据库实例的最近可还原时间，请选择**自动备份**。

您可以还原至备份保留期内的任何时间点。要查看每个数据库实例的最早可还原时间，请在 Amazon RDS 控制台中选择**自动备份**。

有关 PITR 的一般信息，请参阅[将 Amazon RDS 的数据库实例还原到指定时间](USER_PIT.md)。

**Topics**
+ [RDS Custom for SQL Server 的 PITR 注意事项](#custom-backup.pitr.sqlserver)
+ [每种实例类类型符合 PITR 条件的数据库数量](#custom-backup.pitr.sqlserver.eligiblecountperinstance)
+ [使数据库不符合 PITR 资格](#custom-backup.pitr.sqlserver.ineligible)
+ [Amazon S3 中的事务日志](#custom-backup.pitr.sqlserver.tlogs)
+ [使用 AWS 管理控制台、AWS CLI 或 RDS API 进行 PITR 还原。](#custom-backup.pitr-sqs-concli)

## RDS Custom for SQL Server 的 PITR 注意事项
<a name="custom-backup.pitr.sqlserver"></a>

在 RDS Custom for SQL Server 中，PITR 与 Amazon RDS 中的 PITR 有以下重要的区别：
+ PITR 仅还原数据库实例中的数据库。它不会还原操作系统或 C: 驱动器上的文件。
+ 对于 RDS Custom for SQL Server 数据库实例，数据库将自动备份，并且只有在以下条件下才有资格用于 PITR：
  + 数据库处于联机状态。
  + 它的还原模式设置为 `FULL`。
  + 它是可写的。
  + 它的物理文件放在 D: 驱动器上。
  + 它未在 `rds_pitr_blocked_databases` 表中列出。有关更多信息，请参阅 [使数据库不符合 PITR 资格](#custom-backup.pitr.sqlserver.ineligible)。
+ 这些符合 PITR 条件的数据库由其数据库 ID 的顺序决定。RDS Custom for SQL Server 允许每个数据库实例最多 5000 个数据库。不过，针对 RDS Custom for SQL Server 数据库实例，PITR 操作还原的数据库的最大数量取决于实例类的类型。有关更多信息，请参阅 [每种实例类类型符合 PITR 条件的数据库数量](#custom-backup.pitr.sqlserver.eligiblecountperinstance)。

  其它不属于 PITR 的数据库可以从数据库快照中还原，包括用于 PITR 的自动快照备份。
+ 添加新数据库、重命名数据库或还原符合 PITR 条件的数据库将启动数据库实例的快照。
+ 当数据库实例进行扩展计算操作时，符合 PITR 条件的最大数据库数量会发生变化，具体取决于目标实例类的类型。如果实例已纵向扩展，同时允许该实例上的更多数据库符合 PITR 条件，则会拍摄新的快照。
+ 还原的数据库的名称与源数据库实例中的名称相同。您不能指定不同的名称。
+ `AWSRDSCustomSQLServerIamRolePolicy` 需要访问其它 AWS 服务。有关更多信息，请参阅 [将访问策略添加到 AWSRDSCustomSQLServerInstanceRole](custom-setup-sqlserver.md#custom-setup-sqlserver.iam.add-policy)。
+ RDS Custom for SQL Server 不支持时区更改。如果更改操作系统或数据库实例时区，PITR（和其他自动化）将无法正常工作。

## 每种实例类类型符合 PITR 条件的数据库数量
<a name="custom-backup.pitr.sqlserver.eligiblecountperinstance"></a>

下表显示了根据实例类的类型符合 PITR 条件的最大数据库数量。


| 实例类类型 | 符合 PITR 条件的数据库的最大数量 | 
| --- | --- | 
| db.\$1.large | 100 | 
| db.\$1.xlarge 至 db.\$1.2xlarge | 150 | 
| db.\$1.4xlarge 至 db.\$1.8xlarge | 300 | 
| db.\$1.12xlarge 至 db.\$1.16xlarge | 600 | 
| db.\$1.24xlarge、db.\$132xlarge | 1000 | 

`*` *表示不同实例类类型。*

数据库实例上符合 PITR 条件的最大数据库数量取决于实例类类型。该数字的范围从最小的 100 到 RDS Custom for SQL Server 支持的最大实例类类型上的 1000 不等。此限制中并不包括 Server 系统数据库 `(master, model, msdb, tempdb)`。当数据库实例纵向扩展或缩减时，根据目标实例类类型，RDS Custom 将自动更新符合 PITR 条件的数据库数量。当数据库实例上符合 PITR 条件的最大数据库数量发生更改时，RDS Custom for SQL Server 将发送 `RDS-EVENT-0352`。有关更多信息，请参阅 [自定义引擎版本事件](USER_Events.Messages.md#USER_Events.Messages.CEV)。

**注意**  
对超过 100 个数据库的 PITR 支持仅在 2023 年 8 月 26 日之后创建的数据库实例上可用。对于 2023 年 8 月 26 日之前创建的实例，无论实例类如何，符合 PITR 条件的最大数据库数量均为 100。要在 2023 年 8 月 26 日之前创建的数据库实例上启用对超过 100 个数据库的 PITR 支持，您可以执行以下操作：  
将数据库引擎版本升级到 15.00.4322.2.v1 或更高版本

在 PITR 操作期间，RDS Custom 将在还原时在源数据库实例上还原属于 PITR 的所有数据库。目标数据库实例完成还原操作后，如果启用了备份保留，数据库实例将根据目标数据库实例上符合 PITR 条件的最大数据库数量开始备份。

例如，如果您的数据库实例在具有 200 个数据库的 `db.*.xlarge` 上运行：

1. RDS Custom for SQL Server 将选择前 150 个数据库进行 PITR 备份，按其数据库 ID 排序。

1. 您可以修改实例以纵向扩展到 db.\$1.4xlarge。

1. 扩展计算操作完成后，RDS Custom for SQL Server 将选择前 300 个数据库进行 PITR 备份，按其数据库 ID 排序。现在，满足 PITR 要求条件的 200 个数据库中的每一个都符合 PITR 条件。

1. 现在，您可以修改实例以缩减回到 db.\$1.xlarge。

1. 扩展计算操作完成后，RDS Custom for SQL Server 将再次选择前 150 个数据库进行 PITR 备份，按其数据库 ID 排序。

## 使数据库不符合 PITR 资格
<a name="custom-backup.pitr.sqlserver.ineligible"></a>

您可以选择从 PITR 中排除各个数据库。要做到这一点，请将其 `database_id` 值放入 `rds_pitr_blocked_databases` 表中。使用以下 SQL 脚本创建表。

**要创建 rds\$1pitr\$1blocked\$1databases 表**
+ 请运行以下 SQL 脚本。

  ```
  create table msdb..rds_pitr_blocked_databases
  (
  database_id INT NOT NULL,
  database_name SYSNAME NOT NULL,
  db_entry_updated_date datetime NOT NULL DEFAULT GETDATE(),
  db_entry_updated_by SYSNAME NOT NULL DEFAULT CURRENT_USER,
  PRIMARY KEY (database_id)
  );
  ```

有关符合条件和不符合条件的数据库的列表，请参阅 Amazon S3 存储桶 `do-not-delete-rds-custom-$ACCOUNT_ID-$REGION-unique_identifier` 中的 `RDSCustomForSQLServer/Instances/DB_instance_resource_ID/TransactionLogMetadata` 目录中的 `RI.End` 文件。有关 `RI.End` 文件的更多信息，请参阅[Amazon S3 中的事务日志](#custom-backup.pitr.sqlserver.tlogs)。

您还可以使用以下 SQL 脚本确定符合 PITR 条件的数据库列表。将 `@limit` 变量设置为符合该实例类的 PITR 条件的最大数据库数量。有关更多信息，请参阅 [每种实例类类型符合 PITR 条件的数据库数量](#custom-backup.pitr.sqlserver.eligiblecountperinstance)。

**在数据库实例类上确定符合 PITR 条件的数据库列表**
+ 请运行以下 SQL 脚本。

  ```
  DECLARE @Limit INT;
  SET @Limit = (insert-database-instance-limit-here);
  
  USE msdb;
  IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND  TABLE_NAME = 'rds_pitr_blocked_databases'))
      WITH TABLE0 AS (
          SELECT hdrs.database_id as DatabaseId, sdb.name as DatabaseName, 'ALWAYS_ON_NOT_WRITABLE_REPLICA' as Reason, NULL as DatabaseNameOnPitrTable
          FROM sys.dm_hadr_database_replica_states hdrs
          INNER JOIN sys.databases sdb ON sdb.database_id = hdrs.database_id
          WHERE (hdrs.is_local = 1 AND hdrs.is_primary_replica = 0) 
          OR (sys.fn_hadr_is_primary_replica (sdb.name) = 1 AND DATABASEPROPERTYEX (sdb.name, 'Updateability') = 'READ_ONLY')
      ),
      TABLE1 as (
              SELECT dbs.database_id as DatabaseId, sysdbs.name as DatabaseName, 'OPTOUT' as Reason,
              CASE WHEN dbs.database_name = sysdbs.name THEN NULL ELSE dbs.database_name END AS DatabaseNameOnPitrTable
              FROM msdb.dbo.rds_pitr_blocked_databases dbs
              INNER JOIN sys.databases sysdbs ON dbs.database_id = sysdbs.database_id
              WHERE sysdbs.database_id > 4
              ),
      TABLE2 as (
              SELECT
              db.name AS DatabaseName,
              db.create_date AS CreateDate,
              db.state_desc AS DatabaseState,
              db.database_id AS DatabaseId,
              rs.database_guid AS DatabaseGuid,
              rs.last_log_backup_lsn AS LastLogBackupLSN,
              rs.recovery_fork_guid RecoveryForkGuid,
              rs.first_recovery_fork_guid AS FirstRecoveryForkGuid,
              db.recovery_model_desc AS RecoveryModel,
              db.is_auto_close_on AS IsAutoClose,
              db.is_read_only as IsReadOnly,
              NEWID() as FileName,
              CASE WHEN(db.state_desc = 'ONLINE'
                      AND db.recovery_model_desc != 'SIMPLE' 
                      AND((db.is_auto_close_on = 0 and db.collation_name IS NOT NULL) OR db.is_auto_close_on = 1)) 
                      AND db.is_read_only != 1
                      AND db.user_access = 0
                      AND db.source_database_id IS NULL
                      AND db.is_in_standby != 1
                      THEN 1 ELSE 0 END AS IsPartOfSnapshot,
              CASE WHEN db.source_database_id IS NULL THEN 0 ELSE 1 END AS IsDatabaseSnapshot
              FROM sys.databases db
              INNER JOIN sys.database_recovery_status rs
              ON db.database_id = rs.database_id
              WHERE DB_NAME(db.database_id) NOT IN('tempdb') AND
              db.database_id NOT IN (SELECT DISTINCT DatabaseId FROM TABLE1) AND
              db.database_id NOT IN (SELECT DISTINCT DatabaseId FROM TABLE0)
          ),
          TABLE3 as(
              Select @Limit+count(DatabaseName) as TotalNumberOfDatabases from TABLE2 where TABLE2.IsPartOfSnapshot=1 and DatabaseName in ('master','model','msdb')
          )
          SELECT TOP(SELECT TotalNumberOfDatabases from TABLE3)  DatabaseName,CreateDate,DatabaseState,DatabaseId from TABLE2 where TABLE2.IsPartOfSnapshot=1
          ORDER BY TABLE2.DatabaseID ASC
  ELSE
      WITH TABLE0 AS (
          SELECT hdrs.database_id as DatabaseId, sdb.name as DatabaseName, 'ALWAYS_ON_NOT_WRITABLE_REPLICA' as Reason, NULL as DatabaseNameOnPitrTable
          FROM sys.dm_hadr_database_replica_states hdrs
          INNER JOIN sys.databases sdb ON sdb.database_id = hdrs.database_id
          WHERE (hdrs.is_local = 1 AND hdrs.is_primary_replica = 0) 
          OR (sys.fn_hadr_is_primary_replica (sdb.name) = 1 AND DATABASEPROPERTYEX (sdb.name, 'Updateability') = 'READ_ONLY')
      ),
      TABLE1 as (
              SELECT
              db.name AS DatabaseName,
              db.create_date AS CreateDate,
              db.state_desc AS DatabaseState,
              db.database_id AS DatabaseId,
              rs.database_guid AS DatabaseGuid,
              rs.last_log_backup_lsn AS LastLogBackupLSN,
              rs.recovery_fork_guid RecoveryForkGuid,
              rs.first_recovery_fork_guid AS FirstRecoveryForkGuid,
              db.recovery_model_desc AS RecoveryModel,
              db.is_auto_close_on AS IsAutoClose,
              db.is_read_only as IsReadOnly,
              NEWID() as FileName,
              CASE WHEN(db.state_desc = 'ONLINE'
                      AND db.recovery_model_desc != 'SIMPLE' 
                      AND((db.is_auto_close_on = 0 and db.collation_name IS NOT NULL) OR db.is_auto_close_on = 1)) 
                      AND db.is_read_only != 1
                      AND db.user_access = 0
                      AND db.source_database_id IS NULL
                      AND db.is_in_standby != 1
                      THEN 1 ELSE 0 END AS IsPartOfSnapshot,
              CASE WHEN db.source_database_id IS NULL THEN 0 ELSE 1 END AS IsDatabaseSnapshot
              FROM sys.databases db
              INNER JOIN sys.database_recovery_status rs
              ON db.database_id = rs.database_id
              WHERE DB_NAME(db.database_id) NOT IN('tempdb') AND
              db.database_id NOT IN (SELECT DISTINCT DatabaseId FROM TABLE0)
          ),
          TABLE2 as(
              SELECT @Limit+count(DatabaseName) as TotalNumberOfDatabases from TABLE1 where TABLE1.IsPartOfSnapshot=1 and DatabaseName in ('master','model','msdb')
          )
          select top(select TotalNumberOfDatabases from TABLE2)  DatabaseName,CreateDate,DatabaseState,DatabaseId from TABLE1 where TABLE1.IsPartOfSnapshot=1
          ORDER BY TABLE1.DatabaseID ASC
  ```

**注意**  
仅作为符号链接的数据库也被排除在符合 PITR 操作条件的数据库之外。上述查询不会根据此条件进行筛选。

## Amazon S3 中的事务日志
<a name="custom-backup.pitr.sqlserver.tlogs"></a>

备份保留期决定了 RDS Custom for SQL Server 数据库实例的事务日志是否会自动提取并上传到 Amazon S3。非零值表示创建了自动备份，且 RDS Custom 代理每 5 分钟将事务日志上传到 S3 一次。

S3 上的事务日志文件将使用您在创建数据库实例时提供的 AWS KMS key 静态加密。有关更多信息，请参阅《Amazon Simple Storage Service 用户指南》**中的[使用服务器端加密保护数据](https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html)。

每个数据库的事务日志将被上传到名为 `do-not-delete-rds-custom-$ACCOUNT_ID-$REGION-unique_identifier` 的 S3 存储桶。S3 存储桶中的 `RDSCustomForSQLServer/Instances/DB_instance_resource_ID` 目录包含两个子目录：
+ `TransactionLogs` – 包含每个数据库的事务日志及其各自的元数据。

  事务日志文件名称遵循模式 `yyyyMMddHHmm.database_id.timestamp`，例如：

  ```
  202110202230.11.1634769287
  ```

  带有后缀 `_metadata` 的相同文件名称包含有关事务日志的信息，例如日志序列号、数据库名称和 `RdsChunkCount`。`RdsChunkCount` 确定有多少个物理文件代表单个事务日志文件。您可能会看到带有后缀 `_0001`、`_0002` 等的文件，它们表示事务日志文件的物理数据块。如果要使用分块的事务日志文件，请确保在下载后合并这些数据块。

  考虑您拥有以下文件的情况：
  + `202110202230.11.1634769287`
  + ` 202110202230.11.1634769287_0001`
  + ` 202110202230.11.1634769287_0002 `
  + ` 202110202230.11.1634769287_metadata`

  `RdsChunkCount` 为 `3`。合并文件的顺序如下：`202110202230.11.1634769287`、` 202110202230.11.1634769287_0001`、`202110202230.11.1634769287_0002`。
+ `TransactionLogMetadata` – 包含有关事务日志提取的每次迭代的元数据信息。

  `RI.End` 文件包含提取了事务日志的所有数据库的信息，以及所有存在但没有提取其事务日志的数据库的信息。`RI.End` 文件名称遵循模式 `yyyyMMddHHmm.RI.End.timestamp`，例如：

  ```
  202110202230.RI.End.1634769281
  ```

## 使用 AWS 管理控制台、AWS CLI 或 RDS API 进行 PITR 还原。
<a name="custom-backup.pitr-sqs-concli"></a>

您可以使用 AWS 管理控制台、AWS CLI 或 RDS API 将 RDS Custom for SQL Server 数据库实例还原到某个时间点。

### 控制台
<a name="custom-backup-sqs.pitr2.CON"></a>

**要将 RDS Custom 数据库实例还原至指定时间**

1. 登录 AWS 管理控制台 并通过以下网址打开 Amazon RDS 控制台：[https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/)。

1. 在导航窗格中，选择 **Automated backups**（自动备份）。

1. 选择要还原的 RDS Custom 数据库实例。

1. 对于 **Actions (操作)**，选择 **Restore to point in time (还原到时间点)**。

   此时会显示**还原到时间点**窗口。

1. 选择**最近可还原时间**以还原到可能的最近时间，或选择**自定义**来选择时间。

   如果您选择 **Custom**（自定义），请输入要将实例集群还原到的日期和时间。

   时间以您的本地时区显示，表示为协调世界时 (UTC) 的偏移量。例如，UTC-5 是东部标准时间/中部夏令时。

1. 对于 **DB instance identifier**（数据库实例标识符），请输入目标还原后的 RDS Custom 数据库实例的名称。名称必须唯一。

1. 根据需要选择其他选项，例如数据库实例类。

1. 选择**还原到时间点**。

### AWS CLI
<a name="custom-backup-sqs.pitr2.CLI"></a>

通过使用 [ restore-db-instance-to-point-in-time](https://docs.aws.amazon.com/cli/latest/reference/rds/restore-db-instance-to-point-in-time.html) AWS CLI 命令创建新的 RDS Custom 数据库实例，将数据库实例还原到指定时间。

使用以下选项之一指定要从中恢复的备份：
+ `--source-db-instance-identifier mysourcedbinstance`
+ `--source-dbi-resource-id dbinstanceresourceID`
+ `--source-db-instance-automated-backups-arn backupARN`

`custom-iam-instance-profile` 选项是必需的。

截至指定的时间，以下示例会将 `my-custom-db-instance` 还原到名为 `my-restored-custom-db-instance` 的新数据库实例。

**Example**  
对于 Linux、macOS 或 Unix：  

```
1. aws rds restore-db-instance-to-point-in-time \
2.     --source-db-instance-identifier my-custom-db-instance\
3.     --target-db-instance-identifier my-restored-custom-db-instance \
4.     --custom-iam-instance-profile AWSRDSCustomInstanceProfileForRdsCustomInstance \
5.     --restore-time 2022-10-14T23:45:00.000Z
```
对于：Windows  

```
1. aws rds restore-db-instance-to-point-in-time ^
2.     --source-db-instance-identifier my-custom-db-instance ^
3.     --target-db-instance-identifier my-restored-custom-db-instance ^
4.     --custom-iam-instance-profile AWSRDSCustomInstanceProfileForRdsCustomInstance ^
5.     --restore-time 2022-10-14T23:45:00.000Z
```