SQL Server: Kill Process usando Stored Procedure
-
05-07-2019 - |
Domanda
Voglio modificare quanto segue poiché non sembra uccidere i processi - penso che dovrebbe disconnettere gli utenti (è lo stesso?). Voglio essere in grado di terminare tutto il processo per un determinato database - come posso modificare quanto segue:
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
Aggiornamento
Quanto sopra non funziona, cioè non uccide il processo.
Non uccide il processo. guardo a il monitor attività e il suo fermo mostra che il processo continua e io posso vedere la mia query ancora funzionante in finestra di query. Quando faccio "uccidere 53", il l'interrogazione si interrompe nella finestra dell'interrogazione e il processo è passato dall'attività tenere sotto controllo! Quindi l'uccisione funziona ma non questa procedura perché?
Soluzione
Conosco bene questo script. Uccide tutti gli SPID che utilizzano un database, sì. Devi eseguirlo con le autorizzazioni corrette, non tutti gli utenti possono uccidere gli SPID.
Inoltre, è possibile che siano presenti applicazioni che tentano di mantenere connessioni persistenti al DB e pertanto potrebbero riconnettersi poco dopo aver ucciso il loro SPID.
Altri suggerimenti
Stai solo cercando di interrompere tutte le attività su un determinato DB in modo da poter fare un po 'di manutenzione su di esso?
In tal caso, puoi eseguire le seguenti operazioni:
ALTER DATABASE myDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
Questo ucciderà tutti gli altri SPID che accedono al DB e metterà il DB in modalità utente singolo. Quindi eseguire le operazioni di manutenzione e procedere come segue:
ALTER DATABASE myDB SET MULTI_USER;
Potresti provare a usare exec invece di sp_exec (non che dovrebbe fare alcuna differenza)
SET @killstatement = 'KILL ' + cast(@spid as varchar(3))
EXEC (@killstatement)
Hai provato a eseguire il debug / output di ciò che si verifica effettivamente quando viene eseguita la procedura? Ad esempio, puoi modificare @killstatement per essere dichiarato come nvarchar (max) e includere un output dettagliato come il seguente e pubblicare i risultati? Sostanzialmente sostituisci tutto nel tuo blocco iniziale / finale con qualcosa del tipo:
-- 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;
Non vi è alcun motivo per cui qualcosa dovrebbe comportarsi in modo diverso all'interno del codice della procedura rispetto all'esecuzione esplicita all'interno di una connessione - supponendo che tu abbia le autorizzazioni appropriate, stia uccidendo spid validi, ecc. ecc. Se puoi pubblicare i risultati di alcuni debug come quanto sopra (e qualsiasi altra cosa tu possa aver provato), aiuterebbe a capire dov'è il problema. Potresti anche voler includere un output di debug dei risultati del cursore dichiarare che stai utilizzando per assicurarti di ottenere effettivamente le sessioni che stai tentando di uccidere - vale a dire semplicemente includere la stessa selezione che stai usando nel cursore dichiarare di produrre un set di risultati, in questo modo:
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 puoi pubblicare i risultati, speriamo che ci dia qualcosa da fare.
Le probabilità sono buone che nessuna di queste si applichi a te, ma nel caso in cui qui ci siano alcune situazioni strane che ho incontrato lavorando su cose come questa qualche anno fa (tutto SQL 2005).
- Non puoi uccidere la tua connessione.
- Nel codice che ho usato, mi sono assicurato di non mai tentare di uccidere qualsiasi spid sotto 51. (Queste sono connessioni di sistema; non so se possono essere uccise, ma non vorrei provalo.)
- Se una connessione sta elaborando una transazione, deve eseguire il rollback di quella transazione prima che possa essere interrotta. Transazioni enormi possono richiedere molto tempo per il rollback.
- Attenzione al pool di connessioni. Sono come i non morti: uccidili e tornano subito, spesso in meno di un secondo.
L'esecuzione di SQL Profiler e il monitoraggio di accessi e disconnessioni durante l'esecuzione di questo processo potrebbero rivelare problemi, in particolare per problemi di pool di connessioni.
Questo funziona per me in 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