Thứ ba T-SQL của tháng này đang được tổ chức bởi Mike Fal (blog | twitter) và chủ đề là Trick Shots, nơi chúng tôi được mời nói với cộng đồng về một số giải pháp mà chúng tôi đã sử dụng trong SQL Server mà chúng tôi cảm thấy, ít nhất là đối với chúng tôi, như một loại "cú đánh lừa" - một cái gì đó tương tự như sử dụng massé, "tiếng Anh" hoặc các cú đánh ngân hàng phức tạp trong bida hoặc bi da. Sau khi làm việc với SQL Server khoảng 15 năm, tôi đã có dịp nghĩ ra các thủ thuật để giải quyết một số vấn đề khá thú vị, nhưng một thủ thuật có vẻ khá dễ sử dụng lại, dễ dàng thích ứng với nhiều tình huống và thực hiện đơn giản, là thứ mà tôi gọi là "schema switch-a-roo".
Giả sử bạn có một tình huống trong đó bạn có một bảng tra cứu lớn cần được làm mới định kỳ. Bảng tra cứu này cần thiết trên nhiều máy chủ và có thể chứa dữ liệu được điền từ nguồn bên ngoài hoặc bên thứ ba, ví dụ:Dữ liệu IP hoặc miền hoặc có thể đại diện cho dữ liệu từ bên trong môi trường của riêng bạn.
Một vài tình huống đầu tiên mà tôi cần một giải pháp cho việc này là tạo siêu dữ liệu và dữ liệu không chuẩn hóa sẵn có cho "bộ nhớ đệm dữ liệu" chỉ đọc - thực sự chỉ là các phiên bản SQL Server MSDE (và sau đó là Express) được cài đặt trên các máy chủ web khác nhau, vì vậy các máy chủ web đã kéo dữ liệu được lưu vào bộ nhớ cache cục bộ này thay vì làm phiền hệ thống OLTP chính. Điều này có vẻ thừa, nhưng hoạt động đọc không tải khỏi hệ thống OLTP chính và có thể loại bỏ hoàn toàn kết nối mạng, đã dẫn đến một sự thay đổi thực sự về hiệu suất toàn diện và đáng chú ý nhất là đối với người dùng cuối .
Các máy chủ này không cần các bản sao dữ liệu cập nhật từng phút; trên thực tế, rất nhiều bảng cache chỉ được cập nhật hàng ngày. Nhưng vì hệ thống có kích thước 24 × 7 và một số cập nhật này có thể mất vài phút, chúng thường cản trở khách hàng thực làm những việc thực sự trên hệ thống.
(Các) Phương pháp Tiếp cận Ban đầu
Lúc đầu, mã khá đơn giản:chúng tôi xóa các hàng đã bị xóa khỏi nguồn, cập nhật tất cả các hàng mà chúng tôi có thể biết đã thay đổi và chèn tất cả các hàng mới. Nó trông giống như thế này (xử lý lỗi, v.v. bị xóa cho ngắn gọn):
BEGIN TRANSACTION; DELETE dbo.Lookup WHERE [key] NOT IN (SELECT [key] FROM [source]); UPDATE d SET [col] = s.[col] FROM dbo.Lookup AS d INNER JOIN [source] AS s ON d.[key] = s.[key] -- AND [condition to detect change]; INSERT dbo.Lookup([cols]) SELECT [cols] FROM [source] WHERE [key] NOT IN (SELECT [key] FROM dbo.Lookup); COMMIT TRANSACTION;
Không cần phải nói giao dịch này có thể gây ra một số vấn đề về hiệu suất thực khi hệ thống đang được sử dụng. Chắc chắn là có những cách khác để làm điều này, nhưng mọi phương pháp chúng tôi đã thử đều chậm và tốn kém như nhau. Làm thế nào chậm và tốn kém? "Để tôi đếm số lần quét ..."
Vì MERGE có từ trước này và chúng tôi đã loại bỏ các phương pháp tiếp cận "bên ngoài" như DTS, qua một số thử nghiệm, chúng tôi xác định rằng sẽ hiệu quả hơn nếu chỉ xóa bảng và điền lại nó, thay vì thử và đồng bộ hóa với nguồn :
BEGIN TRANSACTION; TRUNCATE TABLE dbo.Lookup; INSERT dbo.Lookup([cols]) SELECT [cols] FROM [source]; COMMIT TRANSACTION;
Bây giờ, như tôi đã giải thích, truy vấn này từ [nguồn] có thể mất vài phút, đặc biệt nếu tất cả các máy chủ web đang được cập nhật song song (chúng tôi đã cố gắng trì hoãn ở nơi có thể). Và nếu khách hàng đang ở trên trang web và cố gắng chạy một truy vấn liên quan đến bảng tra cứu, họ phải đợi giao dịch đó kết thúc. Trong hầu hết các trường hợp, nếu họ đang chạy truy vấn này vào lúc nửa đêm, sẽ không thực sự quan trọng nếu họ nhận được bản sao dữ liệu tra cứu của ngày hôm qua hay của ngày hôm nay; vì vậy, việc bắt họ chờ làm mới có vẻ ngớ ngẩn và thực sự đã dẫn đến một số cuộc gọi hỗ trợ.
Vì vậy, mặc dù điều này tốt hơn, nhưng nó chắc chắn vẫn chưa hoàn hảo.
Giải pháp ban đầu của tôi:sp_rename
Giải pháp ban đầu của tôi, hồi SQL Server 2000 còn mát mẻ, là tạo một bảng "bóng":
CREATE TABLE dbo.Lookup_Shadow([cols]);
Bằng cách này, tôi có thể điền bảng bóng mà không làm gián đoạn người dùng và sau đó thực hiện đổi tên theo ba cách - một thao tác nhanh, chỉ dành cho siêu dữ liệu - chỉ sau khi toàn bộ tập hợp. Một cái gì đó như thế này (một lần nữa, được đơn giản hóa hoàn toàn):
TRUNCATE TABLE dbo.Lookup_Shadow; INSERT dbo.Lookup_Shadow([cols]) SELECT [cols] FROM [source]; BEGIN TRANSACTION; EXEC sp_rename N'dbo.Lookup', N'dbo.Lookup_Fake'; EXEC sp_rename N'dbo.Lookup_Shadow', N'dbo.Lookup'; COMMIT TRANSACTION; -- if successful: EXEC sp_rename N'dbo.Lookup_Fake', N'dbo.Lookup_Shadow';
Nhược điểm của cách tiếp cận ban đầu này là sp_rename có một thông báo đầu ra không thể ngăn chặn cảnh báo bạn về sự nguy hiểm của việc đổi tên các đối tượng. Trong trường hợp của chúng tôi, chúng tôi đã thực hiện tác vụ này thông qua các công việc của SQL Server Agent và chúng tôi đã xử lý rất nhiều siêu dữ liệu và các bảng bộ đệm khác, vì vậy lịch sử công việc chứa đầy tất cả các thông báo vô ích này và thực sự gây ra các lỗi thực sự bị cắt bớt khỏi chi tiết lịch sử. (Tôi đã phàn nàn về điều này vào năm 2007, nhưng cuối cùng đề xuất của tôi đã bị bác bỏ và đóng lại là "Sẽ không khắc phục.")
Giải pháp tốt hơn:Lược đồ
Sau khi chúng tôi nâng cấp lên SQL Server 2005, tôi đã phát hiện ra lệnh tuyệt vời này có tên là CREATE SCHEMA. Việc triển khai cùng một loại giải pháp bằng cách sử dụng các lược đồ thay vì đổi tên bảng là điều tầm thường và giờ đây, lịch sử Tác nhân sẽ không bị ô nhiễm với tất cả các thông báo vô ích này. Về cơ bản, tôi đã tạo hai lược đồ mới:
CREATE SCHEMA fake AUTHORIZATION dbo; CREATE SCHEMA shadow AUTHORIZATION dbo;
Sau đó, tôi di chuyển bảng Lookup_Shadow vào giản đồ bộ nhớ cache và đổi tên nó:
ALTER SCHEMA shadow TRANSFER dbo.Lookup_Shadow; EXEC sp_rename N'shadow.Lookup_Shadow', N'Lookup';
(Nếu bạn chỉ đang triển khai giải pháp này, bạn sẽ tạo một bản sao mới của bảng trong lược đồ, không phải di chuyển bảng hiện có đến đó và đổi tên nó.)
Với hai lược đồ đó và một bản sao của bảng Tra cứu trong lược đồ bóng, việc đổi tên ba chiều của tôi đã trở thành một chuyển đổi giản đồ ba chiều:
TRUNCATE TABLE shadow.Lookup; INSERT shadow.Lookup([cols]) SELECT [cols] FROM [source]; -- perhaps an explicit statistics update here BEGIN TRANSACTION; ALTER SCHEMA fake TRANSFER dbo.Lookup; ALTER SCHEMA dbo TRANSFER shadow.Lookup; COMMIT TRANSACTION; ALTER SCHEMA shadow TRANSFER fake.Lookup;
Tại thời điểm này, tất nhiên bạn có thể làm trống bản sao bóng của bảng, tuy nhiên trong một số trường hợp, tôi thấy hữu ích khi để lại bản sao "cũ" của dữ liệu cho các mục đích khắc phục sự cố:
TRUNCATE TABLE shadow.Lookup;
Bất kỳ điều gì khác mà bạn làm với bản sao bóng, bạn sẽ muốn đảm bảo rằng bạn thực hiện bên ngoài giao dịch - hai hoạt động chuyển giao phải ngắn gọn và nhanh chóng nhất có thể.
Một số lưu ý
- Phím Ngoại
Điều này sẽ không hoạt động nếu bảng tra cứu được tham chiếu bởi các khóa ngoại. Trong trường hợp của chúng tôi, chúng tôi không chỉ ra bất kỳ ràng buộc nào tại các bảng bộ nhớ cache này, nhưng nếu bạn làm vậy, bạn có thể phải gắn bó với các phương thức xâm nhập như MERGE. Hoặc sử dụng các phương pháp chỉ bổ sung và tắt hoặc bỏ khóa ngoại trước khi thực hiện bất kỳ sửa đổi dữ liệu nào (sau đó tạo lại hoặc kích hoạt lại chúng sau đó). Nếu bạn gắn bó với các kỹ thuật MERGE / UPSERT và bạn đang thực hiện việc này giữa các máy chủ hoặc tệ hơn là từ một hệ thống từ xa, tôi thực sự khuyên bạn nên lấy dữ liệu thô cục bộ thay vì cố gắng sử dụng các phương pháp này giữa các máy chủ.
- Thống kê
Chuyển đổi bảng (sử dụng đổi tên hoặc chuyển lược đồ) sẽ dẫn đến thống kê lật qua lại giữa hai bản sao của bảng và đây rõ ràng có thể là một vấn đề đối với các kế hoạch. Vì vậy, bạn có thể cân nhắc thêm các bản cập nhật thống kê rõ ràng như một phần của quá trình này.
- Các phương pháp tiếp cận khác Tất nhiên, có nhiều cách khác để làm điều này mà tôi chỉ đơn giản là chưa có dịp thử. Chuyển đổi phân vùng và sử dụng chế độ xem + từ đồng nghĩa là hai cách tiếp cận mà tôi có thể điều tra trong tương lai để xử lý chủ đề kỹ lưỡng hơn. Tôi muốn nghe kinh nghiệm của bạn và cách bạn giải quyết vấn đề này trong môi trường của bạn. Và có, tôi nhận thấy rằng vấn đề này phần lớn được giải quyết bởi Nhóm khả dụng và phụ lục có thể đọc được trong SQL Server 2012, nhưng tôi coi đó là một "cú đánh lừa" nếu bạn có thể giải quyết vấn đề mà không ném giấy phép cao cấp vào vấn đề hoặc sao chép một toàn bộ cơ sở dữ liệu để tạo ra một vài bảng dư thừa. :-)
Kết luận
Nếu bạn có thể sống với những hạn chế ở đây, thì cách tiếp cận này có thể mang lại hiệu quả tốt hơn so với trường hợp bạn thực hiện một bảng ngoại tuyến bằng cách sử dụng SSIS hoặc quy trình MERGE / UPSERT của riêng bạn, nhưng hãy đảm bảo kiểm tra cả hai kỹ thuật. Điểm quan trọng nhất là người dùng cuối truy cập vào bảng phải có trải nghiệm giống hệt nhau, bất kỳ lúc nào trong ngày, ngay cả khi họ chạm vào bảng ở giữa bản cập nhật định kỳ của bạn.