Tôi có thể được tự động thông báo về việc chặn kéo dài trong máy chủ SQL không?


8

Mỗi tuần một lần tôi phải giải quyết chuỗi chặn trên cơ sở dữ liệu SQL Server 2005, do khóa đọc tồn tại lâu từ giao diện người dùng Access 2003. Khóa được lấy ra bất cứ khi nào người dùng mở một biểu mẫu nhất định và được giải phóng khi người dùng đã hoàn thành việc cuộn qua biểu mẫu hoặc đóng nó. Vì nhiều người dùng của chúng tôi mở biểu mẫu này làm tài liệu tham khảo, các khóa này tồn tại trong một thời gian. Bất kỳ cập nhật nào cho bảng đều gây ra sự chặn và đột nhiên không ai có thể chọn từ bảng này vì tất cả đều đang chờ ở khóa đầu tiên. Đây là một vấn đề khá lớn đối với chúng tôi, vì rất nhiều ứng dụng dựa trên dữ liệu này. Tôi hiểu rằng hành vi khóa này là một phần trong cách Access hoạt động với các bảng được liên kết.

Tôi đã giải quyết vấn đề từ Trình giám sát hoạt động, bằng cách giết bất kỳ quy trình CHỌN nào là Trình chặn đầu bất cứ khi nào tôi phát hiện ra. Đây là một vấn đề không chỉ bởi vì tôi mất thời gian để làm nó bằng tay, mà còn bởi vì nó phản ứng. Vào thời điểm tôi nghe về nó, nó đã là một vấn đề đối với nhiều người.

Tôi muốn biết liệu có cách nào tự động để kiểm tra các chuỗi chặn kéo dài này hay không và được gửi qua email hoặc giải quyết vấn đề tự động. Logic có vẻ đủ đơn giản ("nếu bất kỳ quy trình nào khớp với truy vấn CHỌN này đã bị chặn lâu hơn một phút, hãy thông báo cho tôi / tiêu diệt nó") nhưng tôi không biết cách thực hiện điều này với SQL Server.

Đối với những gì nó có giá trị, tôi nghĩ giải pháp thích hợp là sửa chữa hoặc viết lại ứng dụng. Tuy nhiên, do chính trị của bộ, đây không phải là một lựa chọn trong vài tháng tới, vì vậy tôi đang tìm kiếm một điểm dừng.


Câu trả lời:


9

Bạn đã xem xét sử dụng cách ly ảnh chụp ? Việc kích hoạt read_commned_snapshot trong cơ sở dữ liệu sẽ khiến tất cả các lần đọc (chọn) bị khóa miễn phí:

alter database [...] set read_committed_snapshot on;

Không có thay đổi ứng dụng. Một số ngữ nghĩa thay đổi trong ảnh chụp nhanh và ứng dụng của bạn có thể phản ứng kỳ lạ, nhưng đó là ngoại lệ không phải là tiêu chuẩn. Phần lớn các ứng dụng không nhận thấy bất kỳ sự khác biệt nào, chúng chỉ giúp tăng hiệu suất miễn phí.

Dù sao, tôi cũng trả lời câu hỏi ban đầu : làm thế nào để phát hiện (và có thể giết) một truy vấn chạy dài. Trên thực tế, động cơ đã làm điều đó cho bạn. Có một sự kiện được nêu ra khi ngưỡng được thông qua: Lớp sự kiện báo cáo quy trình bị chặn . Ngưỡng được cấu hình thông qua tùy chọn ngưỡng quá trình bị chặn . Bất kỳ sự kiện theo dõi nào cũng có thể được chuyển thành Thông báo sự kiện và thông báo sự kiện có thể kích hoạt quy trình . Kết nối các dấu chấm và bạn có mã kích hoạt theo yêu cầu chạy khi động cơ phát hiện truy vấn đã vượt qua ngưỡng thời gian thực hiện. Không bỏ phiếu, không giám sát. Lưu ý rằng thông báo không đồng bộ, tại thời điểm bạn xử lý nó, truy vấn có thể đã được hoàn thành để phải được tính đến.

Đây là một ví dụ:

use msdb;
go

create queue blocked_process_report_queue;
go

create service blocked_process_report_service
    on queue blocked_process_report_queue
    ([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]);

create event notification blocked_process_report_notification
    on server
    for BLOCKED_PROCESS_REPORT
    to service N'blocked_process_report_service',
          N'current database';
go  

sp_configure 'show advanced options', 1 ;
GO
RECONFIGURE ;
GO
sp_configure 'blocked process threshold', 20 ;
GO
RECONFIGURE ;

Bây giờ trong một truy vấn mới thiết lập WAITFORmột thông báo mong đợi:

use msdb;
waitfor(
   receive cast(message_body as xml), * 
   from  blocked_process_report_queue);

Và đi trước và gây ra một số tắc nghẽn. Tôi đã sử dụng một quy trình tạo bảng và không cam kết và từ một cửa sổ truy vấn khác tôi đã cố gắng chọn từ bảng. Trong 20 giây (ngưỡng được định cấu hình của tôi ở trên) tôi đã nhận được báo cáo chặn:

<blocked-process-report>
  <blocked-process>
    <process id="process1b5a24ca8" ...>
      <executionStack>
        <frame line="1" stmtstart="-1"... />
      </executionStack>
      <inputbuf>
          select * from t   </inputbuf>
    </process>
  </blocked-process>
  <blocking-process>
    <process status="sleeping" spid="51" ...>
      <executionStack />
      <inputbuf>
         begin transaction
         create table t (a int)   </inputbuf>
    </process>
  </blocking-process>
</blocked-process-report>

Tôi sẽ để lại nhiệm vụ gói nó thành một quy trình tự động như một bài tập cho người đọc. Và có, thủ tục hàng đợi / dịch vụ / kích hoạt phải được đưa vào [msdb].


Tôi chưa, nhưng tôi chắc chắn sẽ đọc nó! Tôi nên tìm loại kỳ lạ nào? Nếu nói chung là tăng hiệu suất, có lý do nào cách ly ảnh chụp nhanh không được bật theo mặc định không?
Chiến binh Bob

theo đuổi liên kết bên trong liên kết được cung cấp, đọc qua điều này và xem nó áp dụng như thế nào cho tình huống của bạn
swasheck

3
Tôi khuyên bạn nên đọc So sánh các kết quả khác nhau với RCSI & Đọc cam kết và các liên kết ở cuối. Những lo lắng đặc biệt được bảo đảm nếu bạn có UDF đa câu, ví dụ. Các bài đọc liên quan đến UDF trong READ_COMMITTED_SNAPSHOT có thể không nhất quán . Cuối cùng, bạn cần phải kiểm tra. Nhưng một lần nữa, phần lớn các trường hợp không có hiệu ứng rõ ràng.
Remus Rusanu

1
Không có hiệu ứng có thể nhìn thấy trên ứng dụng, tôi đồng ý. Trong hệ thống cơ sở dữ liệu, bạn sẽ muốn theo dõi tempdb. Có nhiều tải hơn ở đó từ read_commned_snapshot.
Cấp Fritchey

1
@AlexKuznetsov: Cách thức triển khai RCSI tuyệt vời cho thấy bản chất của nó: được triển khai bằng một thay đổi duy nhất đối với DB và nó âm thầm ánh xạ đọc cam kết để chụp nhanh cho mỗi câu lệnh. Tất cả điều này đọc cho tôi 'nỗ lực tuyệt vọng để sửa một ứng dụng bị hỏng không thể thay đổi'. OP hiện đang xem xét việc tiêu diệt các quá trình chặn mỗi N phút . Cung cấp cho RCSI một ổ đĩa thử nghiệm có vẻ khá hợp lý với tôi trong trường hợp như vậy. Tôi biết từ kinh nghiệm rằng số lượng các trường hợp mà RCSI giúp đỡkhông phá vỡ mọi thứ vượt xa các trường hợp khi xảy ra sự cố.
Remus Rusanu

5

Bạn có thể xây dựng công cụ giám sát của riêng mình hoặc tìm đến giải pháp bên thứ 3 có thể cung cấp giải pháp cho bạn. Nếu bạn quan tâm đến việc xây dựng của riêng mình, điều đó phụ thuộc vào phiên bản SQL Server mà bạn đang làm việc. Nếu là năm 2005, bạn có thể sử dụng sự kiện theo dõi Báo cáo quy trình bị chặn . Nếu bạn đang chạy 2008 trở lên, tôi khuyên bạn nên sử dụng sự kiện mở rộng tương đương, bị chặn_ process_Vport. Jonathan Kehayias có một bài viết tốt về cách sử dụng nó.

Nếu bạn đang xem các sản phẩm của bên thứ 3, SQL Monitor của phần mềm Red Gate đã chặn quy trình và cảnh báo quy trình chạy dài được tích hợp.


3

Mặc dù điều này không giải quyết làm thế nào để thông báo cho bạn về vấn đề, quy trình này sẽ chỉ cho bạn cách truy vấn để xem liệu chặn có tồn tại hay không. Nó cũng sẽ tạo các lệnh kill cho bạn, nếu bạn truyền đúng tham số.

Hy vọng điều này cung cấp cho bạn một số ý tưởng.

IF (object_id('Who') IS NOT NULL)
BEGIN
  print 'Dropping procedure: Who'
  drop procedure Who
END
print 'Creating procedure: Who'
GO
CREATE PROCEDURE Who
(
  @db varchar(100) = '%',
  @kill char(1) = 'N',
  @tran char(1) = 'N'
)
as

/*  This proc should be rewritten to take advantage of:
  select * from sys.dm_exec_connections
  select * from sys.dm_exec_sessions
  select * from sys.dm_exec_requests
*/



---------------------------------------------------------------------------------------------------
-- Date Created: July 17, 2007
-- Author:       Bill McEvoy
-- Description:  This procedure gives a summary report and a detailed report of the logged-in
--               processes.  This procedure is a derivative of sp_who3 which I wrote in 2002.
--               
---------------------------------------------------------------------------------------------------
-- Date Revised: 
-- Author:       
-- Reason:       
---------------------------------------------------------------------------------------------------
set nocount on

---------------------------------------------------------------------
-- Validate input parameters                                       --
---------------------------------------------------------------------


---------------------------------------------------------------------
-- M A I N   P R O C E S S I N G                                   --
---------------------------------------------------------------------
--                                                                 --
-- Generate login summary report                                   --
--                                                                 --
--                                                                 --
---------------------------------------------------------------------


---------------------------------------------------------------------
-- Generate login summary report                                   --
---------------------------------------------------------------------

select 'loginame'   = convert(char(30),loginame),
       'connection' = count(*),
       'phys_io'    = str(sum(physical_io),10),
--       'cpu'        = sum(cpu),
       'cpu(mm:ss)' = str((sum(cpu)/1000/60),12) + ':' + case 
                                            when left((str(((sum(cpu)/1000) % 60),2)),1) = ' ' then stuff(str(((sum(cpu)/1000) %60),2),1,1,'0') 
                                            else str(((sum(cpu)/1000) %60),2)
                                         end,
       'wait_time'  = str(sum(waittime),12),
       'Total Memory(MB)' = convert(decimal(12,2),sum(convert(float,memusage) * 8192.0 / 1024.0 / 1024.0))
from master.dbo.sysprocesses
where db_name(dbid) like @db
  and not (loginame = 'sa' and program_name = '' and db_name(dbid) = 'master')
group by loginame
order by 3 desc



---------------------------------------------------------------------
-- Generate detailed activity report                               --
---------------------------------------------------------------------

select 'loginame'     = left(loginame, 30),
       'hostname'     = left(hostname,25),
       'database'     = left(db_name(dbid),25),
       'spid'         = str(spid,4,0),
       'block'        = str(blocked,5,0),
       'phys_io'      = str(physical_io,10,0),
       'cpu(mm:ss)'   = str((cpu/1000/60),10) + ':' + case when left((str(((cpu/1000) % 60),2)),1) = ' ' then stuff(str(((cpu/1000) % 60),2),1,1,'0') else str(((cpu/1000) % 60),2) END,
       'mem(MB)'      = str((convert(float,memusage) * 8192.0 / 1024.0 / 1024.0),8,2),
       'program_name' = left(program_name,50),
       'command'      = cmd,
       'lastwaittype' = left(lastwaittype,20),
       'login_time'   = convert(char(19),login_time,120),
       'last_batch'   = convert(char(19),last_batch,120),
       'status'       = left(nt_username,20)
  from master..sysprocesses
where db_name(dbid) like @db
  and not (loginame = 'sa' and program_name = '' and db_name(dbid) = 'master')
order by 5,4


---------------------------------------------------------------------
-- Generate KILL commands                                          --
---------------------------------------------------------------------

IF (upper(@Kill) = 'Y')
BEGIN
  select 'kill' + ' ' + str(spid,4,0)
    from master..sysprocesses
  where db_name(dbid) like @db
    and not (loginame = 'sa' and program_name = '' and db_name(dbid) = 'master')
  order by spid
END



---------------------------------------------------------------------
-- Report on open transactions                                     --
---------------------------------------------------------------------

IF (UPPER(@Tran) = 'Y')
BEGIN

  -- Create the temporary table to accept the results.
  IF (object_id('tempdb..#Transactions') is NOT NULL)
    DROP TABLE #Transactions
  CREATE TABLE #Transactions
  (
    DatabaseName    varchar(30),
    TransactionName varchar(25),
    Details         sql_variant 
  )

  -- Execute the command, putting the results in the table.
  exec sp_msforeachdb '
  INSERT INTO #Transactions (TransactionName, Details)
     EXEC (''DBCC OPENTRAN([?]) WITH TABLERESULTS, NO_INFOMSGS'');
  update #Transactions 
     set DatabaseName = ''[?]''
   where DatabaseName is NULL'

  -- Display the results.
  SELECT * FROM #Transactions order by transactionname
END


go
IF (object_id('Who') IS NOT NULL)
  print 'Procedure created'
ELSE
  print 'Procedure NOT created'
GO


exec who @tran=Y

Bạn đang đưa cho anh ta cây búa trước khi để anh ta nghiên cứu tốt hơn các vấn đề ngăn chặn :-). Tôi muốn nói rằng bạn nên thay đổi điều kiện để chỉ giết các phiên MSACCESS: D.
Mary

Tôi chỉ cố gắng chỉ ra cách bắt đầu điều tra ... đó là một Proc cũ mặc dù ... có khả năng sẽ không hoạt động vào năm 2012
datagod

2

Tôi khuyên bạn nên đọc chủ đề diễn đàn MSDN sau đây . Đó là về việc khóa gây ra bởi Access to SQL Server db. Đề xuất chủ yếu là truy cập các bảng bằng các truy vấn bằng gợi ý NOLOCK, do đó sẽ không gây ra bất kỳ vấn đề khóa nào. NOLOCK không phải là giải pháp tốt nhất, vì nó có thể gây ra các vấn đề khác, nhưng sẽ giảm hầu hết các vấn đề về khóa của bạn.

Giải pháp tốt hơn là triển khai ý tưởng của Remus, thiết lập cách ly ảnh chụp nhanh trên cơ sở dữ liệu của bạn. Hoặc chỉ thực hiện mức cô lập ảnh chụp nhanh cho các kết nối nhất định mà bạn thấy gây ra chặn.

Để giám sát chính xác máy chủ của bạn để chặn các sự cố tôi đề xuất:

  • xây dựng dấu vết phía máy chủ sẽ theo dõi các sự cố chặn lâu hơn x giây (Tôi muốn nói 5 là đủ tốt);
  • lưu dấu vết trên mỗi ngày để bạn có lịch sử trong ít nhất 30 ngày qua để xem xu hướng và mẫu;
  • có một công việc hàng giờ để nghiên cứu tệp theo dõi của ngày hiện tại và gửi email cho bạn bất kỳ tình huống chặn thú vị nào;

Nếu bạn muốn có phản hồi chủ động cho vấn đề này, thay vì có một công việc mỗi giờ để theo dõi dấu vết, hãy làm cho nó chạy mỗi phút và tiêu diệt bất kỳ phiên truy cập chặn hàng đầu nào.


0

Theo câu trả lời tuyệt vời của @Remus Rusanu, tôi đã thực hiện nhiệm vụ của người đọc để kết nối sự kiện với một thủ tục được lưu trữ.

Trong trường hợp của tôi, sp sẽ ghi xml của sự kiện chặn vào một bảng, nhưng bạn có thể tự do làm bất cứ điều gì bạn muốn tại vị trí đó.

Vì vậy, hãy làm theo mã của Remus và tạo queue, một servicenotificationmột bản sao / dán đơn giản từ phía trên. Thêm các sp_configuretùy chọn và bạn về cơ bản được thiết lập.

Điều duy nhất còn lại phải làm là

  • Tạo một SP không có đối số.
  • Tạo bảng để SP ghi dữ liệu vào (ví dụ: SP của bạn có thể thay đổi)
  • Kích hoạt SP trên queue

Ngay khi bạn kích hoạt SP, các sự kiện sẽ bắt đầu chảy vào bảng của bạn.

Tôi phát hiện ra rằng hàng đợi tự hủy kích hoạt ngay lập tức, nếu SP có lỗi. Trong trường hợp đó, bạn cần vào Server Studio và kích hoạt lại nó trong menu ngữ cảnh của mục nhập hàng đợi ( [msdb]->Service Broker->Warteschlangentrong phiên bản tiếng Đức).

Tôi đã mất khá nhiều thời gian để làm việc này và tìm ra những điểm phù hợp trong tài liệu, vì vậy tôi cho rằng điều này cũng hữu ích cho những người khác. Tôi đang sử dụng SQLServer 2005.

Tạo SP không có đối số

CREATE PROCEDURE sp_addrecord
AS
  DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER;
  DECLARE @RecvReqMsg XML;
  DECLARE @RecvReqMsgName sysname;

  WHILE (1=1)
  BEGIN

    BEGIN TRANSACTION;

    WAITFOR
    ( RECEIVE TOP(1)
        @RecvReqDlgHandle = conversation_handle,
        @RecvReqMsg = message_body,
        @RecvReqMsgName = message_type_name
      FROM blocked_process_report_queue
    ), TIMEOUT 5000;

    IF (@@ROWCOUNT = 0)
    BEGIN
      ROLLBACK TRANSACTION;
      BREAK;
    END

    IF @RecvReqMsgName = 
        N'http://schemas.microsoft.com/SQL/Notifications/EventNotification'
    BEGIN
        print 'Insert SQL to pdix_lock_events'
        INSERT INTO pdix_lock_events
               VALUES(null, cast( @RecvReqMsg as xml));

    END
    ELSE IF @RecvReqMsgName =
        N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
    BEGIN
       END CONVERSATION @RecvReqDlgHandle;
    END
    ELSE IF @RecvReqMsgName =
        N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
    BEGIN
       END CONVERSATION @RecvReqDlgHandle;
    END

    COMMIT TRANSACTION;

  END
GO

Tạo pdix_lock_eventsbảng

USE [msdb]
GO

/****** Object:  Table [dbo].[pdix_lock_events]    Script Date: 05/06/2015 17:48:36 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[pdix_lock_events](
    [locktime] [timestamp] NULL,
    [lockevent] [xml] NOT NULL
) ON [PRIMARY]

GO

Kích hoạt SP trên queue

alter queue blocked_process_report_queue with activation( 
    procedure_name=sp_addrecord, 
    max_queue_readers = 1, 
    status = on, 
    execute as 'dbo');  
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.