Làm cách nào để tìm cơ sở dữ liệu với số lượng lớn tệp nhật ký ảo?


7

Tôi có một số cơ sở dữ liệu trên một máy chủ sản xuất có hàng trăm gigabyte và có hàng ngàn giao dịch chạy qua chúng hàng ngày.

Hầu như tất cả các cơ sở dữ liệu này đều được nhân đôi bằng SQL Server Mirroring.

Mặc dù chúng tôi đã lên kế hoạch cẩn thận các kích thước tệp nhật ký vật lý để phù hợp với hoạt động của tệp nhật ký dự kiến; đôi khi có điều gì đó không ổn và nhật ký cần phát triển vượt quá mức tối đa dự đoán của chúng tôi. Chúng tôi đã thiết lập tất cả các tệp nhật ký để tăng thêm 8192 MB, tuy nhiên khi cơ sở dữ liệu chịu áp lực phát triển tệp nhật ký, đôi khi nó sẽ chỉ phát triển nhật ký thành các phần rất nhỏ, do đó tạo ra một số trường hợp hàng trăm nghìn tệp nhật ký ảo (VLF) .

Tôi đã hiểu được tầm quan trọng của việc giữ số lượng VLF thấp khi một trong những cơ sở dữ liệu sản xuất của chúng tôi bất ngờ đi vào phục hồi với hơn 200.000 VLF. Phục hồi mất hơn 20 giờ; trong thời gian đó một phần kinh doanh của chúng tôi không thể hoạt động.

Tôi cần một giải pháp có thể giám sát số lượng tệp nhật ký ảo cho tất cả các cơ sở dữ liệu có trên máy chủ, gửi email cảnh báo nếu bất kỳ tệp nhật ký cụ thể nào có nhiều hơn một số lượng VLF nhất định.

Tôi biết DBCC LOGINFO;trả về danh sách các VLF, tuy nhiên, tôi không muốn tự chạy nó.

Tôi đã tạo câu lệnh SQL sau đây để tạo một bảng đẹp liệt kê các cơ sở dữ liệu, cùng với số lượng VLF, tuy nhiên, tôi không biết làm thế nào tôi có thể đưa nó vào công việc Tác nhân SQL để gửi email cho nhóm của chúng tôi bất cứ khi nào cơ sở dữ liệu có số "x" của các VLF.

DECLARE @cmd_per_database_prefix nvarchar(max);
DECLARE @cmd_per_database nvarchar(max);
DECLARE @database_name nvarchar(255);
SET @cmd_per_database = '';
SET @cmd_per_database_prefix = 
'
    SET NOCOUNT ON;
    DECLARE @vlf_count_table TABLE (database_name nvarchar(255), vlf_count int);
    DECLARE @params nvarchar(max);
    DECLARE @db_name nvarchar(255);
    DECLARE @vlf_count int;
    SET @params = ''@db_name nvarchar(255) OUTPUT, @vlf_count int OUTPUT'';
    DECLARE @cmdGetVLFCount nvarchar(max);
    SET @cmdGetVLFCount = 
    ''
        DECLARE @tab TABLE 
        (
            FileId int
            , FileSize nvarchar(255)
            , StartOffset nvarchar(255)
            , FSeqNo nvarchar(255)
            , Status int
            , Parity int
            , CreateLSN nvarchar(255)
        );
        DECLARE @cmd nvarchar(max);
        SET @cmd = ''''DBCC LOGINFO;'''';
        INSERT INTO @tab
        EXEC sp_executesql @cmd;
        SET @db_name = db_name();
        SET @vlf_count = (select count(*) FROM @tab t);
    '';
';
DECLARE cur CURSOR FOR
SELECT NAME 
FROM sys.databases
WHERE database_id > 4 and state=0;
OPEN cur;
FETCH NEXT FROM cur INTO @database_name;
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @cmd_per_database = @cmd_per_database + 
    '
        EXEC ' + @database_name + '.sys.sp_executesql @cmdGetVLFCount, @params, @db_name OUTPUT, @vlf_count OUTPUT;
        INSERT INTO @vlf_count_table (database_name, vlf_count) VALUES (@db_name, @vlf_count);
    ';
    FETCH NEXT FROM cur INTO @database_name;
END
SET @cmd_per_database = @cmd_per_database_prefix + @cmd_per_database + char(13) + char(10) + 'select * from @vlf_count_table t;';
EXEC sp_executesql @cmd_per_database;
CLOSE cur;
DEALLOCATE cur;

Việc cố gắng xuất dữ liệu này bằng cách sử dụng INSERT...EXECbảng để đính kèm email bằng cách sử dụng sp_send_dbmaillà vô ích.

Máy chủ SQL cuộn cảm với:

An INSERT EXEC statement cannot be nested.
Msg 8164, Level 16, State 1, Line 5

Tôi đã xem xét Simple-talk.com/sql/database-ad dùng / từ - Tôi thực sự không muốn xuất sang Excel hoặc bất cứ thứ gì tương tự. Tôi muốn có một công việc Đại lý SQL thực hiện công việc!
Max Vernon

Con trỏ hiện tại của bạn có cùng các vấn đề mà sp_msforeachdb gặp phải - bạn cần khai báo nó là LOCAL FAST_FORWARD để tránh bỏ sót cơ sở dữ liệu.
Aaron Bertrand

Câu trả lời:


10

Đây là một cách tiếp cận đơn giản hơn một chút để tránh con trỏ và thực thi lồng nhau:

SET NOCOUNT ON;

CREATE TABLE #to
(
  DBName SYSNAME,
  FileCount INT
);

DECLARE @v INT;
SELECT @v = CONVERT(INT, PARSENAME(CONVERT(VARCHAR(32), 
  SERVERPROPERTY('ProductVersion')), 4));

DECLARE @sql NVARCHAR(MAX);

SET @sql = N'CREATE TABLE #ti
  (
    ' + CASE WHEN @v >= 11 THEN 'RecoveryUnitId INT,' ELSE '' END + '    
    FileId int
    , FileSize nvarchar(255)
    , StartOffset nvarchar(255)
    , FSeqNo nvarchar(255)
    , Status int
    , Parity int
    , CreateLSN nvarchar(255)
);';

SELECT @sql = @sql + N'
  INSERT #ti EXEC ' + QUOTENAME(name) 
    + '.sys.sp_executesql N''DBCC LOGINFO WITH NO_INFOMSGS'';
  INSERT #to(DBName,FileCount) SELECT N''' + name + ''', COUNT(*) FROM #ti;
  TRUNCATE TABLE #ti;'
FROM sys.databases
WHERE database_id > 4 AND [state] = 0;

EXEC sp_executesql @sql;

SELECT DBName, FileCount FROM #to -- WHERE FileCount > [some threshold];

DROP TABLE #to;

Cảm ơn Aaron! Tôi không thể tin rằng nó sạch hơn bao nhiêu. Tôi đã cố gắng xác định cách xây dựng @sqltừ một SELECTtuyên bố - hoàn toàn nhìn ra điều hiển nhiên SELECT @sql = @sql + . Đôi khi tôi ghét dev / nền tảng thủ tục của tôi! Tôi luôn nghĩ rằng tốt hơn là sử dụng SET @sql = @sql +trong khi các SELECT @sql =kiểu cấu trúc làm tôi rùng mình vì một lý do kỳ lạ. Không còn như vậy nữa !!!
Max Vernon

2
@MaxVernon Tôi sẽ làm cho nó đơn giản hơn một dòng. Lệnh USE là không cần thiết.
Aaron Bertrand

2
Và một cảnh báo: điều này sẽ phá vỡ nếu bạn đặt tên một cơ sở dữ liệu với dấu nháy đơn, ví dụ O'Brien. Bạn có thể sửa nó nhưng nó xấu - dễ hơn là không cung cấp cho cơ sở dữ liệu tên được chọn kém. :-)
Aaron Bertrand

Nice - tuyên bố của bạn thậm chí còn tương thích ngược!
Max Vernon

0

Bạn có thể tạo một bảng tạm thời và sử dụng nó bên trong SQL động. Tôi đã không dựa trên mã của bạn nhưng điều này cho thấy khái niệm:

create table #mainTab 
(
    DatabaseName varchar(max)
    ,FileId int
    , FileSize nvarchar(255)
    , StartOffset nvarchar(255)
    , FSeqNo nvarchar(255)
    , Status int
    , Parity int
    , CreateLSN nvarchar(255)
);

exec sp_msforeachdb '
    DECLARE @tab TABLE 
    (
        FileId int
        , FileSize nvarchar(255)
        , StartOffset nvarchar(255)
        , FSeqNo nvarchar(255)
        , Status int
        , Parity int
        , CreateLSN nvarchar(255)
    );
    use ?
    insert into @tab exec(''DBCC LOGINFO'')

    insert into #mainTab
    select db_name(), * from @tab'

select DatabaseName, COUNT(*) from #mainTab group by DatabaseName

2
Hãy rất cẩn thận về sp_msforeachdb- xem ở đâyở đây .
Aaron Bertrand

Điểm tốt. Tôi sử dụng phiên bản của bạn nơi tôi làm việc và chỉ đang làm một bản demo ở đây nhưng thật tốt khi chỉ ra.
cfradenburg
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.