Bisakah saya mengandalkan membaca nilai-nilai SQL Server Identity secara berurutan?

24

TL; DR: Pertanyaan di bawah ini bermuara pada: Ketika memasukkan baris, apakah ada jendela peluang antara pembuatanIdentity nilai baru dan penguncian kunci baris yang sesuai dalam indeks berkerumun, di mana pengamat eksternal dapat melihat yang lebih baru Identity nilai dimasukkan oleh transaksi bersamaan? (Dalam SQL Server.)

Versi terperinci

Saya memiliki tabel SQL Server dengan Identitykolom yang disebut CheckpointSequence, yang merupakan kunci dari indeks berkerumun tabel (yang juga memiliki sejumlah indeks nonclustered tambahan). Baris dimasukkan ke dalam tabel oleh beberapa proses dan utas bersamaan (pada tingkat isolasi READ COMMITTED, dan tanpa IDENTITY_INSERT). Pada saat yang sama, ada proses membaca baris secara berkala dari indeks berkerumun, dipesan oleh CheckpointSequencekolom itu (juga pada tingkat isolasi READ COMMITTED, dengan READ COMMITTED SNAPSHOTopsi dimatikan).

Saat ini saya mengandalkan fakta bahwa proses membaca tidak pernah bisa "melewati" pos pemeriksaan. Pertanyaan saya adalah: Dapatkah saya mengandalkan properti ini? Dan jika tidak, apa yang bisa saya lakukan untuk mewujudkannya?

Contoh: Ketika baris dengan nilai identitas 1, 2, 3, 4, dan 5 dimasukkan, pembaca tidak boleh melihat baris dengan nilai 5 sebelum melihat yang dengan nilai 4. Tes menunjukkan bahwa kueri, yang berisi ORDER BY CheckpointSequenceklausa ( dan WHERE CheckpointSequence > -1klausa), andal memblokir kapan pun baris 4 dibaca, tetapi belum berkomitmen, bahkan jika baris 5 sudah dikomit.

Saya percaya bahwa setidaknya secara teori, mungkin ada kondisi ras di sini yang dapat menyebabkan asumsi ini rusak. Sayangnya, dokumentasi pada Identitytidak mengatakan banyak tentang cara Identitykerjanya dalam konteks beberapa transaksi bersamaan, hanya mengatakan "Setiap nilai baru dihasilkan berdasarkan pada seed & increment saat ini." dan "Setiap nilai baru untuk transaksi tertentu berbeda dari transaksi bersamaan lainnya di atas meja." ( MSDN )

Alasan saya adalah, itu harus bekerja entah bagaimana seperti ini:

  1. Transaksi dimulai (baik secara eksplisit atau implisit).
  2. Nilai identitas (X) dihasilkan.
  3. Kunci baris terkait diambil pada indeks berkerumun berdasarkan pada nilai identitas (kecuali jika eskalasi kunci menendang, dalam hal ini seluruh tabel terkunci).
  4. Baris dimasukkan.
  5. Transaksi dilakukan (mungkin cukup banyak waktu kemudian), sehingga kunci dihapus lagi.

Saya pikir antara langkah 2 dan 3, ada jendela yang sangat kecil di mana

  • sesi bersamaan dapat menghasilkan nilai identitas berikutnya (X + 1) dan menjalankan semua langkah yang tersisa,
  • sehingga memungkinkan pembaca datang tepat pada titik waktu itu untuk membaca nilai X + 1, kehilangan nilai X.

Tentu saja, kemungkinan ini tampaknya sangat rendah; tapi tetap saja - itu bisa terjadi. Atau mungkinkah itu?

(Jika Anda tertarik dengan konteksnya: Ini adalah implementasi dari SQL Persistence Engine NEventStore. NEventStore mengimplementasikan toko acara khusus-append di mana setiap acara mendapatkan nomor urut pos pemeriksaan naik yang baru. Klien membaca acara dari toko acara yang dipesan oleh pos pemeriksaan) untuk melakukan perhitungan dari segala macam. Begitu suatu peristiwa dengan pos pemeriksaan X telah diproses, klien hanya mempertimbangkan peristiwa "baru", yaitu peristiwa dengan pos pemeriksaan X +1 dan di atasnya. Oleh karena itu, sangat penting bahwa peristiwa tidak pernah dapat dilewati, karena mereka tidak akan pernah dipertimbangkan lagi. Saat ini saya mencoba untuk menentukan apakah Identityimplementasi pos pemeriksaan berbasis memenuhi persyaratan ini. Ini adalah pernyataan SQL yang tepat digunakan : Skema , permintaan Writer ,Permintaan Pembaca .)

Jika saya benar dan situasi yang dijelaskan di atas dapat muncul, saya hanya dapat melihat dua opsi untuk berurusan dengan mereka, yang keduanya tidak memuaskan:

  • Saat melihat nilai urutan pos pemeriksaan X + 1 sebelum melihat X, singkirkan X + 1 dan coba lagi nanti. Namun, karena Identitytentu saja dapat menghasilkan kesenjangan (misalnya, ketika transaksi dibatalkan), X mungkin tidak pernah datang.
  • Jadi, pendekatan yang sama, tetapi menerima kesenjangan setelah n milidetik. Namun, nilai n apa yang harus saya asumsikan?

Ada ide yang lebih baik?

Fabian Schmied
sumber
Sudahkah Anda mencoba menggunakan Urutan alih-alih identitas? Dengan identitas, saya tidak berpikir Anda dapat dengan andal memprediksi sisipan mana yang akan mendapatkan nilai identitas tertentu tetapi ini seharusnya tidak menjadi masalah menggunakan urutan. Tentu saja itu mengubah cara Anda melakukan berbagai hal sekarang.
Antoine Hernandez
@ SoleDBAGuy Bukankah Sekuens membuat kondisi balapan yang saya jelaskan di atas lebih mungkin? Saya menghasilkan nilai Sequence baru X (menggantikan langkah 2 di atas), lalu menyisipkan baris (langkah 3 dan 4). Antara 2 dan 3, ada kemungkinan bahwa orang lain dapat menghasilkan nilai Urutan berikutnya X + 1, membuatnya, dan pembaca membaca nilai itu X + 1 bahkan sebelum saya bisa memasukkan baris saya dengan nilai Urutan X.
Fabian Schmied

Jawaban:

26

Saat menyisipkan baris, apakah ada jendela peluang antara pembuatan nilai Identitas baru dan penguncian kunci baris yang sesuai dalam indeks berkerumun, di mana pengamat eksternal dapat melihat nilai Identitas yang lebih baru dimasukkan oleh transaksi bersamaan?

Iya nih.

The alokasi nilai-nilai identitas independen dari transaksi pengguna mengandung . Ini adalah salah satu alasan nilai-nilai identitas dikonsumsi bahkan jika transaksi dibatalkan. Operasi kenaikan itu sendiri dilindungi oleh kait untuk mencegah korupsi, tetapi sejauh itulah perlindungan.

Dalam keadaan spesifik implementasi Anda, alokasi identitas (panggilan ke CMEDSeqGen::GenerateNewValue) dilakukan sebelum transaksi pengguna untuk memasukkan bahkan diaktifkan (dan sebelum kunci diambil).

Dengan menjalankan dua sisipan bersamaan dengan debugger yang dilampirkan untuk memungkinkan saya membekukan satu utas tepat setelah nilai identitas bertambah dan dialokasikan, saya dapat mereproduksi skenario di mana:

  1. Sesi 1 memperoleh nilai identitas (3)
  2. Sesi 2 memperoleh nilai identitas (4)
  3. Sesi 2 melakukan penyisipan dan melakukan (sehingga baris 4 sepenuhnya terlihat)
  4. Sesi 1 melakukan penyisipan dan komitmennya (baris 3)

Setelah langkah 3, kueri yang menggunakan row_number di bawah penguncian read berkomitmen mengembalikan yang berikut:

Tangkapan layar

Dalam implementasi Anda, ini akan mengakibatkan ID Pos Pemeriksaan 3 dilewati secara tidak benar.

Jendela misopportunity relatif kecil, tetapi ada. Untuk memberikan skenario yang lebih realistis daripada memiliki debugger terpasang: Sebuah thread permintaan mengeksekusi dapat menghasilkan penjadwal setelah langkah 1 di atas. Ini memungkinkan utas kedua mengalokasikan nilai identitas, menyisipkan, dan melakukan, sebelum utas asli melanjutkan untuk melakukan sisipannya.

Untuk kejelasan, tidak ada kunci atau objek sinkronisasi lain yang melindungi nilai identitas setelah dialokasikan dan sebelum digunakan. Misalnya, setelah langkah 1 di atas, transaksi konkuren dapat melihat nilai identitas baru menggunakan fungsi T-SQL seperti IDENT_CURRENTsebelum baris ada dalam tabel (bahkan tidak berkomitmen).

Pada dasarnya, tidak ada lagi jaminan seputar nilai identitas selain yang didokumentasikan :

  • Setiap nilai baru dihasilkan berdasarkan seed & increment saat ini.
  • Setiap nilai baru untuk transaksi tertentu berbeda dari transaksi bersamaan lainnya di atas meja.

Itu benar-benar itu.

Jika pemrosesan FIFO transaksional ketat diperlukan, Anda mungkin tidak punya pilihan selain membuat cerita bersambung secara manual. Jika aplikasi memiliki persyaratan kurang satu, Anda memiliki lebih banyak opsi. Pertanyaannya tidak 100% jelas dalam hal itu. Namun demikian, Anda dapat menemukan beberapa informasi yang berguna dalam artikel Remus Rusanu Menggunakan Tabel sebagai Antrian .

Paul White mengatakan GoFundMonica
sumber
7

Ketika Paul White menjawab sepenuhnya benar, ada kemungkinan untuk "melewatkan" baris identitas sementara. Ini hanya sepotong kecil kode untuk mereproduksi kasus ini untuk Anda sendiri.

Buat database dan tabel tes:

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)

Lakukan penyisipan dan seleksi bersamaan pada tabel ini dalam program konsol 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();
                }
            }
        }
    }
}

Konsol ini mencetak baris untuk setiap kasus ketika salah satu utas pembacaan "melewatkan" entri.

Stefan Kainz
sumber
1
Kode yang bagus tetapi Anda hanya memeriksa id berturut-turut ( "// tulis baris jika row1 dan baris tidak berturut-turut" ). Mungkin ada celah yang dihasilkan bahwa kode Anda akan dicetak. Itu tidak berarti bahwa celah ini akan diisi nanti.
ypercubeᵀᴹ
1
Karena kode tidak memicu skenario di mana IDENTITYakan menghasilkan kesenjangan (seperti memutar kembali transaksi), garis yang dicetak memang menunjukkan nilai "dilewati" (atau setidaknya mereka lakukan ketika saya berlari dan memeriksanya di mesin saya). Sampel repro yang sangat bagus!
Fabian Schmied
5

Yang terbaik adalah tidak mengharapkan identitas menjadi berurutan karena ada banyak skenario yang dapat meninggalkan celah. Lebih baik mempertimbangkan identitas seperti angka abstrak dan tidak melampirkan arti bisnis apa pun padanya.

Pada dasarnya, kesenjangan dapat terjadi jika Anda memutar kembali operasi INSERT (atau menghapus baris secara eksplisit), dan duplikat dapat terjadi jika Anda mengatur properti tabel IDENTITY_INSERT ke ON.

Kesenjangan dapat terjadi ketika:

  1. Catatan dihapus.
  2. Terjadi kesalahan saat mencoba menyisipkan catatan baru (diputar kembali)
  3. Pembaruan / masukkan dengan nilai eksplisit (opsi identity_insert).
  4. Nilai tambahan lebih dari 1.
  5. Transaksi dibatalkan.

Properti identitas pada kolom tidak pernah dijamin:

• Keunikan

• Nilai berturut-turut dalam suatu transaksi. Jika nilai harus berturut-turut maka transaksi harus menggunakan kunci eksklusif di atas meja atau menggunakan tingkat isolasi SERIALIZABLE.

• Nilai berturut-turut setelah server restart.

• Penggunaan kembali nilai.

Jika Anda tidak dapat menggunakan nilai identitas karena hal ini, buat tabel terpisah yang menyimpan nilai saat ini dan kelola akses ke tabel dan penetapan angka dengan aplikasi Anda. Ini memang berpotensi memengaruhi kinerja.

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

stacylaray
sumber
Saya pikir kesenjangan bukanlah masalah utama saya - masalah utama saya adalah naiknya visibilitas nilai. (Yaitu, katakanlah, nilai identitas 7 tidak boleh terlihat pada permintaan pemesanan dengan nilai itu sebelum nilai identitas 6).
Fabian Schmied
1
Saya telah melihat nilai-nilai identitas melakukan seperti: 1, 2, 5, 3, 4.
stacylaray
Tentu, ini mudah direproduksi, misalnya, menggunakan skenario dari jawaban Lennart. Pertanyaan yang saya perjuangkan adalah apakah saya dapat mengamati perintah komit tersebut ketika menggunakan kueri dengan ORDER BY CheckpointSequenceklausa (yang merupakan urutan indeks berkerumun). Saya pikir itu bermuara pada pertanyaan apakah generasi nilai Identitas bagaimanapun terkait dengan kunci yang diambil oleh pernyataan INSERT, atau jika ini hanya dua tindakan yang tidak berhubungan yang dilakukan oleh SQL Server satu demi satu.
Fabian Schmied
1
Apa itu query? Jika menggunakan read commit maka, dalam contoh Anda, order by akan menunjukkan 1, 2, 3, 5 karena komitmen telah dilakukan dan 4 belum, yaitu read kotor. Juga, penjelasan Anda tentang NEventStore menyatakan "Oleh karena itu, sangat penting bahwa peristiwa tidak pernah dapat dilewati, karena mereka tidak akan pernah dipertimbangkan lagi."
stacylaray
Kueri diberikan di atas ( gist.github.com/fschmied/47f716c32cb64b852f90 ) - itu adalah paged, tetapi intinya adalah sederhana SELECT ... FROM Commits WHERE CheckpointSequence > ... ORDER BY CheckpointSequence. Saya tidak berpikir permintaan ini akan membaca melewati baris yang terkunci 4, atau akankah itu? (Dalam percobaan saya, itu memblokir ketika kueri mencoba untuk mendapatkan kunci KUNCI untuk baris 4.)
Fabian Schmied
1

Saya menduga bahwa itu kadang-kadang dapat menyebabkan masalah, masalah yang menjadi lebih buruk ketika server berada di bawah beban berat. Pertimbangkan dua transaksi:

  1. T1: masukkan ke T ... - katakan 5 bisa dimasukkan
  2. T2: masukkan ke T ... - katakan 6 bisa dimasukkan
  3. T2: komit
  4. Pembaca melihat 6 tetapi tidak 5
  5. T1: komit

Dalam skenario di atas, LAST_READ_ID Anda akan menjadi 6, jadi 5 tidak akan pernah dibaca.

Lennart
sumber
Tes saya tampaknya menunjukkan bahwa skenario ini tidak menjadi masalah karena Reader (langkah 4) akan memblokir (sampai T1 telah merilis kunci-nya) ketika mencoba membaca baris dengan nilai 5. Apakah saya kehilangan sesuatu?
Fabian Schmied
Anda mungkin benar, saya tidak tahu mekanisme penguncian di SQL server dengan baik (karenanya saya curiga dalam jawaban saya).
Lennart
Tergantung pada tingkat isolasi pembaca. Ini saya melihat keduanya, memblokir, atau hanya melihat 6.
Michael Green
0

Menjalankan skrip ini:

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

Di bawah ini adalah kunci yang saya lihat diperoleh dan dirilis saat ditangkap oleh sesi Peristiwa Diperpanjang:

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      

Perhatikan bahwa kunci kunci RI_N diperoleh tepat sebelum kunci tombol X untuk baris baru dibuat. Kunci rentang berumur pendek ini akan mencegah sisipan konkuren mendapatkan kunci RI_N KEY lainnya karena kunci RI_N tidak kompatibel. Jendela yang Anda sebutkan antara langkah 2 dan 3 tidak menjadi masalah karena kunci rentang diperoleh sebelum kunci baris pada kunci yang baru dibuat.

Selama Anda SELECT...ORDER BYmemulai pemindaian sebelum baris yang baru saja dimasukkan, saya akan mengharapkan perilaku yang Anda inginkan di READ COMMITTEDtingkat isolasi default selama READ_COMMITTED_SNAPSHOTopsi database dimatikan.

Dan Guzman
sumber
1
Menurut technet.microsoft.com/en-us/library/… , dua kunci dengan RangeI_Nyang kompatibel , yaitu, jangan memblokir satu sama lain (kunci itu sebagian besar ada untuk memblokir pada pembaca serializable yang ada).
Fabian Schmied
@FabianSchmied, menarik. Topik itu bertentangan dengan matriks kompatibilitas kunci di technet.microsoft.com/en-us/library/ms186396(v=sql.105).aspx , yang menunjukkan kunci tidak kompatibel. Contoh sisipan di tautan yang Anda sebutkan tidak menyatakan perilaku yang sama seperti yang ditunjukkan pada jejak dalam jawaban saya (kunci rentang masukkan berumur pendek untuk menguji rentang sebelum kunci kunci eksklusif).
Dan Guzman
1
Sebenarnya, matriks mengatakan "N" untuk "tidak ada konflik" (bukan untuk "tidak kompatibel") :)
Fabian Schmied
0

Dari pemahaman saya tentang SQL Server perilaku default adalah untuk permintaan kedua untuk tidak menampilkan hasil apa pun sampai permintaan pertama telah dilakukan. Jika kueri pertama melakukan ROLLBACK alih-alih KOMIT, maka Anda akan memiliki ID yang hilang di kolom Anda.

Konfigurasi Dasar

Tabel Database

Saya membuat tabel database dengan struktur berikut:

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

Tingkat Isolasi Basis Data

Saya memeriksa tingkat isolasi basis data saya dengan pernyataan berikut:

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

Yang mengembalikan hasil berikut untuk database saya:

snapshot_isolation_state    snapshot_isolation_state_desc   is_read_committed_snapshot_on
0                           OFF                             0

(Ini adalah pengaturan default untuk database di SQL Server 2012)

Tes Script

Skrip berikut dieksekusi menggunakan pengaturan klien SSMS SQL Server standar dan pengaturan SQL Server standar.

Pengaturan koneksi klien

Klien telah diatur untuk menggunakan Level Isolasi Transaksi READ COMMITTEDsesuai Opsi Kueri di SSMS.

Pertanyaan 1

Kueri berikut dijalankan di jendela Kueri dengan 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]
*/

Pertanyaan 2

Permintaan berikut dijalankan di jendela Query dengan 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

Permintaan tidak selesai dan sedang menunggu kunci eXclusive untuk dirilis pada HALAMAN.

Script untuk menentukan penguncian

Script ini menampilkan penguncian yang terjadi pada objek database untuk dua transaksi:

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)

Dan inilah hasilnya:

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

Hasil penelitian menunjukkan bahwa jendela permintaan satu (SPID 57) memiliki kunci Bersama (S) pada DATABASE kunci yang dimaksudkan eXlusive (IX) pada OBJECT, kunci yang dimaksudkan eXlusive (IX) pada PAGE yang ingin disisipkan ke dan eXclusive mengunci (X) pada KUNCI itu telah dimasukkan, tetapi belum dilakukan.

Karena data yang tidak dikomit, kueri kedua (SPID 58) memiliki kunci Bersama (S) pada tingkat DATABASE, kunci yang Dibagikan Bersama (IS) pada OBJECT, kunci yang Dibagikan Bersama (IS) pada halaman yang Dibagikan (S) ) mengunci KUNCI dengan status permintaan TUNGGU.

Ringkasan

Kueri di jendela kueri pertama dijalankan tanpa melakukan. Karena kueri kedua hanya dapat READ COMMITTEDdata itu menunggu baik sampai waktu habis terjadi atau sampai transaksi telah dilakukan dalam permintaan pertama.

Ini dari pemahaman saya tentang perilaku default Microsoft SQL Server.

Anda harus memperhatikan bahwa ID tersebut memang berurutan untuk pembacaan selanjutnya oleh pernyataan SELECT jika pernyataan pertama BERKOMITMEN.

Jika pernyataan pertama melakukan ROLLBACK maka Anda akan menemukan ID yang hilang dalam urutan, tetapi masih dengan ID dalam urutan menaik (asalkan Anda membuat INDEX dengan opsi default atau ASC pada kolom ID).

Memperbarui:

(Terus terang) Ya, Anda bisa mengandalkan kolom identitas yang berfungsi dengan benar, sampai Anda menemukan masalah. Hanya ada satu HOTFIX tentang SQL Server 2000 dan kolom identitas di situs web Microsoft.

Jika Anda tidak dapat mengandalkan pemutakhiran kolom identitas dengan benar, saya pikir akan ada lebih banyak perbaikan terbaru atau tambalan di situs web Microsoft.

Jika Anda memiliki Kontrak Dukungan Microsoft, Anda selalu dapat membuka Kasus Penasihat dan meminta informasi tambahan.

John alias hot2use
sumber
1
Terima kasih untuk analisisnya, tetapi pertanyaan saya adalah apakah ada jendela waktu antara generasi nilai berikutnya Identitydan perolehan kunci KUNCI pada baris (di mana membaca / penulis bersamaan dapat jatuh ke dalam). Saya tidak berpikir ini terbukti mustahil oleh pengamatan Anda karena orang tidak dapat menghentikan eksekusi permintaan dan menganalisis kunci selama jendela waktu ultra-pendek.
Fabian Schmied
Tidak, Anda tidak dapat menghentikan pernyataan, tetapi pengamatan saya (lambat) adalah apa yang terjadi secara cepat / normal. Segera setelah satu SPID mendapatkan kunci untuk memasukkan data, yang lain tidak akan dapat memperoleh kunci yang sama. Pernyataan yang lebih cepat akan memiliki keuntungan karena telah memperoleh kunci dan ID secara berurutan. Pernyataan berikutnya akan menerima ID berikutnya setelah kunci telah dirilis.
John aka hot2use
1
Secara normal, pengamatan Anda cocok dengan saya sendiri (dan juga harapan saya) - itu bagus untuk diketahui. Saya bertanya-tanya apakah ada situasi luar biasa di mana mereka tidak akan bertahan.
Fabian Schmied