Tôi tin rằng đây thực sự là một lỗ hổng thiết kế, mặc dù không dành riêng cho SQL Server 2016, vì tất cả các triển khai khác của các bảng tạm thời (theo như tôi biết) đều có cùng một lỗ hổng. Các vấn đề có thể phát sinh với các bảng thời gian vì điều này là khá nghiêm trọng; kịch bản trong ví dụ của bạn là nhẹ so với những gì có thể đi sai nói chung:
Tham chiếu khóa ngoại bị hỏng : Giả sử chúng ta có hai bảng tạm thời, với bảng A có tham chiếu khóa ngoại đến bảng B. Bây giờ giả sử chúng ta có hai giao dịch, cả hai đều chạy ở mức cách ly READ CAMITED: giao dịch 1 bắt đầu trước giao dịch 2, giao dịch 2 chèn một hàng vào bảng B và cam kết, sau đó giao dịch 1 chèn một hàng trong bảng A với tham chiếu đến hàng mới được thêm vào của B. Vì việc thêm hàng mới vào B đã được cam kết, ràng buộc khóa ngoại được thỏa mãn và giao dịch 1 có thể cam kết thành công. Tuy nhiên, nếu chúng ta xem cơ sở dữ liệu "NHƯ VẬY" vào khoảng thời gian giữa khi giao dịch 1 bắt đầu và khi giao dịch 2 bắt đầu, thì chúng ta sẽ thấy bảng A có tham chiếu đến một hàng B không tồn tại. Vì vậy, trong trường hợp này,bảng tạm thời cung cấp một cái nhìn không nhất quán về cơ sở dữ liệu . Tất nhiên đây không phải là mục đích của tiêu chuẩn SQL: 2011, trong đó nêu rõ,
Các hàng hệ thống lịch sử trong một bảng được phiên bản hệ thống tạo thành các ảnh chụp nhanh bất biến của quá khứ. Bất kỳ ràng buộc nào có hiệu lực khi hàng hệ thống lịch sử được tạo sẽ được kiểm tra khi hàng đó là hàng hệ thống hiện tại, do đó không bao giờ cần phải thực thi các ràng buộc trên các hàng hệ thống lịch sử.
Khóa chính không phải là duy nhất : Giả sử chúng ta có một bảng có khóa chính và hai giao dịch, cả hai đều ở mức cách ly READ CAMEDED, trong đó xảy ra các trường hợp sau: Sau khi giao dịch 1 bắt đầu nhưng trước khi chạm vào bảng này, giao dịch 2 sẽ xóa một số nhất định hàng của bảng và cam kết. Sau đó, giao dịch 1 chèn một hàng mới có cùng khóa chính với hàng đã bị xóa. Điều này diễn ra tốt đẹp, nhưng khi bạn nhìn vào bảng NHƯ một khoảng thời gian giữa khi giao dịch 1 bắt đầu và khi giao dịch 2 bắt đầu, chúng ta sẽ thấy hai hàng có cùng khóa chính.
Lỗi về các cập nhật đồng thời : Giả sử chúng ta có một bảng và hai giao dịch cả hai cập nhật cùng một hàng trong đó, một lần nữa ở mức cô lập ĐỌC. Giao dịch 1 bắt đầu trước, nhưng giao dịch 2 là lần đầu tiên cập nhật hàng. Giao dịch 2 sau đó cam kết và giao dịch 1 sau đó thực hiện cập nhật khác trên hàng và cam kết. Tất cả đều ổn, ngoại trừ nếu đây là bảng tạm thời, khi thực hiện cập nhật trong giao dịch 1 khi hệ thống chèn hàng yêu cầu vào bảng lịch sử, SysStartTime được tạo sẽ là thời gian bắt đầu giao dịch 2, trong khi SysEndTime sẽ là thời gian bắt đầu của giao dịch 1, không phải là khoảng thời gian hợp lệ vì SysEndTime sẽ ở trước SysStartTime. Trong trường hợp này, SQL Server sẽ đưa ra lỗi và khôi phục giao dịch (ví dụ: xemthảo luận này ). Điều này rất khó chịu, vì ở cấp độ cách ly READ CAMITED, người ta sẽ không mong đợi rằng các vấn đề tương tranh sẽ dẫn đến những thất bại hoàn toàn, điều đó có nghĩa là các ứng dụng không nhất thiết phải được chuẩn bị để thử lại. Cụ thể, điều này trái với "bảo đảm" trong tài liệu của Microsoft:
Hành vi này đảm bảo rằng các ứng dụng cũ của bạn sẽ tiếp tục hoạt động khi bạn bật phiên bản hệ thống trên các bảng sẽ được hưởng lợi từ phiên bản. ( liên kết )
Các triển khai khác của bảng tạm thời đã xử lý tình huống này (hai giao dịch đồng thời cập nhật cùng một hàng) bằng cách cung cấp tùy chọn tự động "điều chỉnh" dấu thời gian nếu chúng không hợp lệ (xem tại đây và tại đây ). Đây là một cách giải quyết xấu, vì nó có hậu quả đáng tiếc là phá vỡ tính nguyên tử của các giao dịch, vì các báo cáo khác trong cùng một giao dịch sẽ không được điều chỉnh dấu thời gian theo cùng một cách; tức là, với cách giải quyết này, nếu chúng ta xem cơ sở dữ liệu "NHƯ" một số điểm nhất định thì chúng ta có thể thấy các giao dịch được thực hiện một phần.
Giải pháp: Bạn đã đề xuất giải pháp rõ ràng, đó là cho việc triển khai sử dụng thời gian kết thúc giao dịch (tức là thời gian cam kết) thay vì thời gian bắt đầu. Đúng, khi chúng tôi thực hiện một tuyên bố ở giữa một giao dịch, không thể biết thời gian cam kết sẽ là bao nhiêu (vì trong tương lai, hoặc thậm chí có thể không tồn tại nếu giao dịch được thực hiện trở lại). Nhưng điều này không có nghĩa là giải pháp không thể thực hiện được; nó chỉ phải được thực hiện theo một cách khác Ví dụ: khi thực hiện câu lệnh CẬP NHẬT hoặc XÓA, khi tạo hàng lịch sử, hệ thống chỉ có thể đặt ID giao dịch hiện tại thay vì thời gian bắt đầu và sau đó ID có thể được chuyển đổi thành dấu thời gian sau đó bởi hệ thống sau khi giao dịch được thực hiện .
Trong ngữ cảnh của loại triển khai này, tôi sẽ đề xuất rằng trước khi giao dịch được thực hiện, bất kỳ hàng nào được thêm vào bảng lịch sử sẽ không hiển thị cho người dùng. Từ góc độ người dùng, đơn giản là các hàng này sẽ được thêm vào (với dấu thời gian cam kết) tại thời điểm cam kết. Đặc biệt, nếu giao dịch không bao giờ cam kết thành công thì nó sẽ không bao giờ xuất hiện trong lịch sử. Tất nhiên, điều này không phù hợp với tiêu chuẩn SQL: 2011 mô tả các phần chèn vào lịch sử (bao gồm cả dấu thời gian) xảy ra tại thời điểm các câu lệnh CẬP NHẬT và XÓA (trái ngược với thời điểm cam kết). Nhưng tôi không nghĩ rằng điều này thực sự quan trọng, xem xét rằng tiêu chuẩn chưa bao giờ được thực hiện đúng (và có thể không bao giờ được thực hiện) do các vấn đề được mô tả ở trên,
Từ quan điểm hiệu suất, có vẻ như hệ thống không mong muốn phải quay lại và xem lại các hàng lịch sử để điền vào dấu thời gian cam kết. Nhưng tùy thuộc vào cách thực hiện, chi phí có thể khá thấp. Tôi không thực sự quen thuộc với cách SQL Server hoạt động bên trong, nhưng PostgreQuery chẳng hạn sử dụng nhật ký ghi trước, điều này làm cho nó nếu nhiều bản cập nhật được thực hiện trên cùng một phần của bảng, thì các bản cập nhật đó được hợp nhất để dữ liệu chỉ cần được ghi một lần vào các trang bảng vật lý - và điều đó thường được áp dụng trong kịch bản này. Trong bất kỳ trường hợp nào,
Tất nhiên, vì (theo như tôi biết) loại hệ thống này chưa bao giờ được triển khai, tôi không thể nói chắc chắn rằng nó sẽ hoạt động - có thể tôi đang thiếu thứ gì đó - nhưng tôi không thấy lý do nào cả tại sao nó không thể làm việc
20160707 11:04:58
và bây giờ bạn cập nhật tất cả các hàng với dấu thời gian đó. Nhưng bản cập nhật này cũng chạy trong vài giây và kết thúc vào lúc này20160707 11:05:02
, dấu thời gian nào là kết thúc chính xác của giao dịch? Hoặc giả sử bạn đã sử dụngRead Uncommited
tại20160707 11:05:00
và đã trả lại hàng, nhưng sau đóAS OF
không hiển thị chúng.