【问题标题】:How do you stop a user-instance of Sql Server? (Sql Express user instance database files locked, even after stopping Sql Express service)如何停止 Sql Server 的用户实例? (Sql Express 用户实例数据库文件被锁定,即使在停止 Sql Express 服务之后)
【发布时间】:2011-01-22 23:40:17
【问题描述】:

当使用带有如下连接字符串的 SQL Server Express 2005 的用户实例功能时:

<add name="Default" connectionString="Data Source=.\SQLExpress;
  AttachDbFilename=C:\My App\Data\MyApp.mdf;
  Initial Catalog=MyApp;
  User Instance=True;
  MultipleActiveResultSets=true;
  Trusted_Connection=Yes;" />

我们发现即使停止SqlExpress服务,我们也无法复制数据库文件MyApp.mdf和MyApp_Log.ldf(因为它们被锁定),不得不求助于将SqlExpress服务从自动启动模式设置为手动启动模式,然后重启机器,然后我们就可以复制文件了。

我的理解是停止 SqlExpress 服务也应该停止所有用户实例,这应该会释放这些文件上的锁。但情况似乎并非如此 - 任何人都可以阐明如何停止用户实例,使其数据库文件不再被锁定?


更新

好的,我不再懒惰并启动了 Process Explorer。锁由 sqlserver.exe 持有 - 但有两个 sql server 实例:

sqlserver.exe  PID: 4680  User Name: DefaultAppPool
sqlserver.exe  PID: 4644  User Name: NETWORK SERVICE

文件由 PID:4680 的 sqlserver.exe 实例打开

停止“SQL Server (SQLEXPRESS)”服务,终止 PID: 4644 的进程,但单独留下 PID: 4680。

看到剩余进程的所有者是 DefaultAppPool,接下来我尝试停止 IIS(该数据库正在从 ASP.Net 应用程序中使用)。不幸的是,这也没有终止进程。

手动终止剩余的 sql server 进程确实会删除数据库文件上的打开文件句柄,从而允许它们被复制/移动。

不幸的是,我希望在 WiX 安装程序的一些安装前/安装后任务中复制/恢复这些文件 - 因此,我希望有一种方法可以通过停止 Windows 服务来实现这一点,而不必费劲杀死所有 sqlserver.exe 实例,因为这会带来一些问题:

  1. 杀死所有 sqlserver.exe 实例可能会对计算机上具有其他 Sql Server 实例的用户产生不良后果。
  2. 我无法轻松重启这些实例。
  3. 在安装程序中引入了额外的复杂性。

是否有人对如何关闭与特定用户实例关联的 sql server 实例有任何进一步的想法?

【问题讨论】:

  • A感谢所有答案 - 最有用的答案是使用 SSEUtil.exe。虽然我们需要从 WiX 安装程序中调用它,但我们可以看到它会有一些问题,即如果运行安装程序的用户没有执行 sp_dettach_db 的权限,无论我们使用哪种方法,这都是一个问题 - 所以我们可能只需在升级之前为用户提供一些手动预安装步骤,以便他们可以使用 SSEUtil.exe 自行处理。
  • 我很好奇,SSEUtil.exe 不能解决权限问题吗?如果用户必须使用 SSEUtil 自己处理,那么安装程序不应该调用 SSEUtil 吗?
  • 在我的测试中,情况似乎并非如此 - 我相信这只是因为在调用 SSEUtil 时安装程序没有提升......但我没有时间进一步调查它然而。在旁注中,我们发现 SSEUtil 不会在安装了 SqlServer 和 SqlServer Express 的 Windows7 x64 上正确列出所有活动用户实例,即使传入 -s .\SqlExpress 参数也是如此。所以我们还有一些边缘情况需要解决:)

标签: sql file wix locking sql-server-express


【解决方案1】:

我还不能发表评论,因为我还没有足够高的代表。有人可以将此信息移至其他答案,以免我们被骗吗?

我刚刚使用这篇文章来解决我的 WIX 卸载问题。我使用了 AMissico 的回答中的这一行。

string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);

在使用 WIX 时工作得很好,只是我必须添加一件事才能让它对我有用。

我已取出 sp_detach_db,然后将 db 重新联机。如果您不这样做,WIX 将在卸载后保留 mdf 文件。一旦我将 db 重新联机,WIX 将正确删除 mdf 文件。

这是我修改后的行。

string.Format( "USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tALTER DATABASE [{0}] SET ONLINE\nEND", dbName );

【讨论】:

    【解决方案2】:

    使用“SQL Server Express Utility”(SSEUtil.exe) 或命令来分离 SSEUtil 使用的数据库。

    SQL Server Express 实用程序, SSEUtil 是一个让您轻松与 SQL Server 交互的工具, http://www.microsoft.com/downloads/details.aspx?FamilyID=fa87e828-173f-472e-a85c-27ed01cf6b02&DisplayLang=en

    此外,在最后一个连接关闭后停止服务的默认超时时间是一小时。在您的开发框中,您可能希望将其更改为五分钟(允许的最小值)。

    此外,您可能通过 Visual Studio 的服务器资源管理器数据连接打开了连接,因此请务必断开与那里的任何数据库的连接。

    H:\Tools\SQL Server Express Utility>sseutil -l
    1. master
    2. tempdb
    3. model
    4. msdb
    5. C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIENTS\CORE.DATA.C
    LIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF
    
    H:\Tools\SQL Server Express Utility>sseutil -d C:\DEV*
    Failed to detach 'C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIE
    NTS\CORE.DATA.CLIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF'
    
    H:\Tools\SQL Server Express Utility>sseutil -l
    1. master
    2. tempdb
    3. model
    4. msdb
    
    H:\Tools\SQL Server Express Utility>
    

    使用 .NET Refector,以下命令用于分离数据库。

    string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);
    

    【讨论】:

    • 谢谢,我需要在许多客户机器(他们不会安装 SSEUtil)上的 WiX 安装程序中调用此操作,但我会尽可能地探索这个选项。
    • SSEUtil 是 .NET 应用程序。使用 .NET Reflector 查看它是如何分离数据库的。
    • 我建议将 WiX 安装程序的要求作为完整问题的一部分。 (你的最后一句话。)很容易错过。
    【解决方案3】:

    这可能不是您想要的,但免费工具 Unlocker 有一个可以从 WIX 运行的命令行界面。 (我已经使用了一段时间的解锁器,发现它很稳定,并且非常擅长解锁文件。)

    Unlocker 可以解锁和移动/删除大多数文件。

    这样做的缺点是需要锁定文件的应用程序将不再拥有它。 (但有时仍然可以正常工作。)请注意,这不会杀死拥有锁的进程。它只是删除它的锁。 (可能是重新启动您正在停止的 sql 服务就足以让它重新锁定和/或正常工作。)

    你可以从这里获得解锁器:http://www.emptyloop.com/unlocker/

    查看命令行选项运行unlocker -H 为了方便起见,它们在这里:

    解锁器 1.8.8 命令行用法: Unlocker.exe 对象 [选项] 目的: 完整路径,包括文件或文件夹的驱动器 选项: /H 或 -H 或 /?或 -?:显示命令行用法 /S 或 -S:解锁对象而不显示 GUI /L 或 -L:对象是包含要解锁的文件列表的文本文件 /LU 或 -LU:与 /L 类似,带有要解锁的文件的 unicode 列表 /O 或 -O:输出 Unlocker 目录下的 Unlocker-Log.txt 日志文件 /D 或 -D:删除文件 /R Object2 或 -R Object2:重命名文件,如果设置了 /L 或 /LU object2 指向包含文件新名称的文本文件 /M Object2 或 -M Object2:移动文件,如果设置了 /L 或 /LU,则 object2 指向一个包含文件新位置的文本文件

    假设您的目标是将 C:\My App\Data\MyApp.mdf 替换为安装程序中的文件,您可能需要类似 unlocker C:\My App\Data\MyApp.mdf -S -D 的内容。这将删除该文件,以便您可以复制一个新文件。

    【讨论】:

    • 嗯,当你投了反对票而没有评论为什么时,一定会喜欢它。
    • 我认为否决票是因为如果 mdf 被锁定,那是有原因的,使用解锁器可能会损坏数据库或更糟糕的事情。
    【解决方案4】:

    我一直在使用以下辅助方法在单元测试中分离附加到 SQL Server 的 MDF 文件(以便 SQ Server 释放对 MDF 和 LDF 文件的锁定,并且单元测试可以自行清理)...

    private static void DetachDatabase(DbProviderFactory dbProviderFactory, string connectionString)
    {
        using (var connection = dbProviderFactory.CreateConnection())
        {
            if (connection is SqlConnection)
            {
                SqlConnection.ClearAllPools();
    
                // convert the connection string (to connect to 'master' db), extract original database name
                var sb = dbProviderFactory.CreateConnectionStringBuilder();
                sb.ConnectionString = connectionString;
                sb.Remove("AttachDBFilename");
                var databaseName = sb["database"].ToString();
                sb["database"] = "master";
                connectionString = sb.ToString();
    
                // detach the original database now
                connection.ConnectionString = connectionString;
                connection.Open();
                using (var cmd = connection.CreateCommand())
                {
                    cmd.CommandText = "sp_detach_db";
                    cmd.CommandType = CommandType.StoredProcedure;
    
                    var p = cmd.CreateParameter();
                    p.ParameterName = "@dbname";
                    p.DbType = DbType.String;
                    p.Value = databaseName;
                    cmd.Parameters.Add(p);
    
                    p = cmd.CreateParameter();
                    p.ParameterName = "@skipchecks";
                    p.DbType = DbType.String;
                    p.Value = "true";
                    cmd.Parameters.Add(p);
    
                    p = cmd.CreateParameter();
                    p.ParameterName = "@keepfulltextindexfile";
                    p.DbType = DbType.String;
                    p.Value = "false";
                    cmd.Parameters.Add(p);
    
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }
    

    注意事项:

    • SqlConnection.ClearAllPools() 在消除“隐形”连接方面非常有帮助(当连接池化时,即使您“关闭()”它,它也会保持活动状态;通过明确清除池连接,您不需要不必担心在所有连接字符串中将池标志设置为 false)。
    • “神奇成分”是调用系统存储过程sp_detach_db (Transact-SQL)
    • 我的连接字符串包含“AttachDBFilename”但不包含“User Instance=True”,因此此解决方案可能不适用于您的方案

    【讨论】:

    • 感谢代码 - 在我的情况下,我需要从 WiX 安装程序(而不是应用程序本身、测试夹具等)调用它 - 不幸的是,用户实例的 db 用户可能没有在 master 上调用 sp_detach_db 的足够权限。但它确实给了我其他一些尝试的选择。
    • 以下 MSDN 文档页面 msdn.microsoft.com/en-us/library/bb264564(SQL.90).aspx 指出:“通过调用 sp_detach_db 从实例中分离数据库将关闭文件。这是 Visual Studio 用来确保在IDE 在用户实例之间切换。” - 似乎 Visual Studio 本身使用 sp_detach_db 来关闭用户实例:-)。我假设@AMissico 建议的 SSEUtil 工具也使用它(它以方便的 exe 形式提供,可从 Wix 调用)
    • 我更新了答案以包含 SSEUtil 使用的实际命令。
    • 我认为 @keepfulltextindexfile 在 Sql 2008 更改后已被弃用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    相关资源
    最近更新 更多