Servidor SQL: Processo de Kill usando procedimento armazenado
-
05-07-2019 - |
Pergunta
Quero modificar o seguinte, pois ele não parece matar processos - acho que é necessário desconectar os usuários (isso é o mesmo?). Quero poder matar todo o processo para um banco de dados específico - como posso modificar o abaixo:
create procedure [dbo].[sp_killusers](@database varchar(30))
as
----------------------------------------------------
-- * Created By David Wiseman, Updated 19/11/2006
-- * http://www.wisesoft.co.uk
-- * This procedure takes the name of a database as input
-- * and uses the kill statment to disconnect them from
-- * the database.
-- * PLEASE USE WITH CAUTION!!
-- * Usage:
-- * exec sp_killusers 'databasename'
----------------------------------------------------
set nocount on
declare @spid int
declare @killstatement nvarchar(10)
-- Declare a cursor to select the users connected to the specified database
declare c1 cursor for select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = @database
open c1
fetch next from c1 into @spid
-- for each spid...
while @@FETCH_STATUS = 0
begin
-- Don't kill the connection of the user executing this statement
IF @@SPID <> @spid
begin
-- Construct dynamic sql to kill spid
set @killstatement = 'KILL ' + cast(@spid as varchar(3))
exec sp_executesql @killstatement
-- Print killed spid
print @spid
end
fetch next from c1 into @spid
end
-- Clean up
close c1
deallocate c1
Atualizar
O acima não funciona, ou seja, não mata o processo.
Não mata o processo. Eu olho para o monitor de atividades e ainda mostra o processo continuando e posso ver minha consulta ainda trabalhando na janela de consulta. Quando eu faço "Kill 53", as paradas de consulta na janela da consulta e o processo desapareceu do monitor de atividades! Então, a matança funciona, mas não esse procedimento, por quê?
Solução
Estou familiarizado com este script. Ele mata todos os spids que estão usando um banco de dados, sim. Você precisa executá -lo sob as permissões corretas - não apenas qualquer usuário pode matar SPIDs.
Além disso, há uma chance de você ter aplicativos que tentam manter conexões persistentes com o banco de dados e, portanto, podem se reconectar logo após matar o SPID deles.
Outras dicas
Você está apenas tentando interromper toda a atividade em um banco de dados específico para poder fazer alguma manutenção nele?
Nesse caso, você pode fazer o seguinte:
ALTER DATABASE myDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
Isso matará todos os outros espides acessando o banco de dados e colocará o banco de dados no modo de usuário único. Em seguida, execute sua ação de manutenção e faça o seguinte depois:
ALTER DATABASE myDB SET MULTI_USER;
Você pode tentar usar o EXEC em vez do sp_exec (não que ele faça alguma diferença)
SET @killstatement = 'KILL ' + cast(@spid as varchar(3))
EXEC (@killstatement)
Você já tentou alguma depuração/saída do que realmente ocorre quando o procedimento é executado? Por exemplo, você pode modificar você @killstatement a ser declarado como nvarchar (max) e incluir alguma saída detalhada, como a seguinte e postar os resultados? Basicamente, substitua tudo no seu bloco de início/final por algo como:
-- Construct dynamic sql to kill spid
select @killstatement = N'
select *
from sys.dm_exec_sessions s
join sys.dm_exec_connections c
on s.session_id = c.session_id
where c.session_id = @spid;
kill ' + cast(@spid as varchar(3)) + ';
select *
from sys.dm_exec_sessions s
join sys.dm_exec_connections c
on s.session_id = c.session_id
where c.session_id = @spid;
';
-- Print & Exec
print @killstatement;
exec sp_executesql @killstatement, N'@spid smallint', @spid;
print @spid;
Não há razão para que algo deva se comportar de maneira diferente dentro do código do procedimento versus executar explicitamente dentro de uma conexão - supondo que você tenha as permissões apropriadas, está matando espides válidos, etc. etc. se você puder postar os resultados de alguma depuração como o acima ( E qualquer outra coisa que você possa ter tentado), ajudaria a descobrir onde está o problema. Você também pode querer incluir uma saída de depuração dos resultados do cursor que está usando para garantir que você esteja realmente obtendo as sessões que está tentando matar - ou seja, inclua a mesma seleção que você está usando no seu cursor declarar para produzir uma saída A Conjunto de resultados, assim:
declare c1 cursor for select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = @database
-- Debug output - sessions we should try and kill...
select request_session_id
from sys.dm_tran_locks
where resource_type='DATABASE'
AND DB_NAME(resource_database_id) = @database;
Se você puder postar os resultados, espero que isso nos dê algo para continuar.
As chances são boas de que nenhuma delas se aplique a você, mas, para o caso de aqui algumas situações estranhas que encontrei ao trabalhar em coisas assim alguns anos atrás (todos os SQL 2005).
- Você não pode matar sua própria conexão.
- No código que usei, certifiquei -me Nunca Tente matar qualquer SPID abaixo de 51. (Essas são conexões do sistema; não sei se elas podem ser mortas, mas eu não tentaria.)
- Se uma conexão estiver processando uma transação, ela terá que reverter essa transação antes que ela possa ser morta. Transações enormes podem levar um tempo significativo para reverter.
- Cuidado com o pool de conexão. Eles são como os mortos-vivos-matam-os, e eles voltam diretamente, em menos de um segundo.
Executar o SQL Profiler e rastrear logins e logouts enquanto você executa esse processo pode ser revelador, principalmente para problemas de agrupamento de conexões.
Isso funciona para mim no sqlserver 2000
DECLARE @DbName VARCHAR(100)
DECLARE @SPID INT
DECLARE @TranUOW UNIQUEIDENTIFIER
DECLARE @KillStmt NVARCHAR(100)
SET @DbName = 'MyDatabase'
-----------------------------------
-- Kill distributed transactions
DECLARE dist CURSOR FOR
SELECT DISTINCT req_transactionUOW
FROM master..syslockinfo
WHERE db_name(rsc_dbid) = @DbName
AND req_transactionUOW <> '00000000-0000-0000-0000-000000000000'
OPEN dist
FETCH NEXT FROM dist INTO @TranUOW
WHILE @@FETCH_STATUS = 0
BEGIN
SET @KillStmt = 'kill ''' + CAST(@TranUOW AS VARCHAR(50)) + ''''
PRINT @KillStmt
EXECUTE(@KillStmt)
FETCH NEXT FROM dist INTO @TranUOW
END
CLOSE dist
DEALLOCATE dist
-----------------------------------
-- Kill user connections
DECLARE cur CURSOR FOR
SELECT spid
FROM master..sysprocesses
WHERE db_name(dbid) = @DbName
AND spid > 50
OPEN cur
FETCH NEXT FROM cur INTO @SPID
WHILE @@FETCH_STATUS = 0
BEGIN
SET @KillStmt = 'kill ' + CAST(@SPID AS VARCHAR(10))
PRINT @KillStmt
EXECUTE(@KillStmt)
FETCH NEXT FROM cur INTO @SPID
END
CLOSE cur
DEALLOCATE cur