SQL Server: عملية القتل باستخدام الإجراء المخزن
-
05-07-2019 - |
سؤال
أريد تعديل ما يلي لأنه لا يبدو أنه يقتل العمليات - أعتقد أنه من المفترض أن يفصل المستخدمون (هل هذا هو نفسه؟). أريد أن أكون قادرًا على قتل جميع عمليات قاعدة بيانات معينة - كيف يمكنني تعديل ما يلي:
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
تحديث
ما سبق لا يعمل ، أي أنه لا يقتل العملية.
لا تقتل العملية. إنني أنظر إلى مراقبة النشاط وما زال يوضح العملية مستمرة ويمكنني رؤية استفساري لا يزال يعمل في نافذة الاستعلام. عندما أفعل "Kill 53" ، يتوقف الاستعلام في نافذة الاستعلام وذهب العملية من شاشة النشاط! لذا فإن القتل يعمل ولكن ليس هذا الإجراء لماذا؟
المحلول
أنا على دراية بهذا السيناريو. إنه يقتل جميع السدادات التي تستخدم قاعدة بيانات ، نعم. تحتاج إلى تشغيله تحت الأذونات الصحيحة - وليس فقط أي مستخدم يمكن أن يقتل السدادات.
أيضًا ، هناك فرصة لك قد يكون لديك تطبيقات تحاول الحفاظ على اتصالات مستمرة مع DB ، وبالتالي قد تقوم بإعادة الاتصال بعد فترة وجيزة من قتل Spid.
نصائح أخرى
هل تحاول فقط إيقاف كل النشاط على ديسيبل معين حتى تتمكن من القيام ببعض الصيانة؟
إذا كان الأمر كذلك ، يمكنك القيام بما يلي:
ALTER DATABASE myDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
سيؤدي ذلك إلى قتل جميع السدادات الأخرى التي تصل إلى DB ، وسيضع DB في وضع المستخدم الواحد. ثم قم بإجراء إجراء الصيانة ، وقم بما يلي بعد ذلك:
ALTER DATABASE myDB SET MULTI_USER;
قد ترغب في محاولة استخدام EXEC بدلاً من SP_EXEC (لا ينبغي أن يحدث أي فرق)
SET @killstatement = 'KILL ' + cast(@spid as varchar(3))
EXEC (@killstatement)
هل جربت أي تصحيح/إخراج لما يحدث بالفعل عند تشغيل الإجراء؟ على سبيل المثال ، هل يمكنك تعديل 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;
إذا تمكنت من نشر النتائج ، نأمل أن يمنحنا ذلك شيئًا نذهب إليه.
من الجيد أن أيا من هذه الاحتمالات لا ينطبق عليك ، ولكن في حالة وجود بعض المواقف الغريبة التي واجهتها عند العمل على أشياء مثل هذه قبل بضع سنوات (جميع SQL 2005).
- لا يمكنك قتل اتصالك الخاص.
- في الكود الذي استخدمته ، تأكدت من ذلك أبداً حاول أن تقتل أي سبد أقل من 51. (هذه هي اتصالات النظام ؛ لا أعرف ما إذا كان يمكن قتلها ، لكنني لن أحاول ذلك.)
- إذا كان الاتصال يعالج معاملة ، فيجب عليه أن يلف هذه المعاملة قبل أن يتم قتله. يمكن أن تستغرق المعاملات الضخمة وقتًا كبيرًا للتراجع.
- حذار تجميع الاتصال. إنهم مثل أوندد-خلكهم ، وهم يعودون مباشرة ، وغالبًا ما يكونون في الثانية.
قد يكون تشغيل SQL Profiler وتتبع تسجيلات تسجيل الدخول و Logouts أثناء تشغيل هذه العملية يكشف ، خاصة بالنسبة لمشكلات تجميع الاتصالات.
هذا يعمل بالنسبة لي في 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