如果您的 SQL Server 数据库文件分布在多个磁盘上,您可以同时备份所有磁盘,从而保持应用和数据的一致性。您可以使用 Compute Engine 即时快照一致性组实现这种精确的时间点备份 ,该组 可让您备份一组磁盘中的数据。
本教程介绍了如何使用 Compute Engine 快照以及 SQL Server 2022 及更高版本中提供的 Transact-SQL (T-SQL) 快照功能备份 SQL Server 数据库。 此解决方案支持 Windows 和 Linux 部署,并最大限度地减少对实时工作负载的性能影响。
工作原理
此工作流包含以下主要步骤,这些步骤由在计算实例上运行的脚本进行管理:
- 冻结数据库:脚本向 SQL Server 发送 T-SQL 命令,以 暂停目标数据库的所有写入操作。这可确保数据库文件处于一致的状态以进行备份。
- 创建即时快照:在数据库冻结期间,创建数据库的数据和日志文件所在的磁盘的即时快照组。这相当于使用硬件或服务级快照机制。 Google Cloud
- 记录和解冻:脚本向 SQL Server 发送另一个 T-SQL 命令,以记录
备份元数据。此命令会创建一个指向即时快照一致性
组的小型备份文件,并记录在
msdb备份历史记录中。完成后, SQL Server 会自动解冻数据库并恢复正常运行。
整个过程通常在不到一秒的时间内完成,从而最大限度地缩短数据库的写入冻结时间。在冻结期间,可以读取数据,但无法写入数据。您可以为数据库设置
SUSPEND_FOR_SNAPSHOT_BACKUP=OFF,以手动取消冻结状态。
目标
在本教程中,您将学习如何完成以下任务:- 创建一个包含两个数据磁盘的 SQL Server 实例。
- 创建一个新数据库,并将数据和日志文件放在单独的磁盘上。
- 为运行 SQL Server 的虚拟机的所有磁盘创建一个一致性组。
- 创建磁盘组的即时快照。
- 通过即时快照创建新磁盘。
费用
在本文档中,您将使用的以下收费组件: Google Cloud
您可使用 价格计算器 根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
在本教程中,您需要一个 Google Cloud 项目。您可以创建一个新项目,也可以选择现有项目:
-
在 Google Cloud 控制台的项目选择器页面上, 选择或创建一个 Google Cloud 项目。
选择或创建项目所需的角色
- 选择项目:选择项目不需要特定的 IAM 角色,您可以选择已被授予角色的任何项目。
-
创建项目:如需创建项目,您需要具有 Project Creator 角色
(
roles/resourcemanager.projectCreator),该角色包含resourcemanager.projects.create权限。了解如何授予 角色。
-
验证是否已为您的 Google Cloud 项目启用结算功能。
-
在 Google Cloud 控制台中,激活 Cloud Shell。
-
确保已安装并运行 Microsoft SQL Server 2022 或更高版本。
所需权限
除了标准读取权限之外,请确保 SQL Server 管理员向您授予目标数据库的 ALTER DATABASE 权限。
如需获得创建实例和创建快照所需的权限,请让管理员向您授予项目的以下 IAM 角色:
-
管理实例:
compute.instanceAdmin.v1 -
创建快照:
compute.storageAdmin
如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。
创建 Compute Engine SQL 实例
创建一个 SQL Server 实例。
在 Google Cloud 控制台中,点击激活 Cloud Shell
按钮,以打开 Cloud Shell。
创建一个 SQL Server 实例。粘贴以下命令:
REGION=REGIONZONE=$REGION-a VM_NAME=VM_NAMEgcloud compute instances create $VM_NAME \ --boot-disk-auto-delete \ --boot-disk-size 100 \ --boot-disk-type hyperdisk-balanced \ --image-family sql-std-2022-win-2025 \ --image-project windows-sql-cloud \ --machine-type c4-highmem-4 \ --zone $ZONE \ --network-interface subnet=default \ --tags sql-server-instant-snapshot \ --scopes=cloud-platform,service-control,service-management,monitoring-write,logging-write,storage-rw \ --create-disk=device-name=$VM_NAME-data-disk1,mode=rw,name=$VM_NAME-data-disk1,size=100,type=hyperdisk-balanced \ --create-disk=device-name=$VM_NAME-data-disk2,mode=rw,name=$VM_NAME-data-disk2,size=100,type=hyperdisk-balanced
替换以下内容:
- 区域:新实例将部署到的区域。
- VM_NAME:新 SQL Server 实例的名称。
创建磁盘的一致性组
创建一个一致性组。
gcloud compute resource-policies create disk-consistency-group $VM_NAME-snap-grp \ --region=$REGION将虚拟机的磁盘添加到一致性组。
gcloud compute disks add-resource-policies $VM_NAME \ --zone=$ZONE \ --resource-policies=$VM_NAME-snap-grp gcloud compute disks add-resource-policies $VM_NAME-data-disk1 \ --zone=$ZONE \ --resource-policies=$VM_NAME-snap-grp gcloud compute disks add-resource-policies $VM_NAME-data-disk2 \ --zone=$ZONE \ --resource-policies=$VM_NAME-snap-grp
创建一个新数据库
- 为虚拟机实例创建用户名和密码
- 使用远程桌面连接到虚拟机 然后使用上一步中创建的用户名和密码登录。
- 右键点击开始 按钮(或者按 Win+X ),然后点击终端 (管理员) 。
- 点击是以确认提升权限提示。
在打开的终端窗口中运行以下 PowerShell 脚本。此脚本会初始化数据磁盘,使用 64 KB 的块大小格式化磁盘,并分配盘符。
$availableDisks = Get-PhysicalDisk -CanPool $True $diskLetters = [char[]] (68..72) | Where-Object { !(Get-PSDrive $_ -ErrorAction SilentlyContinue) } $diskCount = 0 foreach ($disk in $availableDisks) { $diskLetter = $diskLetters[$diskCount] $diskLabel = "$diskLetter-DataDisk" Initialize-Disk -Number $disk.DeviceId -PartitionStyle MBR -PassThru New-Partition -DiskNumber $disk.DeviceId -UseMaximumSize ` -DriveLetter $diskLetter | Format-Volume -FileSystem NTFS ` -NewFileSystemLabel $diskLabel -AllocationUnitSize 65536 -Confirm:$false New-Item -ItemType Directory -Path "$($diskLetter):\MSSQL" $diskCount = $diskCount +1 }打开 SQL Server Management Studio (SSMS)。以管理员身份运行。
在“连接到服务器”对话框中,验证服务器名称是否设置为
localhost,然后选择连接 。在文件菜单中,依次选择文件 > 新建 > 查询 ,使用当前连接。
将以下代码复制到新打开的查询窗口。
USE Master; GO CREATE DATABASE SnapBackupDB ON PRIMARY ( NAME = 'SnapBackupDB_Data', FILENAME = 'D:\MSSQL\SnapBackupDB.mdf', SIZE = 500MB, MAXSIZE = UNLIMITED, FILEGROWTH = 64MB ) LOG ON ( NAME = 'SnapBackupDB_Log', FILENAME = 'E:\MSSQL\SnapBackupDB_log.ldf', SIZE = 250MB, MAXSIZE = 2GB, FILEGROWTH = 64MB );
部署快照脚本
此脚本会自动执行对运行 SQL Server 的虚拟机所挂接的磁盘进行应用一致性快照所需的完整过程。
- 使用相同的远程桌面会话,打开记事本。
- 复制以下脚本的内容,然后粘贴到打开的记事本窗口中。
将文件另存为
c:\scripts中的take-snapshot.ps1# Import Modules Import-Module -Name SQLPS # Set variables $formattedTimestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $backupLocation = 'D:\MSSQL\Backup' $dbName = 'SnapBackupDB' $bkmFile = Join-Path -Path $backupLocation -ChildPath ("${dbName}_${formattedTimestamp}.bkm") $sqlServer = 'localhost' # SQL Commands $suspendDbCmd = "ALTER DATABASE [$dbName] SET SUSPEND_FOR_SNAPSHOT_BACKUP=ON WITH NO_WAIT;" $backupMetadataCmd = "BACKUP DATABASE [$dbName] TO DISK='$bkmFile' WITH METADATA_ONLY, FORMAT;" $unsuspendDbCmd = "ALTER DATABASE [$dbName] SET SUSPEND_FOR_SNAPSHOT_BACKUP=OFF;" # --- Helper Function for SQL Execution --- function Invoke-MySqlCommand { param( [Parameter(Mandatory=$true)] [string]$CommandText, [Parameter(Mandatory=$true)] [System.Data.SqlClient.SqlConnection]$SqlConnection ) Write-Host "Executing SQL Command: $($CommandText)" try { $Command = New-Object System.Data.SqlClient.SqlCommand($CommandText, $SqlConnection) $Result = $Command.ExecuteNonQuery() return $true } catch { Write-Host "Error executing SQL Command: $($CommandText)`n$($_.Exception.Message)" -ForegroundColor Red return $false } } $sqlConn = New-Object System.Data.SqlClient.SqlConnection $sqlConn.ConnectionString = "server='$sqlServer';database='$dbName';Integrated Security=True;" $databaseSuspended = $false if (-not(Test-Path $backupLocation)) { New-Item -Path $backupLocation -ItemType directory -Force } try { $instanceName = (Invoke-RestMethod -Headers @{"Metadata-Flavor"="Google"} -Uri "http://metadata.google.internal/computeMetadata/v1/instance/name") $vmConfigString = (gcloud compute instances list --filter="name=('$instanceName')" --format=json --quiet) if ($LASTEXITCODE -ne 0) { Write-Host "Error querying gcloud for VM instances (exit code: $LASTEXITCODE). Exiting." -ForegroundColor Red exit 1 } $vmConfig = $vmConfigString | ConvertFrom-Json # Open SQL Server connection $sqlConn.Open() # Suspend Database Write-Host "Suspending database '$dbName' for snapshot..." if (-not (Invoke-MySqlCommand -CommandText $suspendDbCmd -SqlConnection $sqlConn)) { Write-Host "Failed to suspend database. Exiting." -ForegroundColor Red exit 1 } $databaseSuspended = $true # Take a disk snapshot try { $consistencyGroupListStr = (gcloud compute resource-policies list --filter='name:*-snap-grp' --format=json --quiet) if ($LASTEXITCODE -ne 0) { throw "gcloud resource-policies list failed (exit code: $LASTEXITCODE)." } $consistencyGroupList = $consistencyGroupListStr | ConvertFrom-Json if (@($consistencyGroupList).Count -eq 0) { throw "No consistency group found matching '*-snap-grp'." } if (@($consistencyGroupList).Count -gt 1) { Write-Warning "More than one consistency group found, using the first one." } $consistencyGroupSelfLink = $consistencyGroupList[0].selfLink $zoneName = Split-Path $vmConfig[0].zone -Leaf $snapshotName = "$($vmConfig.Name)-${formattedTimestamp}" $snapshotCmd = "gcloud compute instant-snapshot-groups create $snapshotName --source-consistency-group=$consistencyGroupSelfLink --zone=$zoneName --quiet" Invoke-Expression $snapshotCmd if ($LASTEXITCODE -ne 0) { throw "gcloud compute instant-snapshot-groups create failed (exit code: $LASTEXITCODE)." } Write-Host "Instant snapshot group created successfully." } catch { Write-Host "Error during snapshot creation: $($_.Exception.Message)" -ForegroundColor Red exit 1 # Exit after handling in finally } # Backup database metadata if (-not (Invoke-MySqlCommand -CommandText $backupMetadataCmd -SqlConnection $sqlConn)) { Write-Host "Failed to backup database metadata." -ForegroundColor Red exit 1 } # Unsuspend Database Write-Host "Unsuspending database '$dbName'..." if (-not (Invoke-MySqlCommand -CommandText $unsuspendDbCmd -SqlConnection $sqlConn)) { Write-Host "Failed to unsuspend database after successful snapshot and metadata backup. Manual intervention may be required." -ForegroundColor Red exit 1 } $databaseSuspended = $false # Successfully unsuspended } catch { Write-Host "An unhandled error occurred: $($_.Exception.Message)" -ForegroundColor Red exit 1 } finally { if ($databaseSuspended -and ($sqlConn.State -eq [System.Data.ConnectionState]::Open)) { if (-not (Invoke-MySqlCommand -CommandText $unsuspendDbCmd -SqlConnection $sqlConn)) { Write-Host "Failed to unsuspend database in finally block. Manual intervention may be required." -ForegroundColor Red } } # Ensure SQL connection is closed if ($sqlConn.State -eq [System.Data.ConnectionState]::Open) { $sqlConn.Close() } }右键点击开始 按钮(或者按 Win+X ),然后点击终端 (管理员) 。
点击是以确认提升权限提示。
运行以下命令以执行脚本。
C:\scripts\take-snapshot.ps1
验证是否已创建即时快照
在 Cloud Shell 中运行以下命令
验证脚本是否创建了快照。
gcloud compute instant-snapshots list --filter="name:$VM_NAME*"列出即时快照组。
gcloud compute instant-snapshot-groups list --zones $ZONE查看即时快照组的详细信息。
gcloud compute instant-snapshot-groups describe \INSTANT_SNAPSHOT_NAME--zone=$ZONE复制
selfLink值以在下一部分中使用。
将即时快照恢复到新磁盘
在 Google Cloud 控制台窗口中运行以下命令。
通过即时快照的一致性组创建磁盘。
gcloud compute disks bulk create --source-instant-snapshot-group \INSTANT_SNAPSHOT_NAME_URL\ --source-instant-snapshot-group-region $REGION --zone=$ZONE通过列出可用区中的磁盘来确认快照已恢复。
gcloud compute disks list --zones=$ZONE --filter="name:$VM_NAME-*"通过新创建的磁盘创建一个新虚拟机。
REGION=
REGIONZONE=$REGION-a VM_NAME=VM_NAMEgcloud compute instances create $VM_NAME \ --machine-type c4-highmem-4 \ --zone $ZONE \ --network-interface subnet=default \ --tags sql-server-instant-snapshot \ --disk=name=BOOT_DISK_NAME,boot=yes,auto-delete=no \ --disk=name=DATA_DISK_1_NAME,mode=rw,auto-delete=no \ --disk=name=DATA_DISK_2_NAME,mode=rw,auto-delete=no
替换以下内容:
- 区域:新实例将部署到的区域。
- VM_NAME:新 SQL 实例的名称。
- BOOT_DISK_NAME:在上一步中创建的启动磁盘的名称。
- DATA_DISK_1_NAME:在上一步中创建的第一个数据磁盘的名称。
- DATA_DISK_2_NAME:在上一步 中创建的第二个数据磁盘的名称。
您现在可以将磁盘挂接到新的或现有的虚拟机。
清理
为避免系统因本教程中使用的资源 向您的 Google Cloud 账号收取费用,请删除项目。
删除项目
- 在 Google Cloud 控制台中,前往 管理资源 页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击 关闭以删除项目。
后续步骤
- 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud 架构中心。