Câu trả lời ngắn
Dường như dữ liệu trong msdb.dbo.sysjobschedules
được cập nhật bởi một luồng nền trong Tác nhân SQL, được xác định là SQLAgent - Schedule Saver
, cứ sau 20 phút (hoặc ít thường xuyên hơn, nếu xp_sqlagent_notify
không được gọi và không có công việc nào chạy trong thời gian đó).
Để biết thông tin chính xác hơn, nhìn next_scheduled_run_date
vào msdb.dbo.sysjobactivity
. Điều này được cập nhật trong thời gian thực bất cứ khi nào một công việc được thay đổi hoặc một công việc đã chạy. Là một phần thưởng bổ sung, sysjobactivity
lưu trữ dữ liệu đúng cách (dưới dạng cột thời gian), giúp công việc trở nên dễ dàng hơn rất nhiều so với các INT ngu ngốc đó.
Đó là câu trả lời ngắn gọn:
Nó có thể lên đến 20 phút trước khi sysjobschedules phản ánh sự thật; tuy nhiên, sysjobactivity sẽ luôn được cập nhật. Nếu bạn muốn biết thêm chi tiết về điều này, hoặc cách tôi tìm ra nó ...
Câu trả lời dài
Nếu bạn quan tâm theo dõi con thỏ một lúc, khi bạn gọi sp_add_jobschedule
, chuỗi sự kiện này được thiết lập thành chuyển động:
msdb.dbo.sp_add_jobschedule == calls ==> msdb.dbo.sp_add_schedule
msdb.dbo.sp_attach_schedule
msdb.dbo.sp_attach_schedule == calls ==> msdb.dbo.sp_sqlagent_notify
msdb.dbo.sp_sqlagent_notify == calls ==> msdb.dbo.xp_sqlagent_notify
Bây giờ, chúng ta không thể đuổi theo con thỏ nữa, vì chúng ta không thể nhìn trộm những gì xp_sqlagent_notify
nó làm. Nhưng tôi nghĩ rằng chúng ta có thể đoán rằng quy trình mở rộng này tương tác với dịch vụ Đại lý và nói với nó rằng đã có một sự thay đổi đối với công việc và lịch trình cụ thể này. Bằng cách chạy theo dõi phía máy chủ, chúng ta có thể thấy rằng, ngay lập tức, SQL động sau đây được gọi bởi Tác nhân SQL:
exec sp_executesql N'DECLARE @nextScheduledRunDate DATETIME
SET @nextScheduledRunDate = msdb.dbo.agent_datetime(@P1, @P2)
UPDATE msdb.dbo.sysjobactivity
SET next_scheduled_run_date = @nextScheduledRunDate
WHERE session_id = @P3 AND job_id = @P4',
N'@P1 int,@P2 int,@P3 int,@P4 uniqueidentifier',
20120819,181600,5,'36924B24-9706-4FD7-8B3A-1F9F0BECB52C'
Có vẻ như nó sysjobactivity
được cập nhật ngay lập tức, và sysjobschedules
chỉ được cập nhật theo lịch trình. Nếu chúng tôi thay đổi lịch trình mới mỗi ngày một lần, vd
@freq_type=4,
@freq_interval=1,
@freq_subday_type=1,
@freq_subday_interval=0,
@freq_relative_interval=0,
@freq_recurrence_factor=1,
Chúng tôi vẫn thấy cập nhật ngay lập tức sysjobactivity
như trên, và sau đó cập nhật khác sau khi công việc kết thúc. Các bản cập nhật khác nhau đến từ nền và các luồng khác trong SQL Agent, ví dụ:
SQLAgent - Job Manager
SQLAgent - Update job activity
SQLAgent - Job invocation engine
SQLAgent - Schedule Saver
Một luồng nền (luồng "Trình tiết kiệm lịch biểu") cuối cùng cũng xuất hiện và cập nhật sysjobschedules
; từ cuộc điều tra ban đầu của tôi, có vẻ như cứ sau 20 phút, và chỉ xảy ra nếu xp_sqlagent_notify
được gọi do thay đổi công việc kể từ lần cuối cùng chạy (Tôi không thực hiện bất kỳ thử nghiệm nào nữa để xem điều gì xảy ra nếu một công việc đã xảy ra đã thay đổi và một cái khác đã được chạy, nếu luồng "Lịch trình tiết kiệm" cập nhật cả hai - tôi nghi ngờ nó phải, nhưng sẽ để nó như một bài tập cho người đọc).
Tôi không chắc liệu chu kỳ 20 phút có được bù từ khi SQL Agent bắt đầu hay từ nửa đêm hoặc từ một thứ gì đó dành riêng cho máy không. Trên hai phiên bản khác nhau trên cùng một máy chủ vật lý, luồng "Lịch trình tiết kiệm" được cập nhật sysjobschedules
, trên cả hai trường hợp, gần như cùng một lúc - 18:31:37 & 18:51:37 trên một, và 18:31:39 & 18:51:39 mặt khác. Tôi đã không khởi động SQL Server Agent cùng một lúc trên các máy chủ này, nhưng có khả năng từ xa là thời gian bắt đầu xảy ra là bù 20 phút. Tôi nghi ngờ điều đó, nhưng tôi không có thời gian để xác nhận bằng cách khởi động lại Đại lý trên một trong số họ và chờ đợi thêm thông tin cập nhật.
Tôi biết ai đã làm điều đó và khi nó xảy ra, bởi vì tôi đã đặt một bộ kích hoạt ở đó và bắt nó, trong trường hợp tôi không thể tìm thấy nó trong dấu vết, hoặc tôi vô tình lọc nó ra.
CREATE TABLE dbo.JobAudit
(
[action] CHAR(1),
[table] CHAR(1),
hostname SYSNAME NOT NULL DEFAULT HOST_NAME(),
appname SYSNAME NOT NULL DEFAULT PROGRAM_NAME(),
dt DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);
CREATE TRIGGER dbo.schedule1 ON dbo.sysjobactivity FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'A';
GO
CREATE TRIGGER dbo.schedule2 ON dbo.sysjobactivity FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'A';
GO
CREATE TRIGGER dbo.schedule3 ON dbo.sysjobschedules FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'S';
GO
CREATE TRIGGER dbo.schedule4 ON dbo.sysjobschedules FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'S';
GO
Điều đó nói rằng, không khó để bắt gặp với một dấu vết tiêu chuẩn, điều này thậm chí còn xuất hiện dưới dạng DML không động:
UPDATE msdb.dbo.sysjobschedules
SET next_run_date = 20120817,
next_run_time = 20000
WHERE (job_id = 0xB87B329BFBF7BA40B30D9B27E0B120DE
and schedule_id = 8)
Nếu bạn muốn chạy một dấu vết được lọc nhiều hơn để theo dõi hành vi này theo thời gian (ví dụ: vẫn tồn tại thông qua khởi động lại Tác nhân SQL thay vì theo yêu cầu), bạn có thể chạy một dấu vết có appname = 'SQLAgent - Schedule Saver'
...
Vì vậy, tôi nghĩ rằng nếu bạn muốn biết thời gian chạy tiếp theo ngay lập tức, hãy nhìn vào sysjobactivity
, không sysjobschedules
. Bảng này được Đại lý hoặc chủ đề nền của nó cập nhật trực tiếp ("Cập nhật hoạt động công việc", "Trình quản lý công việc" và "Công cụ gọi công việc") khi hoạt động xảy ra hoặc như được thông báo bởi xp_sqlagent_notify
.
Mặc dù vậy, hãy lưu ý rằng rất dễ dàng để lấy một trong hai bảng - vì không có sự bảo vệ nào chống lại việc xóa dữ liệu khỏi các bảng này. (Vì vậy, nếu bạn quyết định dọn dẹp, chẳng hạn, bạn có thể dễ dàng xóa tất cả các hàng cho công việc đó khỏi bảng hoạt động.) Trong trường hợp này tôi không chắc chắn chính xác cách SQL Server Agent nhận hoặc lưu ngày chạy tiếp theo. Có lẽ xứng đáng để điều tra nhiều hơn vào một ngày sau đó khi tôi có thời gian rảnh ...