SQL Server: уничтожить процесс с помощью хранимой процедуры - PullRequest
4 голосов
/ 27 ноября 2009

Я хочу изменить следующее, так как оно, кажется, не убивает процессы - я думаю, что оно должно отключать пользователей (это то же самое?). Я хочу иметь возможность убить все процессы для конкретной базы данных - как я могу изменить ниже:

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

Обновление

Вышеприведенное не работает, т.е. не убивает процесс.

Это не убивает процесс. я смотрю на монитор активности и его еще показывает процесс продолжается, и я могу увидеть мой запрос все еще работает в окно запроса. Когда я делаю "убить 53", запрос останавливается в окне запроса и процесс ушел из деятельности монитор! Так что убить работает, но не эта процедура, почему?

Ответы [ 6 ]

3 голосов
/ 08 декабря 2009

Вы просто пытаетесь остановить все действия с определенной БД, чтобы можно было выполнить некоторые операции с ней?

Если это так, вы можете сделать следующее:

ALTER DATABASE myDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;

Это уничтожит все остальные SPID, обращающиеся к БД, и переведет БД в однопользовательский режим. Затем выполните действия по обслуживанию и выполните следующие действия:

ALTER DATABASE myDB SET MULTI_USER;
2 голосов
/ 27 ноября 2009

Я знаком с этим сценарием. Это убивает все SPID, которые используют базу данных, да. Вы должны запустить его с правильными разрешениями - не любой пользователь может убить SPID.

Также есть вероятность, что у вас могут быть приложения, которые пытаются поддерживать постоянные соединения с БД, и, следовательно, могут переподключиться вскоре после того, как вы уничтожите их SPID.

0 голосов
/ 08 декабря 2009

Это работает для меня в 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
0 голосов
/ 07 декабря 2009

Скорее всего, ни один из них не применим к вам, но на всякий случай вот некоторые странные ситуации, с которыми я столкнулся, работая над подобными вещами несколько лет назад (все в SQL 2005).

  • Вы не можете разорвать собственное соединение.
  • В коде, который я использовал, я убедился, что никогда не попытается уничтожить любой спид до 51. (Это системные соединения; я не знаю, могут ли они быть уничтожены, но я бы не попробуй.)
  • Если соединение обрабатывает транзакцию, оно должно откатить эту транзакцию, прежде чем оно может быть прервано. Откат огромных транзакций может занять значительное время.
  • Остерегайтесь пулов соединений. Они как нежить - убивают их, и они просто возвращаются, часто через секунду.

Запуск SQL Profiler и отслеживание входов и выходов из системы во время выполнения этого процесса может быть показательным, особенно для проблем с пулами соединений.

0 голосов
/ 03 декабря 2009

Вы пробовали отладку / вывод того, что на самом деле происходит при запуске процедуры? Например, можете ли вы изменить свой @killstatement так, чтобы он был объявлен как nvarchar (max), и включили несколько подробных выводов, таких как приведенные ниже, и опубликовали результаты? В основном замените все в вашем блоке начала / конца на что-то вроде:

-- 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;

Нет причин, по которым что-то должно вести себя по-другому в коде процедуры по сравнению с явным выполнением в соединении - при условии, что у вас есть соответствующие разрешения, вы убиваете допустимые спиды и т. Д., И т. Д. Если вы можете опубликовать результаты некоторой отладки, например выше (и все, что вы, возможно, пытались), это поможет выяснить, где проблема. Возможно, вы также захотите включить отладочный вывод результатов объявления курсора, который вы используете, чтобы убедиться, что вы фактически получаете сеансы, которые вы пытаетесь уничтожить - т.е. просто включите тот же выбор, который вы используете в объявлении курсора, чтобы вывести набор результатов, например:

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;

Если вы сможете опубликовать результаты, надеюсь, это даст нам возможность продолжить.

0 голосов
/ 30 ноября 2009

Возможно, вы захотите попробовать использовать exec вместо sp_exec (не то, чтобы это имело значение)

SET @killstatement = 'KILL ' + cast(@spid as varchar(3)) 
EXEC (@killstatement)
...