MS SQL/Powershell:如何杀死可用性组中辅助节点上的后台进程
Posted
技术标签:
【中文标题】MS SQL/Powershell:如何杀死可用性组中辅助节点上的后台进程【英文标题】:MS SQL/Powershell: how to kill background process on secondary node in Availability Group 【发布时间】:2015-10-26 14:42:25 【问题描述】:我有一个配置了 MS-SQL Server 2014 和始终在线高可用性组的环境(在 2 个节点上)。
我正在编写一个 Powershell 脚本,它从可用性组(在主服务器上)中删除数据库,然后应该在辅助服务器上删除数据库。
这在大多数情况下都有效,但并非总是如此......
我使用以下命令删除辅助服务器上的数据库(此时数据库已从主服务器上的可用性组中删除,并且在辅助服务器上处于“正在恢复”状态):
$SecondaryServerConnection.Databases[$x.Name.ToString()].Drop()
当它失败时,我收到错误消息:
System.Management.Automation.MethodInvocationException:使用“0”参数调用“Drop”的异常:“Drop failed for Database '客户_2'。 " ---> Microsoft.SqlServer.Management.Smo.FailedOperationException:删除数据库“Customer_2”失败。 ---> Microsoft.SqlServer.Management.Common.ExecutionFailureException:执行 Transact-SQL 语句时发生异常或 批。 ---> System.Data.SqlClient.SqlException: 无法删除数据库“Customer_2”,因为它当前正在使用中。
当我在脚本运行时检查 DB-Server (sp_who2) 时,我看到 DB "Customer_2" 有一个进程,其 Status="background"、Command="DB STARTUP" 和 LastWaitType="REDO_THREAD_PENDING工作”。
脚本一旦失败,“Customer_2”的进程就会消失。
我试图修改我的脚本以终止所有进程,但是当我这样做时,我收到错误消息:Only user processes can be killed.
如果发生,它总是发生在第二个数据库上。可用性组中有几个数据库 (3 - 5)。
所以,现在我有几个问题:
如何在我的 Powershell 脚本中摆脱该后台进程?有可能吗?
为什么它适用于第一个数据库而不适用于第二个数据库?我的脚本是否在该数据库上有一个进程,因此该进程仅在脚本失败后才消失?
或者是时间问题,在我删除主服务器上的数据库之后?下降后我有 6 秒 start-sleep
..
这些进程到底是什么?它们存在于辅助服务器上的所有数据库中。
我还没有找到为什么它可以在某些数据库上运行而在其他数据库上不起作用的原因。这可能是数据库大小的问题。有些只有几百 MB,而有些则高达40Gb...
我无法将数据库设置为脱机或将其设置为单用户模式,因为辅助服务器上的数据库未联机。数据库处于“正在恢复”状态。
更新: 我忘了提到的是该进程的 SPID 通常高于 50 。根据我的阅读,低于 50 的 SPID 始终是系统进程。对吗?
【问题讨论】:
在 SQL Server 2000 之后,SPID below 50 = system
不再适用。
好的,感谢@Shawn Melton 的澄清:-)
【参考方案1】:
你可以试试这个脚本,它会杀死指定数据库中的所有活动进程:
DECLARE @sql varchar(50);
DECLARE @dbname sysname;
DECLARE @killStmts TABLE (stmt varchar(30));
SET @dbname = 'yourDatabase'; -- Set this to your database name
INSERT INTO @killStmts
SELECT 'KILL ' + CONVERT(varchar(10), [spid])
FROM master..sysprocesses pr
INNER JOIN master..sysdatabases db ON pr.[dbid] = db.[dbid]
WHERE db.name = @dbname
DECLARE @killCtr int;
SELECT @killCtr = COUNT(1) FROM @killStmts;
WHILE (@killCtr > 0)
BEGIN
SELECT TOP 1 @sql = stmt FROM @killStmts ORDER BY stmt;
EXEC (@sql);
DELETE @killStmts WHERE stmt = @sql;
SELECT @killCtr = @killCtr - 1;
END
您可以将其调整为可由 PowerShell 调用的存储过程(将其安装到主数据库中,以便为您喜欢的任何其他用户数据库调用它),或者您可以尝试将此脚本调整为 PowerShell 本身。
【讨论】:
感谢@pmbAusting,但我之前已经在我的 Powershell 脚本中尝试过类似的方法,但它不起作用。但可以肯定的是,我尝试了您的脚本并收到以下错误消息:Msg 6107, Level 14, State 1, Line 55 Only user processes can be killed.
我个人认为,这是一个 Powershell 问题。也许刷新我的连接,或者关闭并重新打开它,会有所帮助......我要试试
这种方法有一个明显的缺陷,即认为杀死所有进程会阻止创建新进程......即使你正在杀死它们。一旦完成,就会弹出另一个进程。
感谢@JeroenMostert。在许多情况下,您的回答可能是正确的,但在我的情况下,数据库上没有“用户进程”,因为数据库处于“恢复”状态。因此,我无法使数据库脱机,也无法将数据库设置为单用户模式。在输入此答案时,我想知道我是否尝试在删除之前使数据库脱机...现在就试试.谢谢!
当数据库处于“正在恢复”状态时,我无法使数据库脱机。如果我尝试这样做,我会收到错误消息:Exception calling "SetOffline" with "0" argument(s): "Set offline failed for Database 'Customer_2'. " ---> Set offline failed for Database 'Customer_2'. --->ALTER DATABASE is not permitted while a database is in the Restoring state.
【参考方案2】:
我找到了解决这个问题的方法(但不是那个问题的根源)。
我最终编写了一个 Powershell 函数,它尝试使用 3 种不同的方法来删除数据库
Function Remove-SqlDatabase
[CmdletBinding()]
Param(
[string]$Server,
[string]$Database
)
try
$smo = New-SMOconnection -server $Server
$smo.KillDatabase($Database)
$smo.Refresh()
#Write-Host "Successfully dropped $Database on $($smo.name)"
catch
try
$smo.Databases[$Database].Drop()
#Write-Host "Successfully dropped $Database on $($smo.name)"
catch
try
$null = $smo.ConnectionContext.ExecuteNonQuery("DROP DATABASE $Database")
#Write-Host "Successfully dropped $Database on $($smo.name)"
catch
Write-Error "Could not drop database $Database!"
Write-Error $_
throw $_
【讨论】:
以上是关于MS SQL/Powershell:如何杀死可用性组中辅助节点上的后台进程的主要内容,如果未能解决你的问题,请参考以下文章
powershell MS SQL Powershell加载SQL Server PS模块
powershell MS SQL Powershell使用SQLPS创建视图
powershell MS SQL Powershell使用TSQL创建表
powershell MS SQL Powershell使用SQLPS创建表