Tôi có thể dựa vào việc đọc các giá trị Nhận dạng Máy chủ SQL theo thứ tự không?


24

TL; DR: Câu hỏi dưới đây rút ra: Khi chèn một hàng, có một cửa sổ cơ hội giữa việc tạo ra một Identitygiá trị mới và khóa của khóa hàng tương ứng trong chỉ mục được nhóm, trong đó người quan sát bên ngoài có thể thấy một cái mới hơn Identity giá trị được chèn bởi một giao dịch đồng thời? (Trong Máy chủ SQL.)

Phiên bản chi tiết

Tôi có một bảng SQL Server với một Identitycột được gọi CheckpointSequence, đó là khóa của chỉ mục được nhóm của bảng (cũng có một số chỉ mục không bao gồm bổ sung). Các hàng được chèn vào bảng bởi một số quy trình và luồng đồng thời (ở mức cô lập READ COMMITTEDvà không có IDENTITY_INSERT). Đồng thời, có các quy trình đọc định kỳ các hàng từ chỉ mục được nhóm, được sắp xếp theo CheckpointSequencecột đó (cũng ở mức cô lập READ COMMITTED, với READ COMMITTED SNAPSHOTtùy chọn bị tắt).

Tôi hiện đang dựa vào thực tế là các quá trình đọc không bao giờ có thể "bỏ qua" một điểm kiểm tra. Câu hỏi của tôi là: Tôi có thể dựa vào tài sản này? Và nếu không, tôi có thể làm gì để biến nó thành sự thật?

Ví dụ: Khi các hàng có giá trị nhận dạng 1, 2, 3, 4 và 5 được chèn, người đọc không được nhìn thấy hàng có giá trị 5 trước khi nhìn thấy hàng có giá trị 4. Các thử nghiệm cho thấy truy vấn có chứa ORDER BY CheckpointSequencemệnh đề ( và một WHERE CheckpointSequence > -1mệnh đề), đáng tin cậy chặn bất cứ khi nào hàng 4 được đọc, nhưng chưa được cam kết, ngay cả khi hàng 5 đã được cam kết.

Tôi tin rằng ít nhất trên lý thuyết, có thể có một điều kiện chủng tộc ở đây có thể khiến giả định này bị phá vỡ. Thật không may, tài liệu về Identitykhông nói nhiều về cách Identityhoạt động trong bối cảnh của nhiều giao dịch đồng thời, nó chỉ nói "Mỗi giá trị mới được tạo dựa trên hạt giống & gia tăng hiện tại." và "Mỗi giá trị mới cho một giao dịch cụ thể khác với các giao dịch đồng thời khác trên bảng." ( MSDN )

Lý do của tôi là, nó phải hoạt động bằng cách nào đó như thế này:

  1. Một giao dịch được bắt đầu (rõ ràng hoặc ngầm).
  2. Một giá trị nhận dạng (X) được tạo ra.
  3. Khóa hàng tương ứng được lấy trên chỉ mục được phân cụm dựa trên giá trị nhận dạng (trừ khi khóa leo thang khóa, trong trường hợp đó toàn bộ bảng bị khóa).
  4. Hàng được chèn.
  5. Giao dịch được cam kết (có thể khá nhiều thời gian sau đó), vì vậy khóa được gỡ bỏ một lần nữa.

Tôi nghĩ rằng giữa bước 2 và 3, có một cửa sổ rất nhỏ trong đó

  • một phiên đồng thời có thể tạo ra giá trị nhận dạng tiếp theo (X + 1) và thực hiện tất cả các bước còn lại,
  • do đó cho phép người đọc đến chính xác tại thời điểm đó để đọc giá trị X + 1, thiếu giá trị của X.

Tất nhiên, xác suất của điều này có vẻ cực kỳ thấp; nhưng vẫn có thể xảy ra Hay nó có thể?

(Nếu bạn quan tâm đến bối cảnh: Đây là việc triển khai Công cụ bền bỉ SQL của NEventStore . NEventStore triển khai một cửa hàng sự kiện chỉ có phụ lục trong đó mọi sự kiện đều có số thứ tự điểm kiểm tra tăng dần mới. Khách hàng đọc các sự kiện từ cửa hàng sự kiện được sắp xếp theo điểm kiểm tra. Để thực hiện các tính toán của tất cả các loại. Một khi một sự kiện với điểm kiểm tra X đã được xử lý, khách hàng chỉ xem xét các sự kiện "mới hơn", tức là các sự kiện có điểm kiểm tra X + 1 trở lên. Do đó, điều quan trọng là các sự kiện không bao giờ được bỏ qua, vì chúng sẽ không bao giờ được xem xét lại. Hiện tại tôi đang cố gắng xác định xem việc Identitytriển khai điểm kiểm tra dựa trên cơ sở có đáp ứng yêu cầu này không. Đây là các câu lệnh SQL chính xác được sử dụng : Schema , Truy vấn của nhà văn ,Truy vấn của độc giả .)

Nếu tôi đúng và tình huống được mô tả ở trên có thể phát sinh, tôi chỉ có thể thấy hai tùy chọn xử lý chúng, cả hai đều không thỏa đáng:

  • Khi thấy giá trị chuỗi điểm kiểm tra X + 1 trước khi thấy X, hãy bỏ X + 1 và thử lại sau. Tuy nhiên, vì Identitytất nhiên có thể tạo ra các khoảng trống (ví dụ: khi giao dịch được khôi phục), X có thể không bao giờ đến.
  • Vì vậy, cùng một cách tiếp cận, nhưng chấp nhận khoảng cách sau n mili giây. Tuy nhiên, giá trị nào của n tôi nên giả sử?

Còn ý tưởng nào hay hơn không?


Bạn đã thử sử dụng Sequence thay vì danh tính? Với danh tính, tôi không nghĩ bạn có thể dự đoán một cách đáng tin cậy phần chèn nào sẽ nhận được giá trị nhận dạng cụ thể nhưng đây không phải là vấn đề khi sử dụng chuỗi. Tất nhiên điều đó thay đổi cách bạn làm mọi thứ bây giờ mặc dù.
Antoine Hernandez

@SoleDBAGuy Sẽ không có Trình tự làm cho điều kiện cuộc đua mà tôi đã mô tả ở trên thậm chí còn có khả năng hơn? Tôi tạo một giá trị Sequence X mới (thay thế bước 2 ở trên), sau đó chèn một hàng (bước 3 và 4). Từ 2 đến 3, có khả năng người khác có thể tạo giá trị Chuỗi tiếp theo X + 1, và người đọc đọc giá trị X + 1 đó trước khi tôi thậm chí có thể chèn hàng của mình với giá trị Chuỗi X.
Fabian Schmied

Câu trả lời:


26

Khi chèn một hàng, có một cửa sổ cơ hội giữa việc tạo ra một giá trị Nhận dạng mới và khóa của khóa hàng tương ứng trong chỉ mục được nhóm, trong đó người quan sát bên ngoài có thể thấy giá trị Nhận dạng mới hơn được chèn bởi giao dịch đồng thời?

Vâng.

Việc phân bổ các giá trị nhận dạng độc lập với giao dịch người dùng có chứa . Đây là một lý do mà giá trị nhận dạng được tiêu thụ ngay cả khi giao dịch được khôi phục. Hoạt động gia tăng tự nó được bảo vệ bởi một chốt để ngăn ngừa tham nhũng, nhưng đó là phạm vi của các biện pháp bảo vệ.

Trong các trường hợp cụ thể của việc triển khai của bạn, việc phân bổ danh tính (một cuộc gọi đến CMEDSeqGen::GenerateNewValue) được thực hiện trước khi giao dịch người dùng cho phần chèn thậm chí được kích hoạt (và trước khi thực hiện bất kỳ khóa nào).

Bằng cách chạy đồng thời hai phần chèn với trình gỡ lỗi được đính kèm để cho phép tôi đóng băng một luồng ngay sau khi giá trị nhận dạng được tăng và phân bổ, tôi có thể tạo lại một kịch bản trong đó:

  1. Phần 1 có được giá trị nhận dạng (3)
  2. Phần 2 có được giá trị nhận dạng (4)
  3. Phần 2 thực hiện thao tác chèn và cam kết (vì vậy hàng 4 hoàn toàn hiển thị)
  4. Phần 1 thực hiện thao tác chèn và cam kết (hàng 3)

Sau bước 3, một truy vấn sử dụng row_number trong khóa đã đọc đã cam kết trả về như sau:

Ảnh chụp màn hình

Trong quá trình triển khai của bạn, điều này sẽ dẫn đến việc Checkpoint ID 3 bị bỏ qua không chính xác.

Cửa sổ của misopportunity là tương đối nhỏ, nhưng nó tồn tại. Để đưa ra một kịch bản thực tế hơn là có một trình gỡ lỗi được đính kèm: Một luồng truy vấn thực thi có thể mang lại lịch trình sau bước 1 ở trên. Điều này cho phép một luồng thứ hai phân bổ một giá trị nhận dạng, chèn và cam kết, trước khi luồng gốc tiếp tục thực hiện thao tác chèn của nó.

Để rõ ràng, không có khóa hoặc các đối tượng đồng bộ hóa khác bảo vệ giá trị nhận dạng sau khi được phân bổ và trước khi nó được sử dụng. Ví dụ: sau bước 1 ở trên, một giao dịch đồng thời có thể thấy giá trị nhận dạng mới bằng cách sử dụng các hàm T-SQL như IDENT_CURRENTtrước khi hàng tồn tại trong bảng (thậm chí không được cam kết).

Về cơ bản, không có nhiều đảm bảo xung quanh các giá trị nhận dạng hơn tài liệu :

  • Mỗi giá trị mới được tạo dựa trên hạt giống & gia tăng hiện tại.
  • Mỗi giá trị mới cho một giao dịch cụ thể khác với các giao dịch đồng thời khác trên bảng.

Đó thực sự là nó.

Nếu việc xử lý FIFO giao dịch nghiêm ngặt là bắt buộc, bạn có thể không có lựa chọn nào khác ngoài việc tuần tự hóa thủ công. Nếu ứng dụng có ít yêu cầu hơn, bạn có nhiều lựa chọn hơn. Câu hỏi không rõ ràng 100% về vấn đề đó. Tuy nhiên, bạn có thể tìm thấy một số thông tin hữu ích trong bài viết của Remus Rusanu Sử dụng Bảng làm Hàng đợi .


7

Như Paul White đã trả lời hoàn toàn chính xác, có khả năng các hàng danh tính tạm thời bị "bỏ qua". Đây chỉ là một đoạn mã nhỏ để tái tạo trường hợp này cho riêng bạn.

Tạo một cơ sở dữ liệu và một testtable:

create database IdentityTest
go
use IdentityTest
go
create table dbo.IdentityTest (ID int identity, c1 char(10))
create clustered index CI_dbo_IdentityTest_ID on dbo.IdentityTest(ID)

Thực hiện chèn đồng thời và chọn trên bảng này trong chương trình bảng điều khiển C #:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading;

namespace IdentityTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var insertThreads = new List<Thread>();
            var selectThreads = new List<Thread>();

            //start threads for infinite inserts
            for (var i = 0; i < 100; i++)
            {
                insertThreads.Add(new Thread(InfiniteInsert));
                insertThreads[i].Start();
            }

            //start threads for infinite selects
            for (var i = 0; i < 10; i++)
            {
                selectThreads.Add(new Thread(InfiniteSelectAndCheck));
                selectThreads[i].Start();
            }
        }

        private static void InfiniteSelectAndCheck()
        {
            //infinite loop
            while (true)
            {
                //read top 2 IDs
                var cmd = new SqlCommand("select top(2) ID from dbo.IdentityTest order by ID desc")
                {
                    Connection = new SqlConnection("Server=localhost;Database=IdentityTest;Integrated Security=SSPI;Application Name=IdentityTest")
                };

                try
                {
                    cmd.Connection.Open();
                    var dr = cmd.ExecuteReader();

                    //read first row
                    dr.Read();
                    var row1 = int.Parse(dr["ID"].ToString());

                    //read second row
                    dr.Read();
                    var row2 = int.Parse(dr["ID"].ToString());

                    //write line if row1 and row are not consecutive
                    if (row1 - 1 != row2)
                    {
                        Console.WriteLine("row1=" + row1 + ", row2=" + row2);
                    }
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }
        }

        private static void InfiniteInsert()
        {
            //infinite loop
            while (true)
            {
                var cmd = new SqlCommand("insert into dbo.IdentityTest (c1) values('a')")
                {
                    Connection = new SqlConnection("Server=localhost;Database=IdentityTest;Integrated Security=SSPI;Application Name=IdentityTest")
                };

                try
                {
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }
        }
    }
}

Bảng điều khiển này in một dòng cho mọi trường hợp khi một trong các luồng đọc "bỏ lỡ" một mục.


1
Mã đẹp nhưng bạn chỉ kiểm tra các id liên tiếp ( "// ghi dòng nếu hàng1 và hàng không liên tiếp" ). Có thể có những khoảng trống được tạo ra mà mã của bạn sẽ in. Điều đó không có nghĩa là những khoảng trống này sẽ được lấp đầy sau đó.
ypercubeᵀᴹ

1
Vì mã không kích hoạt một kịch bản IDENTITYtạo ra các khoảng trống (chẳng hạn như khôi phục giao dịch), các dòng in thực sự hiển thị các giá trị "bị bỏ qua" (hoặc ít nhất là chúng đã làm khi tôi chạy và kiểm tra nó trên máy của tôi). Mẫu repro rất đẹp!
Fabian Schmied

5

Tốt nhất là không mong đợi các danh tính là liên tiếp vì có nhiều kịch bản có thể để lại những khoảng trống. Tốt hơn là xem xét danh tính như một con số trừu tượng và không gắn bất kỳ ý nghĩa kinh doanh nào với nó.

Về cơ bản, các lỗ hổng có thể xảy ra nếu bạn khôi phục các hoạt động INSERT (hoặc xóa rõ ràng các hàng) và trùng lặp có thể xảy ra nếu bạn đặt thuộc tính bảng IDENTITY_INSERT thành ON.

Khoảng trống có thể xảy ra khi:

  1. Hồ sơ bị xóa.
  2. Đã xảy ra lỗi khi cố gắng chèn một bản ghi mới (khôi phục lại)
  3. Một bản cập nhật / chèn với giá trị rõ ràng (tùy chọn nhận dạng).
  4. Giá trị gia tăng là hơn 1.
  5. Một giao dịch cuộn lại.

Tài sản nhận dạng trên một cột chưa bao giờ được đảm bảo:

• Tính độc đáo

• Giá trị liên tiếp trong một giao dịch. Nếu các giá trị phải liên tiếp thì giao dịch nên sử dụng khóa độc quyền trên bảng hoặc sử dụng mức cô lập SERIALIZABLE.

• Giá trị liên tiếp sau khi máy chủ khởi động lại.

• Tái sử dụng các giá trị.

Nếu bạn không thể sử dụng các giá trị nhận dạng vì điều này, hãy tạo một bảng riêng giữ một giá trị hiện tại và quản lý quyền truy cập vào bảng và gán số với ứng dụng của bạn. Điều này không có tiềm năng ảnh hưởng đến hiệu suất.

https://msdn.microsoft.com/en-us/l Library / ms186775 (v =
sql.105) .aspx https://msdn.microsoft.com/en-us/l Library / ms186775 (v = sql.110) .aspx


Tôi nghĩ rằng khoảng trống không phải là vấn đề chính của tôi - vấn đề chính của tôi là tăng dần khả năng hiển thị của các giá trị. (Tức là, giá trị nhận dạng 7 không được quan sát theo thứ tự truy vấn theo giá trị đó trước giá trị nhận dạng 6).
Fabian Schmied

1
Tôi đã thấy các giá trị nhận dạng như: 1, 2, 5, 3, 4.
stacylaray

Chắc chắn, điều này có thể tái tạo dễ dàng, ví dụ, sử dụng kịch bản từ câu trả lời của Lennart. Câu hỏi tôi đang đấu tranh là liệu tôi có thể quan sát thứ tự cam kết đó khi sử dụng truy vấn với ORDER BY CheckpointSequencemệnh đề (điều này xảy ra là thứ tự của chỉ mục được nhóm). Tôi nghĩ rằng vấn đề đặt ra là liệu việc tạo ra một giá trị Danh tính có liên quan đến các khóa được thực hiện bởi câu lệnh INSERT hay không, nếu đây chỉ là hai hành động không liên quan được thực hiện bởi SQL Server.
Fabian Schmied

1
Truy vấn là gì? Nếu sử dụng đọc đã cam kết thì trong ví dụ của bạn, thứ tự sẽ hiển thị 1, 2, 3, 5 vì chúng đã được cam kết và 4 không, tức là đọc bẩn. Ngoài ra, lời giải thích của bạn về NEventStore nói "Do đó, điều quan trọng là các sự kiện không bao giờ có thể bị bỏ qua, vì chúng sẽ không bao giờ được xem xét lại."
stacylaray

Truy vấn được đưa ra ở trên ( gist.github.com/fschmied/47f716c32cb64b852f90 ) - nó được phân trang, nhưng đơn giản hóa SELECT ... FROM Commits WHERE CheckpointSequence > ... ORDER BY CheckpointSequence. Tôi không nghĩ rằng truy vấn này sẽ đọc qua hàng 4 bị khóa, hoặc nó sẽ? (Trong các thử nghiệm của tôi, nó chặn khi truy vấn cố lấy khóa KEY cho hàng 4.)
Fabian Schmied

1

Tôi nghi ngờ rằng đôi khi nó có thể dẫn đến rắc rối, rắc rối trở nên tồi tệ hơn khi máy chủ đang tải nặng. Xem xét hai giao dịch:

  1. T1: chèn vào T ... - giả sử 5 được chèn
  2. T2: chèn vào T ... - giả sử 6 được chèn
  3. T2: cam kết
  4. Người đọc nhìn thấy 6 nhưng không phải 5
  5. T1: cam kết

Trong trường hợp trên, LAST_READ_ID của bạn sẽ là 6, vì vậy 5 sẽ không bao giờ được đọc.


Các thử nghiệm của tôi dường như chỉ ra rằng kịch bản này không phải là vấn đề vì Reader (bước 4) sẽ chặn (cho đến khi T1 phát hành khóa) khi nó cố đọc hàng có giá trị 5. Tôi có thiếu gì không?
Fabian Schmied

Bạn có thể đúng, tôi không biết cơ chế khóa trong máy chủ SQL là tốt (do đó tôi nghi ngờ trong câu trả lời của mình).
Lennart

Phụ thuộc vào mức độ cô lập của người đọc. Tôi thấy cả hai, chặn hoặc chỉ thấy 6.
Michael Green

0

Chạy tập lệnh này:

BEGIN TRAN;
INSERT INTO dbo.Example DEFAULT VALUES;
COMMIT;

Dưới đây là các khóa tôi thấy có được và được phát hành khi được chụp bởi phiên Sự kiện mở rộng:

name            timestamp                   associated_object_id    mode    object_id   resource_type   session_id  resource_description
lock_acquired   2016-03-29 06:37:28.9968693 1585440722              IX      1585440722  OBJECT          51          
lock_acquired   2016-03-29 06:37:28.9969268 7205759890195415040     IX      0           PAGE            51          1:1235
lock_acquired   2016-03-29 06:37:28.9969306 7205759890195415040     RI_NL   0           KEY             51          (ffffffffffff)
lock_acquired   2016-03-29 06:37:28.9969330 7205759890195415040     X       0           KEY             51          (29cf3326f583)
lock_released   2016-03-29 06:37:28.9969579 7205759890195415040     X       0           KEY             51          (29cf3326f583)
lock_released   2016-03-29 06:37:28.9969598 7205759890195415040     IX      0           PAGE            51          1:1235
lock_released   2016-03-29 06:37:28.9969607 1585440722              IX      1585440722  OBJECT          51      

Lưu ý khóa RI_N KEY có được ngay trước khi khóa phím X cho hàng mới được tạo. Khóa phạm vi có thời gian tồn tại ngắn này sẽ ngăn không cho một bộ chèn đồng thời có được một khóa RI_N KEY khác vì các khóa RI_N không tương thích. Cửa sổ bạn đã đề cập giữa bước 2 và 3 không phải là vấn đề đáng lo ngại vì khóa phạm vi được lấy trước khóa hàng trên khóa mới được tạo.

Miễn là bạn SELECT...ORDER BYbắt đầu quét trước các hàng mới được chèn mong muốn, tôi sẽ mong đợi hành vi bạn mong muốn ở READ COMMITTEDmức cô lập mặc định miễn là READ_COMMITTED_SNAPSHOTtùy chọn cơ sở dữ liệu bị tắt.


1
Theo technet.microsoft.com/en-us/library/... , hai ổ khóa với RangeI_Ntương thích , ví dụ, không chặn nhau (khóa chủ yếu là ở đó chặn trên một đầu đọc serializable hiện có).
Fabian Schmied

@FabianSchmied, thú vị. Chủ đề đó mâu thuẫn với ma trận tương thích khóa trong technet.microsoft.com/en-us/l Library / ms186394 (v = sql.105) .aspx , cho thấy các khóa không tương thích. Ví dụ chèn trong liên kết bạn đã đề cập có nêu hành vi tương tự như được hiển thị trong dấu vết trong câu trả lời của tôi (khóa phạm vi chèn ngắn để kiểm tra phạm vi trước khóa khóa độc quyền).
Dan Guzman

1
Trên thực tế, ma trận nói "N" cho "không xung đột" (không phải "không tương thích") :)
Fabian Schmied

0

Theo hiểu biết của tôi về SQL Server, hành vi mặc định là cho truy vấn thứ hai không hiển thị bất kỳ kết quả nào cho đến khi truy vấn đầu tiên được thực hiện. Nếu truy vấn đầu tiên thực hiện ROLLBACK thay vì CAM KẾT, thì bạn sẽ có một ID bị thiếu trong cột của mình.

Cấu hình cơ bản

Bảng cơ sở dữ liệu

Tôi đã tạo một bảng cơ sở dữ liệu với cấu trúc sau:

CREATE TABLE identity_rc_test (
    ID4VALUE INT IDENTITY (1,1), 
    TEXTVALUE NVARCHAR(20),
    CONSTRAINT PK_ID4_VALUE_CLUSTERED 
        PRIMARY KEY CLUSTERED (ID4VALUE, TEXTVALUE)
)

Cấp độ cách ly cơ sở dữ liệu

Tôi đã kiểm tra mức độ cô lập của cơ sở dữ liệu của mình bằng tuyên bố sau:

SELECT snapshot_isolation_state, 
       snapshot_isolation_state_desc, 
       is_read_committed_snapshot_on
FROM sys.databases WHERE NAME = 'mydatabase'

Trả về kết quả sau cho cơ sở dữ liệu của tôi:

snapshot_isolation_state    snapshot_isolation_state_desc   is_read_committed_snapshot_on
0                           OFF                             0

(Đây là cài đặt mặc định cho cơ sở dữ liệu trong SQL Server 2012)

Kiểm tra tập lệnh

Các tập lệnh sau được thực thi bằng cài đặt máy khách SQL Server SSMS tiêu chuẩn và cài đặt Máy chủ SQL tiêu chuẩn.

Cài đặt kết nối máy khách

Máy khách đã được đặt để sử dụng Cấp cách ly giao dịch READ COMMITTEDtheo Tùy chọn truy vấn trong SSMS.

Truy vấn 1

Truy vấn sau được thực hiện trong cửa sổ Truy vấn với SPID 57

SELECT * FROM dbo.identity_rc_test
BEGIN TRANSACTION [FIRST_QUERY]
INSERT INTO dbo.identity_rc_test (TEXTVALUE) VALUES ('Nine')
/* Commit is commented out to prevent the INSERT from being commited
--COMMIT TRANSACTION [FIRST_QUERY]
--ROLLBACK TRANSACTION [FIRST_QUERY]
*/

Truy vấn 2

Truy vấn sau được thực hiện trong cửa sổ Truy vấn với SPID 58

BEGIN TRANSACTION [SECOND_QUERY]
INSERT INTO dbo.identity_rc_test (TEXTVALUE) VALUES ('Ten')
COMMIT TRANSACTION [SECOND_QUERY]
SELECT * FROM dbo.identity_rc_test

Truy vấn không hoàn thành và đang chờ khóa eXpol được phát hành trên TRANG.

Kịch bản để xác định khóa

Kịch bản lệnh này hiển thị khóa xảy ra trên các đối tượng cơ sở dữ liệu cho hai giao dịch:

SELECT request_session_id, resource_type,
       resource_description, 
       resource_associated_entity_id,
       request_mode, request_status
FROM sys.dm_tran_locks
WHERE request_session_id IN (57, 58)

Và đây là kết quả:

58  DATABASE                    0                   S   GRANT
57  DATABASE                    0                   S   GRANT
58  PAGE            1:79        72057594040549300   IS  GRANT
57  PAGE            1:79        72057594040549300   IX  GRANT
57  KEY         (a0aba7857f1b)  72057594040549300   X   GRANT
58  KEY         (a0aba7857f1b)  72057594040549300   S   WAIT
58  OBJECT                      245575913           IS  GRANT
57  OBJECT                      245575913           IX  GRANT

Kết quả cho thấy cửa sổ truy vấn một (SPID 57) có khóa được chia sẻ (S) trên cơ sở dữ liệu Khóa dữ liệu dự định (IX) trên ĐỐI TƯỢNG, khóa Dự kiến ​​eXlusive (IX) trên TRANG mà nó muốn chèn và khóa eXinating khóa (X) trên KEY nó đã được chèn, nhưng chưa được cam kết.

Do dữ liệu không được cam kết, truy vấn thứ hai (SPID 58) có Khóa chia sẻ (S) ở cấp độ DATABASE, khóa được chia sẻ dự định (IS) trên ĐỐI TƯỢNG, khóa được chia sẻ dự định (IS) trên trang được chia sẻ (S) ) khóa trên KEY với trạng thái yêu cầu WAIT.

Tóm lược

Truy vấn trong cửa sổ truy vấn đầu tiên thực hiện mà không cam kết. Bởi vì truy vấn thứ hai chỉ có thể READ COMMITTEDdữ liệu nên nó chờ cho đến khi hết thời gian chờ hoặc cho đến khi giao dịch được thực hiện trong truy vấn đầu tiên.

Đây là từ hiểu biết của tôi về hành vi mặc định của Microsoft SQL Server.

Bạn nên quan sát rằng ID thực sự theo trình tự cho các lần đọc tiếp theo bởi các câu lệnh CHỌN nếu câu lệnh đầu tiên CAM KẾT.

Nếu câu lệnh đầu tiên thực hiện ROLLBACK thì bạn sẽ tìm thấy một ID bị thiếu trong chuỗi, nhưng vẫn với ID theo thứ tự tăng dần (miễn là bạn đã tạo INDEX với tùy chọn mặc định hoặc ASC trên cột ID).

Cập nhật:

(Nói thẳng ra) Có, bạn có thể dựa vào cột danh tính hoạt động chính xác, cho đến khi bạn gặp phải một vấn đề. Chỉ có một HOTFIX liên quan đến SQL Server 2000 và cột nhận dạng trên trang web của Microsoft.

Nếu bạn không thể dựa vào cột nhận dạng cập nhật chính xác, tôi nghĩ sẽ có nhiều hotfix hoặc bản vá trên trang web của Microsoft.

Nếu bạn có Hợp đồng hỗ trợ của Microsoft, bạn luôn có thể mở Trường hợp tư vấn và yêu cầu thêm thông tin.


1
Cảm ơn bạn đã phân tích, nhưng câu hỏi của tôi là nếu có một cửa sổ thời gian giữa việc tạo ra Identitygiá trị tiếp theo và việc mua khóa KEY trên hàng (nơi có thể đọc / ghi đồng thời). Tôi không nghĩ rằng điều này được chứng minh là không thể bởi các quan sát của bạn bởi vì người ta không thể dừng thực thi truy vấn và phân tích các khóa trong cửa sổ thời gian cực ngắn đó.
Fabian Schmied

Không, bạn không thể dừng các tuyên bố, nhưng quan sát (chậm) của tôi là những gì xảy ra trên cơ sở nhanh / bình thường. Ngay khi một SPID có được một khóa để chèn dữ liệu, thì một SPID khác sẽ không thể có được cùng một khóa. Câu lệnh nhanh hơn sẽ có lợi thế là đã có được khóa và ID theo trình tự. Tuyên bố tiếp theo sẽ nhận được ID tiếp theo sau khi khóa được phát hành.
John aka hot2use 30/03/2016

1
Trên cơ sở bình thường, các quan sát của bạn phù hợp với chính tôi (và cả mong đợi của tôi) - đó là điều tốt để biết. Tôi tự hỏi nếu có những tình huống đặc biệt mà họ sẽ không giữ, mặc dù.
Fabian Schmied
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.