Bagaimana IDENTITY_INSERT memengaruhi concurrency?

11

Saya mencoba untuk membantu pelanggan dengan add-on SAP pihak ke-3 yang memiliki kesalahan pengiriman dan tidak lagi didukung.

Dalam keadaan tertentu, arsip dan posting tidak lengkap dari tabel antrian posting ke tabel arsip postingan. Saya perlu memindahkan hasil yang diarsipkan ini kembali ke antrian.

ID antrian adalah kolom identitas dan saya ingin tetap sama.

Pertanyaannya adalah, jika saya menonaktifkan identitas_insert di / masukkan / identitas_insert, apa yang dapat saya harapkan sehubungan dengan konkurensi dengan proses yang membuat entri antrian dan mengharapkan kolom identitas dihasilkan secara otomatis?

Setiap petunjuk tentang cara terbaik untuk menunjukkan perilaku seperti itu akan sangat dihargai juga.

Metafora
sumber

Jawaban:

8

Pengaturan IDENTITY_INSERT ONsendiri tidak menghilangkan konkurensi - ini tidak menempatkan kunci eksklusif di atas meja, hanya kunci skema stabilitas (Sch-S) singkat.

Jadi apa yang secara teoritis bisa terjadi, di bawah perilaku default, adalah Anda bisa melakukan ini di sesi 1:

BEGIN TRANSACTION;

-- 1
SET IDENTITY_INSERT dbo.tablename ON;

-- 2
INSERT dbo.tablename(id, etc) VALUES(100, 'foo'); -- next identity is now 101

-- 3
INSERT dbo.tablename(id, etc) VALUES(101, 'foo'); -- next identity is now 102

-- 4
SET IDENTITY_INSERT dbo.tablename OFF;

COMMIT TRANSACTION;

Di sesi lain, Anda bisa menyisipkan baris ke tabel pada poin 1, 2, 3 atau 4. Ini mungkin tampak seperti hal yang baik, kecuali apa yang terjadi untuk setiap sisipan yang terjadi antara 2 dan 3, adalah bahwa nilai yang dihasilkan secara otomatis dipicu oleh sesi lain didasarkan pada hasil pernyataan 2 - sehingga akan menghasilkan 101, dan kemudian pernyataan 3 akan gagal dengan pelanggaran kunci utama. Ini cukup sederhana untuk mengatur dan menguji diri Anda dengan beberapa WAITFORs:

-- session 1
-- DROP TABLE dbo.what;
CREATE TABLE dbo.what(id INT IDENTITY PRIMARY KEY);
GO
BEGIN TRANSACTION;

SET IDENTITY_INSERT dbo.what ON;

INSERT dbo.what(id) VALUES(32);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(33);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(34);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(35);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(36);

SET IDENTITY_INSERT dbo.what OFF;

COMMIT TRANSACTION;

Setelah kumpulan itu dimulai, mulai kumpulan ini di jendela lain:

-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20

Sesi 2 seharusnya hanya memasukkan nilai dari 1-20, kan? Kecuali itu karena identitas yang mendasarinya telah diperbarui oleh sesi manual sisipan 1 Anda, pada beberapa titik sesi 2 akan mengambil di mana sesi 1 tinggalkan, dan menyisipkan 32, atau 33, atau 34 dll. Ini akan diizinkan untuk melakukan ini, tetapi maka sesi 1 akan gagal pada sisipan berikutnya dengan pelanggaran PK (yang menang mungkin hanya masalah waktu).

Salah satu cara untuk mengatasinya adalah dengan memohon TABLOCKpada sisipan pertama:

INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);

Ini akan memblokir setiap pengguna lain yang mencoba menyisipkan (atau melakukan apa saja, sungguh) dengan tabel ini sampai Anda selesai memindahkan baris yang diarsipkan kembali. Ini mencekik konkurensi, tentu saja, tetapi ini adalah cara Anda ingin memblokir untuk bekerja. Dan mudah-mudahan ini bukan sesuatu yang terjadi pada tingkat yang sering terjadi di mana Anda memblokir orang lain sepanjang waktu.

Beberapa solusi lain:

  • berhenti peduli tentang IDENTITYnilai yang dihasilkan. Siapa peduli? Gunakan a UNIQUEIDENTIFIER(mungkin dihasilkan dalam tabel terpisah dengan IDENTITYsebagai pengganti) jika nilai aslinya sangat penting.
  • ubah proses arsip untuk menggunakan "hapus lunak" di mana sesuatu ditandai sebagai diarsipkan pada awalnya dan arsip tidak dibuat permanen sampai beberapa waktu kemudian. Maka proses apa pun yang mencoba untuk memindahkan mereka kembali hanya dapat melakukan pembaruan langsung dan memperbaiki bendera hapus lunak.
Aaron Bertrand
sumber
Terima kasih atas penjelasan Anda. Saya menulis utilitas yang berdiri sendiri untuk membantu dengan produk yang tidak didukung yang menciptakan bidang identitas ini. Saya tidak memiliki kendali atas cara kerjanya.
Metafora