Phát hiện các thay đổi trong bảng SQL Server


13

Trong ứng dụng của tôi, với DB chạy trên SQL Server 2012, tôi đã có một công việc (tác vụ theo lịch trình) thực hiện định kỳ một truy vấn đắt tiền và ghi kết quả vào một bảng mà sau đó ứng dụng có thể truy vấn.

Lý tưởng nhất, tôi chỉ muốn chạy truy vấn đắt tiền đó nếu có gì đó thay đổi kể từ khi truy vấn được thực hiện lần cuối. Vì các bảng nguồn rất lớn, tôi không thể chỉ chọn một tổng kiểm tra trên tất cả các cột ứng cử viên hoặc đại loại như thế.

Tôi đã có những ý tưởng sau:

  • Viết rõ ràng dấu thời gian đã thay đổi lần cuối, cờ "phải là truy vấn" hoặc một cái gì đó như thế này vào bảng theo dõi bất cứ khi nào tôi thay đổi thứ gì đó trong bảng nguồn.
  • Sử dụng một kích hoạt để làm như vậy.

Tuy nhiên, tôi thực sự muốn biết liệu có một cách nhẹ để phát hiện các thay đổi trên bảng mà không cần tôi theo dõi rõ ràng việc ghi. Tôi có thể, ví dụ, có thể lấy "hiện tại" ROWVERSIONcủa một bảng hoặc một cái gì đó tương tự không?

Câu trả lời:


14

Không, không có gì cả. Bất kỳ loại 'theo dõi cập nhật cuối cùng nào' đều gặp phải vấn đề về hiệu suất nghiêm trọng vì tất cả các cập nhật, từ tất cả các giao dịch, sẽ cố gắng cập nhật một bản ghi theo dõi 'cập nhật lần cuối tại'. Điều này có nghĩa là chỉ có một giao dịch có thể cập nhật bảng bất cứ lúc nào và tất cả các giao dịch khác phải chờ giao dịch đầu tiên được thực hiện . Hoàn thành nối tiếp. Số lượng quản trị viên / nhà phát triển sẵn sàng đưa ra hình phạt hiệu suất như vậy chỉ vì lợi ích của việc biết khi nào bản cập nhật cuối cùng xảy ra có lẽ là ít.

Vì vậy, bạn bị mắc kẹt để xử lý nó thông qua mã tùy chỉnh. Điều đó có nghĩa là các kích hoạt vì sự thay thế (phát hiện từ các bản ghi nhật ký) là một đặc quyền chỉ dành riêng cho sao chép giao dịch (hoặc đó là bản sao thay đổi CDC ). Xin lưu ý rằng nếu bạn cố gắng theo dõi thông qua cột 'được cập nhật lần cuối tại' thì bạn sẽ phải đối mặt chính xác với vấn đề nối tiếp được đề cập ở trên. Nếu cập nhật đồng thời là quan trọng thì bạn phải sử dụng cơ chế xếp hàng (trình kích hoạt sử dụng INSERT và sau đó quy trình tổng hợp các giá trị được chèn để tạo thành 'cập nhật lần cuối tại'). Đừng cố gắng gian lận với một số giải pháp 'thông minh' như lén nhìn vào danh tính hiện tại hoặc tìm kiếm sys.dm_db_index_usage_stats . Và cũng là một cột 'update_at' trên mỗi bản ghi, như dấu thời gian của Rails có,

Có sự thay thế 'nhẹ' nào không? Trên thực tế có một, nhưng rất khó để nói liệu nó sẽ làm việc cho bạn và rất khó để làm cho đúng: Thông báo truy vấn . Thông báo truy vấn thực hiện chính xác điều đó, nó sẽ thiết lập một thông báo nếu bất kỳ dữ liệu nào có thay đổi và bạn cần làm mới truy vấn của mình. Mặc dù hầu hết các nhà phát triển chỉ quen thuộc với hóa thân .Net của nó dưới dạng SqlDependency, Thông báo truy vấn có thể được sử dụng như một cơ chế tồn tại lâu dài để phát hiện thay đổi dữ liệu. So với theo dõi thay đổi thực sự, nó sẽ rất nhẹ và ngữ nghĩa của nó gần với nhu cầu của bạn hơn (một cái gì đó, bất cứ thứ gì , đã thay đổi, vì vậy bạn cần chạy lại truy vấn).

Nhưng cuối cùng, ở vị trí của bạn, tôi thực sự sẽ xem xét lại các giả định của mình và quay trở lại bảng vẽ. Có lẽ bạn có thể sử dụng vận chuyển hoặc sao chép nhật ký để thiết lập cơ sở dữ liệu báo cáo, trên một máy chủ khác. Những gì tôi đọc được giữa các dòng là bạn đang cần một đường ống ETL thích hợp và kho dữ liệu phân tích ...


Vậy tại sao Microsoft lại bận tâm tạo sys.dm_db_index_usage_stats, nếu thông tin mà nó cung cấp không thể dựa vào?
Craig Efrein

Nó không phải là DMV được thiết kế để theo dõi thay đổi . Rất đáng tin cậy cho mục đích dự định, đó là điều chỉnh hiệu suất.
Remus Rusanu

8

Có vẻ như tôi trễ hai năm với trò chơi ở đây, nhưng thực sự có một cách khá nhẹ để làm những gì bạn yêu cầu.

Có hai cơ chế SQL Server có thể giúp bạn. Giải pháp cuối cùng của bạn có thể là sự kết hợp của cả hai.

Theo dõi thay đổi . SQL Server có khả năng đặt các bảng cụ thể theo dõi, chỉ ghi lại những hàng nào đã thay đổi (theo giá trị khóa chính của chúng) và loại thay đổi nào (Chèn, Cập nhật hoặc Xóa). Khi bạn thiết lập phát hiện thay đổi trên một tập hợp các bảng, một truy vấn nhẹ có thể cho bạn biết liệu có bất kỳ thay đổi nào được thực hiện đối với bảng kể từ lần cuối bạn kiểm tra hay không. Chi phí hoạt động gần giống như duy trì một chỉ số đơn giản bổ sung.

Rowversion / dấu thời gian . Đây là loại cột biến thiên 8 byte (có thể chuyển thành BigInt) được tăng lên, cơ sở dữ liệu rộng, bất cứ khi nào một hàng có chứa một được chèn hoặc cập nhật (nó không giúp xóa). Nếu bạn đã lập chỉ mục các cột này, bạn có thể dễ dàng biết liệu dữ liệu hàng đã thay đổi hay chưa bằng cách so sánh MAX (dấu thời gian) với giá trị của nó kể từ lần cuối cùng được đánh giá. Vì giá trị đang tăng đơn điệu, điều này sẽ cho bạn một dấu hiệu đáng tin cậy rằng dữ liệu đã thay đổi nếu giá trị mới lớn hơn lần cuối bạn kiểm tra.


7

Nếu nguồn được chèn - chỉ cung cấp cho nó một IDENTITYcột. Khi bạn thực hiện chuyển dữ liệu, bạn đăng nhập giá trị cao nhất được ghi lại. Trong lần chuyển tiếp theo, bạn chỉ cần truy vấn các giá trị lớn hơn giá trị được ghi trong lần chuyển trước đó. Chúng tôi làm điều này để chuyển các bản ghi nhật ký vào một kho dữ liệu.

Đối với các hàng có thể cập nhật, thêm một cờ "bẩn". Nó sẽ có ba giá trị - sạch, bẩn và bị xóa. Các truy vấn hàng ngày sẽ phải bỏ qua các hàng với cờ được đặt thành "đã xóa". Điều này sẽ tốn kém trong bảo trì, thử nghiệm và thời gian chạy. Sau truy vấn lớn, bạn đề cập đến tất cả các hàng được đánh dấu để xóa phải được xóa và đặt lại cờ cho tất cả các hàng khác. Điều này sẽ không quy mô tốt.

Một thay thế nhẹ hơn cho Thay đổi dữ liệu là Thay đổi theo dõi . Nó sẽ không cho bạn biết giá trị nào đã thay đổi, chỉ là hàng đã thay đổi kể từ lần truy vấn cuối cùng. Các chức năng tích hợp tạo điều kiện truy xuất các giá trị thay đổi và quản lý theo dõi. Chúng tôi đã thành công khi sử dụng CT để xử lý khoảng 100.000 thay đổi mỗi ngày trong bảng hàng 100.000.000.

Thông báo truy vấn hoạt động ở mức đòn bẩy cao hơn - ở cấp độ của tập kết quả. Về mặt khái niệm, nó giống như xác định một quan điểm. Nếu SQL Server phát hiện ra rằng bất kỳ hàng nào được trả về qua chế độ xem đó đã thay đổi, nó sẽ gửi một thông báo tới ứng dụng. Không có dấu hiệu cho thấy có bao nhiêu hàng thay đổi, hoặc cột nào. Chỉ có một tin nhắn đơn giản nói rằng "một cái gì đó đã xảy ra." Nó là tùy thuộc vào ứng dụng để tìm hiểu và phản ứng. Thực tế nó phức tạp hơn thế nhiều, như bạn có thể tưởng tượng. Có các hạn chế về cách xác định truy vấn và thông báo có thể kích hoạt các điều kiện khác với dữ liệu đã thay đổi. Khi thông báo kích hoạt nó sẽ bị xóa. Nếu hoạt động quan tâm tiếp theo xảy ra sau đó sẽ không có tin nhắn nào nữa được gửi đi.

Trong bối cảnh câu hỏi của OP, QN sẽ có lợi thế là chi phí thấp để thiết lập và chi phí thời gian chạy ít. Nó có thể là nỗ lực đáng kể để thiết lập và duy trì một chế độ phản ứng tin nhắn đăng ký nghiêm ngặt. Vì bảng dữ liệu lớn nên có thể sẽ có những thay đổi thường xuyên đối với nó, có nghĩa là thông báo có khả năng kích hoạt trong hầu hết các chu kỳ xử lý. Vì không có dấu hiệu cho thấy những gì đã thay đổi xử lý gia tăng của đồng bằng sẽ không thể thực hiện được, như với CT hoặc CDC. Chi phí hoạt động do kích hoạt sai là rất mệt mỏi, nhưng ngay cả trong trường hợp xấu nhất, truy vấn đắt tiền không cần phải chạy thường xuyên hơn hiện tại.


3

SqlTableDependency

SqlTableDependency là một thành phần triển khai cấp cao để truy cập các thông báo có chứa các giá trị bản ghi bảng trên cơ sở dữ liệu SQL Server.

SqlTableDependency là một thành phần C # chung được sử dụng để nhận thông báo khi nội dung của bảng cơ sở dữ liệu được chỉ định thay đổi.

Sự khác biệt với .NET SqlDepenency là gì?

Về cơ bản, sự khác biệt chính là SqlTableDependency gửi các sự kiện chứa các giá trị cho bản ghi được chèn, thay đổi hoặc xóa, cũng như thao tác DML (chèn / xóa / cập nhật) được thực hiện trên bảng: SqlDepenency không cho biết dữ liệu nào đã được thay đổi trên bảng cơ sở dữ liệu, họ chỉ nói rằng một cái gì đó đã thay đổi.

Hãy xem dự án GITHUB .


1

Nếu các bản cập nhật bạn mong đợi ảnh hưởng đến một chỉ mục (và chỉ khi), bạn có thể sử dụng bảng hệ thống sys.dm_db_index_usage_statsđể phát hiện bản cập nhật cuối cùng cho một chỉ mục trên bảng được đề cập. Bạn sẽ sử dụng last_user_updatelĩnh vực này.

Ví dụ: để có được các bảng cập nhật gần đây nhất:

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

Hoặc, để kiểm tra xem một bảng cụ thể đã được thay đổi kể từ một ngày cụ thể:

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'

Bạn nghĩ gì về nhận xét của Remus ở trên? "Đừng cố gắng gian lận với một số giải pháp 'thông minh' như lén nhìn vào danh tính hiện tại hoặc tìm kiếm sys.dm_db_index_usage_stats." (Xem thêm bình luận của anh ấy bên dưới câu trả lời của anh ấy.)
Fabian Schmied

1
@FabianSchmied Thú vị - Tôi đã không thấy rằng khi tôi thêm câu trả lời của mình, tôi không thể tìm thấy bất cứ điều gì có thẩm quyền ngoài câu trả lời khác của Remus để chỉ ra rằng nó không đáng tin cậy cho trường hợp sử dụng này; trang MS để dm_db_index_operational_statshiển thị các sự cố (đã xóa khi xóa bộ đệm siêu dữ liệu), nhưng không phải cho dm_db_index_usage_stats. Vấn đề duy nhất tôi tìm thấy là với việc xây dựng lại chỉ mục, khởi động lại máy chủ và tách cơ sở dữ liệu để xóa các số liệu thống kê sử dụng và có vẻ như điều đó không được áp dụng ở đây. Sẽ được quan tâm để xem thông tin chứng minh về điều này.
Geoff
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.