干这行久了,什么稀奇古怪的报错没见过。但要说最让人血压飙升的,数据库还原时弹那句“数据库正在使用,无法获得独占访问权”,绝对排得上号。你想想,半夜两点,线上系统崩了,你抱着备份文件准备力挽狂澜,结果SQL Server Management Studio给你甩这么一句冷冰冰的提示,想砸电脑的心都有。

这报错说白了,就是有别的进程正攥着你的数据库不放。SQL Server有个脾气,还原操作需要拿到数据库的独占锁,就像你要拆一栋楼,得先确保楼里没人。可偏偏有会话赖在里面不走,可能是某个半夜跑批的任务卡住了,也可能是有个忘了关的查询窗口,甚至是你自己刚才测试连接时留下的残留会话。它不告诉你具体是谁,只丢给你一句“正在使用”,剩下的全靠你自己猜。
第一个办法,也是最直接的,用踢会话的SQL脚本。别去图形界面里点来点去了,直接开个新查询窗口,执行一段代码把占用者干掉。写法不复杂,先查sys.dmexecsessions和sys.dmexecconnections这两个系统视图,找到目标数据库的会话ID,然后KILL掉。关键一步是,查的时候要过滤掉自己当前的会话,不然连自己都踢了,那就闹笑话了。这个办法的好处是精准,坏处是如果你不熟悉系统视图的字段,容易写错条件,误伤其他正常连接的数据库。
第二个办法,把数据库设为单用户模式。这个适合你不想去揪出具体是谁占用的情况,直接粗暴地告诉SQL Server:“这数据库现在只许我一个人碰”。操作也简单,右键数据库 → 属性 → 选项 → 限制访问,改成Single。或者用ALTER DATABASE语句搞定。设成单用户后,再执行还原,基本就畅通无阻了。但注意,单用户模式有个坑,如果你自己连着其他工具,比如某个报表软件还挂着连接,那你也进不去。得先确保所有外部连接都断了,不然你会遇到“无法以独占方式访问”的另一个变种报错。
第三个办法,重启SQL Server服务,这招最省心,也最暴力。直接去服务管理器里,找到SQL Server实例对应的服务,右键重启。服务一重启,所有会话全部清零,数据库自然就“没人用了”。但代价你也清楚,整个实例上的所有数据库都会跟着断一下连接,如果你的服务器上还挂着别的业务库,那就是一场小型的生产事故。所以这招一般留到凌晨没人的时候用,或者你确认这台服务器上只有这一个库在跑。
有人可能会问,这三个办法哪个最好?说实话,得看场景。如果是白天上班时间,业务正跑着,你贸然重启服务,领导能把你骂到怀疑人生。这时候老老实实用脚本踢会话,或者设单用户模式,影响面最小。如果是深夜维护窗口,业务早就停了,那重启服务反而是最干净利落的,省得你一个个排查隐藏的占用者。
还有个小细节容易被忽视,就是那些“孤儿连接”。有时候你明明关掉了所有查询窗口,但后台还挂着一些异常连接,这些通常是因为客户端程序没正常释放连接池导致的。用第一个办法查会话时,能看到很多status为sleeping的会话,它们不干活,但占着茅坑不拉屎。如果你定位到这些是某个应用服务器留下的,最好去那台机器上把对应的应用服务也重启一下,不然你踢完它又会自动连上来。
另外,还原之前养成个好习惯,把目标数据库的“关闭现有连接”选项勾上。在SSMS的还原界面里,选项页有一个“关闭与目标数据库的现有连接”复选框,勾上它,SQL Server会在还原前自动踢掉所有占用会话。这相当于把第一个办法自动化了,省得你手动写脚本。但注意,这个选项只对当前还原操作有效,如果连接是刚建立的,它可能踢不掉,得跑两次。
说回实际操作,我见过不少新手一看到“正在使用”就慌了,直接去重启服务器操作系统。那真是下下策,不仅影响范围大,而且重启后SQL Server可能还要跑崩溃恢复,时间反而更长。记住,数据库报错大多有解,关键是别慌,按顺序排查。先看是不是自己开着查询窗口没关,再看有没有别的应用连着,才考虑重启服务。
这三个办法,本质上都是在解决“独占访问权”的问题。SQL Server的还原机制决定了它必须独占数据库,这既是保护机制,也是麻烦来源。理解了这一点,你就明白为什么报错信息那么不友好,因为它根本不知道是谁占着,只知道占着。所以你得自己当侦探,去查、去踢、去重启。
送你一句话,数据库还原就像搬家,你得先确保旧房子里没人,才能开始搬新家具。下次再碰到这个报错,别急着眼花缭乱地乱试,先停两秒,想想是哪个会话赖着不走,用哪个办法最合适。搞定了记得回来跟我分享下,你用的是哪一招。


