SQL Server 2008 迁移至 2022 容器化部署 技术总结
一、 迁移背景与架构
最近在一套测试验证环境的时候,因为原有测试环境使用的还是阿里云2008版sqlserver数据库,查阅了一下资料,打算迁移到2022版Liunx环境的Docker容器中,这中间遇到了一些问题,打算记录并总结下,以避免以后犯重复的问题。
二、 完整迁移实战命令与全流程清单
步骤 1:宿主机在大空间目录规划与赋权
因为sqlserver数据库有40多G,原有的系统的默认挂接盘空间不足,需要在新加的挂接盘 /data 分区下建立挂载目录,并赋予最高读写权限,防止容器内外出现用户权限冲突:
# 创建新的数据目录与备份存放目录
mkdir -p /data/mssql/main/data
mkdir -p /data/mssql/backup
# 赋予最高级别读写与执行权限(非常关键,防止 Error 31 及权限被拒)
chmod -R 777 /data/mssql
步骤 2:安全迁移备份文件
使用 rsync 工具将从阿里云下载并解压出来的 8.6G 完好备份文件移动到新的备份目录下:
rsync -avh /data/backup/db/rich/hygame/hygame.bak /data/mssql/backup/hygame.bak
# 确保搬迁后的文件权限依旧对容器开放
chmod 777 /data/mssql/backup/hygame.bak
步骤 3:重构 docker-compose.yml 部署配置
修改数据库服务的配置,将数据路径和备份路径以持久化卷(Volumes)的方式直接挂载。这样可以免去 docker cp 传输过程中的二次空间损坏。
version: '3.8'
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
.
.
.
.
.
volumes:
# 1. 将数据目录外挂到 100G 的大硬盘分区
- /data/mssql/main/data:/var/opt/mssql
# 2. 将备份文件所在的宿主机目录直接外挂为容器内 /tmp_backup
- /data/mssql/backup:/tmp_backup
修改完成后,在控制台执行重启以应用全新的大硬盘容器环境:
# 停止旧容器并应用新配置
docker-compose down
docker-compose up -d
步骤 4:查看文件内部逻辑流名称(恢复前置校验)
在正式还原前,需要先探测该 .bak 备份文件内部的逻辑数据流名称(LogicalName)。在宿主机终端输入:
docker-compose exec sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P '你的安全密码' -C \
-Q "RESTORE FILELISTONLY FROM DISK = N'/tmp_backup/hygame.bak'"
返回结果校验: 执行后会输出一个表格,注意看前两行的 LogicalName 列。
成功获取到 2 行数据,**LogicalName**分别为 data1(数据文件)和 log(日志文件)。
LogicalName PhysicalName Type FileGroupName Size MaxSize FileId CreateLSN DropLSN UniqueId ReadOnlyLSN ReadWriteLSN BackupSizeInBytes SourceBlockSize FileGroupId LogGroupGUID DifferentialBaseLSN DifferentialBaseGUID IsReadOnly IsPresent TDEThumbprint SnapshotUrl
-------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
data1 d:\ms3002\Data\hygame\data1.mdf D PRIMARY 43350360064 536870912000 1 0 0 B8F6CED4-0C2E-4C53-A262-9698BD8861E6 0 0 40550793216 512 1 NULL 2747000012926900071 98123469-DE49-411C-823C-BA1A1F1F3ADE 0 1 NULL NULL
log d:\ms3002\log\hygame\log.ldf L NULL 536870912 536870912000 2 0 0 22E8D69C-5239-4B41-BBA6-5F497DC964BC 0 0 0 512 0 NULL 0 00000000-0000-0000-0000-000000000000 0 1 NULL NULL
步骤 5:强行清场并执行最终物理还原
由于 后端应用可能会在容器启动时自动抢占连接,还原前必须加入“独占单用户”指令强行断开外部干扰。
在宿主机终端执行以下组合拳命令:
docker-compose exec sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P '你的安全密码' -C \
-Q "ALTER DATABASE [hygame] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE [hygame]
FROM DISK = N'/tmp_backup/hygame.bak'
WITH MOVE N'上面查询到的LogicalName' TO N'/var/opt/mssql/data/hygame.mdf',
MOVE N'上面查询到的Log 类型的LogicalName' TO N'/var/opt/mssql/data/hygame_log.ldf',
REPLACE,
STATS = 10"
- 命令关键参数释义:
SET SINGLE_USER WITH ROLLBACK IMMEDIATE:立刻强行切断 外部工具的所有连接,防止还原被锁。MOVE N'data1' TO ...:将备份里原本属于 Windows 环境的物理路径,强行重定向到 Linux 容器内部的路径下。REPLACE:强行覆盖现有的同名空数据库。STATS = 10:每当还原进度推进 10%,主动在终端打印一行进度日志(避免长时间无响应误以为卡死)。
步骤 6:异地同步后台进度监控命令(选跑)
在还原大文件过程中,可在另一终端窗口下直接透视 SQL Server 的真实核心进程百分比:
docker-compose exec sqlserver /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P '你的安全密码' -C \
-Q "SELECT r.command, r.percent_complete, r.estimated_completion_time/1000 AS seconds_left
FROM sys.dm_exec_requests r
WHERE r.command = 'RESTORE DATABASE';"
- 当
percent_complete达到100.0且seconds_left清零时,证明底层物理迁移大功告成。
三、 核心关键节点与踩坑排查
在整个迁移过程中,曾遇到了一下一些问题,需要谨记:
节点 1:文件元数据 incomplete / 还原提示文件损坏
- 现象:执行还原命令时,SQL Server 报
file ID 1 incorrectly formed and can not be read错误。执行RESTORE HEADERONLY查看文件头,第一列显示为*** INCOMPLETE ***。 - 原因:Linux 容器内部权限封锁。备份文件上传后默认属于
root:root,当容器内的mssql进程尝试深度扫描大文件数据流时,被 Linux 内核强行截断,导致数据库误判定文件损坏或残缺。 - 解决:利用
docker exec -u root强行突破,将文件属主改为mssql:mssql并赋权777。
节点 2:容器内部文件大小对不上(4.1G 瓶颈)
- 现象:发现宿主机上解压出的原始备份文件是 8.6G,但使用
docker cp强行拷贝进容器内部后,文件莫名其妙缩水变成了 4.1G。 - 原因:Docker 容器默认的存储驱动(Overlay2)或文件层受到了宿主机单文件写入上限(或分区空间不足)的封锁,导致中途断流,文件只拷进去了一半。
- 解决:彻底放弃
docker cp往容器内塞文件的传统做法,采用 “物理目录挂载(Volume)” 的工业级标准,让容器直接读取宿主机硬盘,彻底打破空间和传输大小限制。
节点 3:Modify File Operating System Error 31 报错
- 现象:文件完整度解决后,执行还原时出现
MODIFY FILE encountered operating system error 31 (A device attached to the system is not functioning.) while attempting to expand the physical file。 - 原因:磁盘空间严重不足。因为 8.6G 的备份文件在还原时,需要向硬盘申请至少 10G~20G 的连续空间来创建
.mdf数据文件。原本的挂载分区空间耗尽,导致 Linux 拒绝了 SQL Server 的扩容申请。 - 解决:通过
rsync快速安全地将整个数据库和备份目录无缝搬迁到拥有 100G 宽裕空间的/data独立硬盘分区中,并重构docker-compose.yml挂载路径。
四、 迁移后续最佳实践(必做优化)
数据虽然成功搬迁过来了,但由于源数据库是 2008 版本的,我们需要通过以下两条 SQL 命令完成数据库的“现代化重构升级”:
请在 DataGrip 或 sqlcmd(注意:sqlcmd 命令行后必须换行输入 GO 才会触发执行)中运行:
-- 1. 确保数据库回到正常的多用户多人连接模式
ALTER DATABASE [hygame] SET MULTI_USER;
GO
-- 2. 将数据库架构由 2008(兼容级别 100)强行提升至 2022 最新标准(兼容级别 160)
-- 这能彻底激活 2022 引擎的高级查询优化器,让原本十几年前的老数据在现代硬件和 Linux 容器下获得倍增的性能。
USE [master];
GO
ALTER DATABASE [hygame] SET COMPATIBILITY_LEVEL = 160;
GO
五、 技术总结心得
- 容器大文件切忌 使用
docker cp:直接通过挂载卷映射是应对几吉字节以上数据库备份的唯一可靠方案,能避免各种文件截断和二次损坏。 - 提前关注硬件指标:数据库物理恢复需要接近备份文件 2~3 倍的硬盘冗余空间用于初始化扩容,在迁移前必须通过
df -h确认底层硬盘的可用容量。 - 分清客户端差异:交互式命令行工具
sqlcmd在键入查询后必须紧跟一个独立的GO命令作为结束符,否则无法将请求投递至服务层,在日常测试时应当优先以此排查“有库无数据”的假象。