Descubrir bloqueos en SQL Server

Posted by Miguel Egea | Posted in Relacional | Posted on 08-01-2010

9

Los bloqueos son la principal causa de bajo rendimiento inexplicable, y generalmente son muy complicados de cazar, de detectar y de solucionar, hablaremos de bloqueos más adelante, pero ahora mismo vamos a poner un procedimiento almacenado que sirve para cazar a los procesos bloqueadores y pondremos una imagen en la que se ve cual es el resultado, en proximos capítulos pondremos como configurar el SQL Mail para que además nos lleguen por correo electrónico

 */   CREATE PROC CazaBloqueadores @waittime varchar(10) 
as 
begin 
begin try 
declare @t table (bloqueado int, bloqueador int) -- usaremos la variable de tipo tabla para esperar 
insert into @t (bloqueado,bloqueador) 
select session_id, blocking_session_id 
from sys.dm_exec_requests der where blocking_session_id!=0 -- Recogemos los objetos que están bloqueados.. 
waitfor delay @waittime 
SELECT der.session_id [Sesion bloqueada], det.text [Comando Bloqueado],
 der.[blocking_Session_id] [id del bloqueador], subq.TEXT [Comando Bloqueador], 
des.[host_name] [Nombre del host], des.[Program_name] [Aplicación], 
db_name(der.database_id) [Base de Datos], user_name(der.user_id) [usuario de bbdd], 
des.login_name [login], cast(des.context_info as varchar(100)) [Informacion Adicional], 
des.cpu_time [Tiempo de CPU], des.total_elapsed_time [Tiempo de espera], 
des.logical_reads [lecturas], des.writes [escrituras], der.start_time [Comenzó], 
der.status [estado], 
case des.transaction_isolation_level 
when 0 then 'Sin especificar' 
when 1 then 'Read Uncommitted' 
when 2 then 'Read Committed' 
when 3 then 'Repeatable reads' 
when 4 then 'Serializable' 
when 5 then 'Snapshot' 
else 'Inesperado' end [Nivel de Aislamiento] 
FROM sys.dm_exec_requests der 
                      INNER JOIN sys.dm_exec_sessions des 
                                 ON der.session_id=des.session_id 
                            CROSS APPLY sys.dm_exec_sql_text(sql_handle) det 
                     LEFT JOIN  (select SPID session_id, det2.[text] from sys.sysprocesses der2 CROSS APPLY 
                                         sys.dm_exec_sql_text(sql_handle) det2 ) subq 
                    ON der.[blocking_Session_id]=subq.session_id 
WHERE der.blocking_session_id in 
       (select bloqueador from @t) 
return 0 
end try 
begin catch 
declare @error sysname 
set @error=ERROR_MESSAGE() 
raiserror (@error,16,1) return 1 
end catch 
end

y esta la versión para SQL 2000

-- ***************************************************************************
-- Copyright (C) PortalSQL 2006
--
-- Fichero: CazaBloqueadores.sql
-- Descr.: Busca bloqueos que se mantengan
-- durante el tiempo que le pasemos por parámetro, si hay bloqueos que se mantienen
-- muestra información relevante
-- autor: Miguel Egea Gómez -- Solid Quality Learning IberoAmericana
--
-- comentarios: Puede ser adecuado meter ejecutar este procedimiento desde un job y
-- usar DatabaseMail para reportar sus resultados al administrador de base
-- de datos.
-- He usado la vista de compatibilidad sys.sysprocesses, esto es porque he c
-- comprobado que algunas ocasiones la tabla sys.dm_exec_request no tiene registros
-- para los bloqueadores.
--
-- Parámetros de entrada:
-- @waittime --> Tiempo que debe esperarse para considerar que hay un bloqueo
-- Valores de retorno : 0--> Funcionó de forma correcta
-- 1--> En cualquir otro caso
--
-- <!--(más reciente más arriba) -->
-- yyyy/mm/dd by description
-- ========== ======= ==========================================================
-- 2006/06/04 MEG Creación.
--
-- ***************************************************************************
-- Ejemplo de llamada
--
/*
DECLARE @Valor varchar(10) ,@result int
SET @Valor='00:00:02'
EXEC @result =CazaBloqueadores @valor
select @result

Aquí puedes ver una imagen en la que se ejecuta un comando que genera un bloqueo, y un comando que se ve bloqueado por ese bloqueo, cuando se crea una tabla dentro de una transacción se genera un bloqueo, un select que consulte esa tabla espera a que se haga commit o rollback en la creación, para devolver los registros o un error. 

a continuación puedes ver más resultados devueltos por el procedimiento almacenado

Disfruta del código

Modificado para incluir Versión SQL Server 2000 en marzo de 2011

Si buscas un código equivalente, pero para SQL Server 2000, este podría estar bien

Â

create procedure cazabloqueos2000
as
begin 
create table #bloqueos(spidbloqueador int,hostnamebloqueador sysname,loginamebloqueador sysname,
					   spidbloqueado int,hostnamebloqueado sysname,loginamebloqueado sysname)
insert into #bloqueos					   
select a.spid,a.hostname,a.loginame,b.spid,b.hostname,b.loginame From (
select * from sysprocesses where blocked!=0) a inner join
master.sys.sysprocesses b on a.blocked=b.spid
declare @spid int
 
alter table #bloqueos add sentenciabloqueador varchar(8000);
alter table #bloqueos add sentenciabloqueado varchar(8000);
 
declare cr cursor for select distinct spidbloqueador from #bloqueos
open cr
fetch next from cr into @spid
create table #temp (eventtype varchar(100),paremeters varchar(100),eventinfo varchar(8000) )
declare @sql nvarchar(1000)
 
 while (@@FETCH_STATUS=0)
  begin
   select @sql='dbcc inputbuffer(' +cast(@spid as varchar(10))  +')'
   insert into #temp exec(@sql)    
 
    update b set sentenciabloqueador=eventinfo  
     from #bloqueos b cross join #temp
       where b.spidbloqueador=@spid
    delete from #temp
   fetch next from cr into @spid
  end
close cr 
deallocate cr 
 
 
 
declare cr cursor for select distinct spidbloqueado from #bloqueos
open cr
fetch next from cr into @spid
 
 
 while (@@FETCH_STATUS=0)
  begin
   select @sql='dbcc inputbuffer(' +cast(@spid as varchar(10))  +')'
   insert into #temp exec(@sql)    
 
    update b set sentenciabloqueado=eventinfo  
     from #bloqueos b cross join #temp
       where b.spidbloqueado=@spid
    delete from #temp
   fetch next from cr into @spid
  end
close cr 
deallocate cr 
 
select * From #bloqueos
end

Comments (9)

Un consejo, instalate el plugin codebox para que sea mas entendible para todos nosotros tu codigo :D

Lo dicho eres una p… máquina. Interesantísimo como todos tus artículos. Por cierto, haber cuando nos deleitas con uno dedicado a directivas, ya sabes, por pedir que no quede :)

Nos vemos ….

Instalado el plugging, realmente instalado por segunda vez, porque lo instalé la primera y algo debió subirse mal, pensé que no funcionaba ya hora… me parece una maravilla, gracias GeekZero

Muchas gracias por el aporte. Espero que me sea de utilidad en algún momento, que seguro que así será.

Un saludo

M sale error “sql_handle” no es una opción reconocida de sugerencias de tabla. Si se debe usar como un parámetro en una función con valores de tabla, asegúrese de que el modo de compatibilidad de la base de datos está establecido en 90.
Tengo sql2008,,

Efectivaamente Rafael, esto solo funciona en bases de datos SQL 2005 y adelante, es decir nivel de compatibilidad 90 y adelante. Para compatibilidad 2000 hay que usar dbcc inputbuffer.

Miguel, me interesa mucho tu proceso, pero me pasa lo mismo que a Rafael, pero no se como colocar el dbcc inputbuffer, me puedes ayudar por favor, Gracias

Incluida versión para SQL Server 2000, espero que resulte útlil. y que funcione, porque lo hice en un 2008 intentando acordarme de la sintaxis :)

Hola Miguel, el codigo me resulta muy util, me has salvado.
Una pregunta, lo quiero meter en un job, pero no quiero afectar mas el performance del servidor. Que frecuencia recomiendas para que se ejecute el job sin darle sobrecarga al servidor?

Write a comment