十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SQL Server数据库文件恢复:MDF/NDF/LDF附加与日志重建实战

SQL Server数据库文件恢复:MDF/NDF/LDF附加与日志重建实战 接手过几台数据库服务器的数据恢复我最常被问的一句话就是我把整个数据库目录拷出来了里面有.mdf、.ndf、.ldf文件怎么把数据弄回去这问题听起来简单实际操作里能绕晕一大批人。MDF、NDF、LDF这三个后缀名分别是SQL Server数据库的主数据文件、次要数据文件和事务日志文件。很多人以为只要把文件复制回Data目录就能自动识别结果一附加就报错要么提示版本不兼容要么提示日志文件不匹配严重的时候连库都挂不上只能在紧急模式里靠DBCC CHECKDB一点点救数据。这篇文章我打算把整个恢复链路完整拆开讲三种文件各自的作用、恢复之前必须排查的硬性条件、几种不同文件组合下的具体操作步骤、报错时的排错思路顺带把热搜上那个LDF文件过大怎么清空的问题一并解决掉。内容针对SQL Server但思路对大部分关系型数据库都通用。1. 这三种文件到底谁是谁先把日志机制说清楚1.1 MDF、NDF、LDF的分工SQL Server一个数据库在磁盘上最少有两个文件一个MDF主数据文件一个LDF事务日志文件。主数据文件不只存业务数据它还存着系统元数据比如表结构、索引定义、分配位图这些信息决定了SQL Server能不能识别这个数据库。NDF是次数据文件一般用于把数据分散到不同磁盘或者绕开单个文件大小上限虽然现在没人关心4GB限制但文件组拆分仍然常见。真正的核心是LDF。事务日志记录的是每一个数据页修改的完整轨迹从事务开始到提交再到写入数据文件每一步都被记录下来。SQL Server有一个机制叫WALWrite-Ahead Logging预写日志——任何数据页的变更必须先落到日志文件之后再异步地刷到数据文件。这意味着什么意味着MDF里的数据可能是不完整的最近几秒甚至几分钟的改动可能只存在于LDF里。所以当你拿到一组数据库文件时LDF不是可有可无的附属品它决定了数据的新鲜度和一致性。我经常打一个比方MDF是一本已经印刷出版的账本正文LDF是编辑手里的增补批注。如果你只有正文信息是缺失的只有把批注合并进去账本才是完整的。SQL Server在恢复数据库时会去比对MDF里的检查点位置和LDF里的日志记录把崩溃前未完成的改动重放redo或回滚undo这就是为什么附加有完整日志文件的数据库数据总是处于一个一致的状态。1.2 什么时候该走附加文件这条路什么时候别走不是所有数据库没了都需要从文件恢复先判断场景比急着操作更重要操作系统崩溃、磁盘分区损坏、SQL Server实例彻底重装、原来服务器已经开不了机——这种情况下数据库文件还完整地躺在磁盘上走附加Attach是标准方案。数据库文件被误删了一部分或者你手里只有某个备份文件.bak——那应该走还原Restore不要硬套附加逻辑。你把文件拷到另一台服务器但目标服务器上已经有一个同名数据库——需要先处理名称冲突或者用不同名称附加。文件来自数据库镜像、AlwaysOn可用性组或者复制拓扑——这类文件通常带着特殊状态直接附加往往失败需要先搞清楚它们在原拓扑里的角色。我见过最典型的误区是数据文件损坏了于是反复用FOR ATTACH去挂期望它自动修好。老实说如果MDF物理损坏比如页面校验失败、文件头损坏附加大概率不会成功正确做法是走紧急模式加DBCC CHECKDB修复或者直接用最近的备份还原。附加解决的是逻辑状态问题不是物理损坏问题这个边界要拎清楚。2. 动手之前必须确认的三件事2.1 版本对齐低版本文件不能挂到高版本方向别搞反我接手过一个小伙伴折腾了两天的案例他把SQL Server 2012的MDF文件拿到SQL Server 2019的实例上附加结果报错错误 948数据库 xxx 的版本为 706无法打开。此服务器支持 852 版及更低版本。不支持降级路径。这里有个很多人弄反的规则数据的版本方向是低版本可以往高版本走高版本不能往低版本降。SQL Server每个大版本内部都有一个数据库版本号比如2008是6552012是7062014是7822016是8522017是8692019是904。你可以把2012的库附加到2019上但要先确认2019能否处理706版本的物理格式和元数据结构官方支持路径是向前兼容的反过来说把2019的库附加到2012上必然失败因为低版本的引擎根本不认识新版本引入的文件格式变化。操作前的第一步永远是执行SELECT VERSION确认目标实例版本再用下面这条语句读文件头的版本号DBCC CHECKPRIMARYFILE (ND:\Data\YourDB.mdf, 0);返回结果里有Version列对照下面这张表就能快速判断方向数据库版本号SQL Server版本能否附加到更高版本5392000可以但路径很长6112005可以6552008 / 2008 R2可以7062012可以7822014可以8522016 / 2017可以8692017可以9042019仅限2019及以上9442022仅限2022及以上不要看到版本号就急着附加版本不匹配时强行用各种参数硬挂轻则报错重则让SQL Server实例的日志被错误刷屏干扰其他正常库的运行。2.2 文件权限与路径服务账户决定了生死文件版本对了下一个坑就是权限。SQL Server实例是以某个Windows服务账户运行的默认可能是NT Service\MSSQLSERVER也可能是你配置的域账户。这个账户必须对MDF、NDF、LDF所在目录有读取权限对附加后的目标Data目录有写入权限否则附加会报无法打开物理文件 D:\xxx.mdf。操作系统错误 55(拒绝访问。)。很多人第一反应是右键文件、勾选完全控制把权限全给Everyone这能解决一时的问题但安全隐患很大。更合理的做法是把数据库文件放在SQL Server服务账户有权限的专用目录里通过文件属性的安全页签给服务账户单独授予读取和执行权限。路径这块还有个更隐蔽的坑。假设原服务器上数据库文件在E:\Data\YourDB.mdf你把它拷到新服务器的D:\Data\YourDB.mdf逻辑文件名没变但物理路径变了。使用SQL Server Management StudioSSMS附加时它会让你手动选择文件位置这没问题但如果用T-SQL的CREATE DATABASE ... FOR ATTACH必须把所有文件路径用FILENAME参数明确写出来不能只写MDF的路径然后让SQL Server自己找LDF。SQL Server在附加时有一个行为尝试根据MDF文件头里记录的原始路径去定位其他文件。如果找不到它会去默认Data目录找再找不到就报无法找到日志文件之类错误。所以路径要一次性给全别偷懒。2.3 备份优先恢复前先给文件做副本这一步看起来多余但我坚持放在最前面说任何附加操作之前先把原始文件复制一份放旁边。原因很简单附加不是只读操作。FOR ATTACH_REBUILD_LOG会重建日志ALTER DATABASE SET EMERGENCY加DBCC CHECKDB会修改系统元数据、重建索引、甚至标记一些页面为可疑。如果操作中途发现方向搞错了原文件已经被改动过再想回到初始状态就难了。把文件复制到另一个目录等于给你自己留了一张后悔药。复制时注意先停掉SQL Server服务或者至少确保没有任何进程正在写入这些文件。数据文件不同于普通文本文件在写入过程中被复制复制出来的文件本身就是损坏的后续所有修复工作都会建立在垃圾之上。稳妥的做法是用Windows资源管理器复制前先在服务里停止SQL Server (MSSQLSERVER)服务复制完了再启动。3. 不同文件组合的恢复操作实录3.1 三件套齐全最常规的 FOR ATTACH最常见的恢复场景是MDF、NDF、LDF都在且都是从同一个实例上正常分离Detach或服务器掉电前留下的文件。额外说一句正常分离产生的文件是最干净的SQL Server会把日志和数据文件的检查点对齐附加几乎秒成功。操作很简单SSMS里右键数据库节点选附加添加MDF文件如果同一目录下能找到对应LDF和NDFSSMS会自动带上。命令行方式更可控尤其当你需要精确指定文件路径时CREATE DATABASE [YourDB] ON (FILENAME ND:\Data\YourDB.mdf), (FILENAME ND:\Data\YourDB.ndf), (FILENAME ND:\Data\YourDB_log.ldf) FOR ATTACH;执行成功后立刻验证一下数据库状态SELECT name, state_desc, recovery_model_desc FROM sys.databases WHERE name YourDB;state_desc应该是ONLINErecovery_model_desc保持与原来一致。如果原来是完整恢复模式这里还是完整恢复模式不要随手改成简单模式后面日志管理章节会细说。这里有个细节附加成功后我会顺手执行一次DBCC CHECKDB (YourDB) WITH NO_INFOMSGS确认没有页损坏或一致性错误。很多备份还原流程会自动做一致性校验但附加不会它只保证文件能被引擎识别和挂载不保证数据逻辑层面100%健康。3.2 缺了LDFFOR ATTACH_REBUILD_LOG 重建日志如果三件套里少了LDF或者LDF已经损坏别急着放弃。SQL Server提供了一个参数叫ATTACH_REBUILD_LOG它的语义是只根据MDF和NDF附加数据库并重新生成一个全新的事务日志文件。CREATE DATABASE [YourDB] ON (FILENAME ND:\Data\YourDB.mdf), (FILENAME ND:\Data\YourDB.ndf) FOR ATTACH_REBUILD_LOG;注意这条命令要求数据库之前是正常分离的或者在崩溃时没有未完成的事务。如果原库是非正常掉电MDF里可能存在未提交事务此时重建日志会把未提交事务全部丢弃这也就意味着最近一段时间的数据可能丢失。具体丢多少取决于检查点频率和崩溃前的事务活动量。还有一个前提条件所有数据文件MDF和NDF必须齐全。ATTACH_REBUILD_LOG不能修复缺失的NDF因为日志重建只涉及日志文件数据文件缺了任何一部分数据库都无法形成一个完整的逻辑一致体。另外要注意权限问题重建日志需要在默认Data目录有写权限如果MDF文件是只读的这条命令同样会失败。执行成功后SQL Server会生成一个新的_log.ldf文件大小默认是初始增长设置的值。此时数据库处于无日志链状态强烈建议立即做一次完整备份为后续日志备份建立基线——不然之后连日志备份都没法做。3.3 附加都失败紧急模式加 DBCC CHECKDB 兜底如果文件版本没问题路径也对但附加时还是报一致性错误比如常见的由于文件不可用数据库无法打开。恢复操作已将该数据库标记为 SUSPECT。这种情况下不要反复试附加切换到紧急模式。紧急模式允许SQL Server以受限方式打开数据库不执行正常的恢复流程让你有机会执行诊断和修复命令ALTER DATABASE [YourDB] SET EMERGENCY; ALTER DATABASE [YourDB] SET SINGLE_USER; DBCC CHECKDB ([YourDB], REPAIR_ALLOW_DATA_LOSS); ALTER DATABASE [YourDB] SET MULTI_USER;REPAIR_ALLOW_DATA_LOSS这个名字把话说得很直白它允许通过删除或截断某些受损数据来换取数据库整体可用性。虽然多数场景下它只是修复索引分配、页归属等结构问题并不一定真的丢行数据但存在丢数据的可能性。如果你有备份优先还原备份而不是用它硬修。我的经验是分两步走先执行DBCC CHECKDB ([YourDB]) WITH NO_INFOMSGS不加修复参数拿到完整的错误清单根据错误级别判断严重程度如果只是分配错误比如CHECKDB报告页面分配错误用REPAIR_REBUILD往往更安全——它只重建索引和结构不删除数据页。只有到了非结构性损坏不可的时候才用REPAIR_ALLOW_DATA_LOSS。修复完再跑一次DBCC CHECKDB直到输出不再报错为止。紧急模式操作完之后把数据库改回多用户模式然后做一次完整备份。这个备份非常重要——修复后的数据库结构已经不同于原始状态必须立刻固化一个健康基线。4. 恢复失败时怎么排错从报错信息反推原因4.1 高频报错与真实含义对照恢复过程的大部分时间其实花在排错上。我把这几年遇到的高频报错整理成一张对照表每一条后面都附上我的排查路径报错信息节选真实原因优先排查方向错误 948版本为 xxx此服务器支持 yyy 版及更低版本数据库文件版本高于实例版本确认实例版本找更高版本实例或者还原备份无法打开物理文件操作系统错误 5拒绝访问服务账户无文件权限检查文件NTFS权限、目录权限无法打开物理文件操作系统错误 32正在被另一进程使用文件被占用关闭SSMS连接、停止SQL Server服务排查杀毒软件日志文件与数据文件不一致LDF损坏或日志链断裂尝试FOR ATTACH_REBUILD_LOG重建日志由于文件不可用数据库无法打开附加或启动过程中恢复失败进入紧急模式跑DBCC CHECKDB文件已加密或不是有效的数据库页头文件损坏或根本是另一软件生成的文件检查文件大小和文件头用DBCC CHECKPRIMARYFILE验证数据库已存在实例里已有同名数据库先分离旧库或使用不同数据库名称附加有个规律附加报错时SQL Server错误日志默认位于C:\Program Files\Microsoft SQL Server\MSSQLxx.MSSQLSERVER\MSSQL\Log\ERRORLOG会给出比SSMS弹窗更详细的底层原因。操作系统错误码尤其有价值——5是权限、32是占用、3是路径不存在这些小数字能帮你快速跳过无效排查。4.2 一个容易忽视的坑数据库名称与逻辑文件名的错位最后说一个我踩过的坑它不算高频率但一旦碰上非常容易懵。假设原库逻辑文件名是MyDB_Data物理文件名是MyDB.mdf。附加时报错无法将文件 D:\Data\MyDB_log.ldf 用于... 文件正在使用中。原因可能不是文件被占用而是旧实例上还有一个同名数据库的日志文件正被使用。如果你之前在这个实例上已经附加过同一组文件或者存在一个旧数据库占据了同名的逻辑文件名新附加就会冲突。解决办法是先处理旧数据库分离或删除再执行附加。另一个错位场景是逻辑文件名写在MDF文件头里和物理文件名不一致。比如MDF文件头记录的逻辑文件名是MyDB_Data但物理文件叫CopyOfMyDB.mdf附加时FILENAME参数写的是物理路径逻辑名则以文件头为准。这本身不报错但后续DBCC SHRINKFILE、ALTER DATABASE ... MODIFY FILE等操作时命令里用的name必须是逻辑名不是物理名。写错的话会直接在语法层面报错找不到指定名称的文件。所以附加成功后我习惯立刻跑一遍SELECT file_id, type_desc, name, physical_name FROM sys.database_files;把逻辑名、物理路径全部确认一遍再继续后面的操作。这一步省下的时间绝对比你想象的多。5. 热搜问题连带解决LDF文件过大到底怎么清5.1 日志疯狂膨胀的根源恢复完数据库之后你大概率会遇到另一个热搜问题LDF文件几十GB甚至几百GB比MDF还大磁盘快满了。这个问题的根源几乎都是同一个事务日志从没被截断过。事务日志记录的是所有数据修改操作的轨迹。当日志备份完成后SQL Server会截断truncate日志中已备份的部分那些空间就可以被后续事务重用。但如果数据库设置为简单恢复模式每个检查点后日志自动截断如果设置为完整恢复模式就必须依赖日志备份来触发截断。很多人把数据库维持在完整恢复模式却从不做日志备份日志只会无限膨胀一直涨到磁盘满为止。另外一个常见原因是大事务。一条UPDATE语句影响了几千万行它在单次事务里产生的日志量会非常惊人。即使平时有定期日志备份这种超大事务也会把日志空间瞬间撑大而且在其提交前日志备份也无法截断它。5.2 安全的收缩流程备份-收缩-验证网上各种直接删掉LDF的说法千万别跟着做。LDF文件手动删除后数据库启动时会因为找不到日志文件而无法打开你只能被迫重建日志而重建会丢失未备份的日志链信息严重时会让数据库无法恢复到最后状态。正确的收缩流程应该是第一步查看当前日志使用情况DBCC SQLPERF(LOGSPACE);关注Log Size (MB)和Log Space Used (%)。如果使用率只有个位数说明日志里大部分是空闲空间可以安全收缩。第二步做一次事务日志备份让日志截断发生BACKUP LOG [YourDB] TO DISK ND:\Backup\YourDB_log.trn;如果你确实不需要日志备份来支持时间点恢复也可以暂时切换到简单恢复模式让SQL Server自动截断ALTER DATABASE [YourDB] SET RECOVERY SIMPLE;第三步执行收缩DBCC SHRINKFILE (NYourDB_log, 100);第二个参数100是目标大小单位是MB。这条命令把日志收缩到100MB。如果日志中还有活动部分比如一个超长事务未结束收缩会停在活动部分之前此时别硬等去排查那个长事务。第四步改回完整恢复模式并再次备份ALTER DATABASE [YourDB] SET RECOVERY FULL; BACKUP DATABASE [YourDB] TO DISK ND:\Backup\YourDB_full.bak;改回完整恢复模式后必须立即做一次完整或差异备份否则日志备份链断裂数据库又会回到日志不断增长的危险状态。5.3 清理之后的长期维护建议收缩LDF只是治标日志再次膨胀只是时间问题。真正要解决的是日志管理机制完整恢复模式下建立定期日志备份作业比如每30分钟或每小时一次把日志备份到独立磁盘或异地存储。有了日志备份LDF空间会不断被截断重用不会再无限增长。如果业务真的不需要时间点恢复干脆把恢复模式设为简单配合每周完整备份即可日志由检查点自动截断。监控脚本别省。写一个简单的Job定时执行DBCC SQLPERF(LOGSPACE)当Log Space Used (%)超过70%时发告警邮件大部分日志膨胀问题在变大前就能被发现。收缩不要频繁做。频繁DBCC SHRINKFILE会导致日志文件反复扩展和收缩每一次扩展都伴随文件系统分配的开销数据库性能会受影响。收缩只是为了解决已经膨胀的历史遗留问题不是日常操作。我个人处理过的数据库运维事故里日志文件占满磁盘导致整个实例停止响应的案例占比相当高。很多都是因为备份策略里只做了完整备份没做日志备份这种基础配置问题。如果你刚用文中的方法恢复好一个库请在收尾时把日志策略一起梳理一遍不然一个月后大概率又要再救一次。另外恢复完数据库之后还有一件事别忘验证应用连接。用你的业务账号测试一下增删改查特别是那些依赖视图、存储过程、作业的功能。文件恢复只保证数据库引擎层面健康应用层逻辑是否完整只能靠实测确认。
返回列表