Saya mencoba memahami masalah yang saya alami yang menurut saya tidak mungkin terjadi ketika menangani transaksi menggunakan tingkat isolasi komitmen baca. Saya memiliki tabel yang digunakan sebagai antrian. Dalam satu utas (koneksi 1) saya memasukkan beberapa kumpulan 20 catatan ke dalam setiap tabel. Setiap batch yang terdiri dari 20 catatan dilakukan di dalam suatu transaksi. Di thread kedua (koneksi 2) saya melakukan update untuk mengubah status record yang telah dimasukkan ke dalam antrian, yang juga terjadi di dalam suatu transaksi. Saat dijalankan secara bersamaan, saya berharap jumlah baris yang terpengaruh oleh pembaruan (koneksi 2) harus kelipatan 20, karena koneksi 1 menyisipkan baris ke dalam tabel yang disisipkan dalam kumpulan 20 baris dalam suatu transaksi.
Namun pengujian saya menunjukkan bahwa hal ini tidak selalu terjadi, dan kadang-kadang saya dapat memperbarui subset catatan dari kumpulan koneksi 1. Apakah ini mungkin terjadi atau saya melewatkan sesuatu tentang transaksi, konkurensi, dan tingkat isolasi? Di bawah ini adalah serangkaian skrip pengujian yang saya buat untuk mereproduksi masalah ini di T-SQL.
Skrip ini menyisipkan 20.000 catatan ke dalam tabel dalam kumpulan transaksi sebanyak 20.
USE ReadTest
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
SET NOCOUNT ON
DECLARE @trans_id INTEGER
DECLARE @cmd_id INTEGER
DECLARE @text_str VARCHAR(4000)
SET @trans_id = 0
SET @text_str = 'Placeholder String Value'
-- First empty the table
DELETE FROM TABLE_A
WHILE @trans_id < 1000 BEGIN
SET @trans_id = @trans_id + 1
SET @cmd_id = 0
BEGIN TRANSACTION
-- Insert 20 records into the table per transaction
WHILE @cmd_id < 20 BEGIN
SET @cmd_id = @cmd_id + 1
INSERT INTO TABLE_A ( transaction_id, command_id, [type], status, text_field )
VALUES ( @trans_id, @cmd_id, 1, 1, @text_str )
END
COMMIT
END
PRINT 'DONE'
Skrip ini memperbarui catatan dalam tabel, mengubah status dari 1 menjadi 2 dan kemudian memeriksa jumlah baris dari operasi pembaruan. Jika jumlah baris bukan kelipatan 20, dan pernyataan print menunjukkan hal ini dan jumlah baris yang terpengaruh.
USE ReadTest
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
SET NOCOUNT ON
DECLARE @loop_counter INTEGER
DECLARE @trans_id INTEGER
DECLARE @count INTEGER
SET @loop_counter = 0
WHILE @loop_counter < 100000 BEGIN
SET @loop_counter = @loop_counter + 1
BEGIN TRANSACTION
UPDATE TABLE_A SET status = 2
WHERE status = 1
and type = 1
SET @count = @@ROWCOUNT
COMMIT
IF ( @count % 20 <> 0 ) BEGIN
-- Records in concurrent transaction inserting in batches of 20 records before commit.
PRINT '*** Rowcount not a multiple of 20. Count = ' + CAST(@count AS VARCHAR) + ' ***'
END
IF @count > 0 BEGIN
-- Delete the records where the status was changed.
DELETE TABLE_A WHERE status = 2
END
END
PRINT 'DONE'
Skrip ini membuat tabel antrian pengujian di database baru bernama ReadTest.
USE master;
GO
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'ReadTest')
BEGIN;
DROP DATABASE ReadTest;
END;
GO
CREATE DATABASE ReadTest;
GO
ALTER DATABASE ReadTest
SET ALLOW_SNAPSHOT_ISOLATION OFF
GO
ALTER DATABASE ReadTest
SET READ_COMMITTED_SNAPSHOT OFF
GO
USE ReadTest
GO
CREATE TABLE [dbo].[TABLE_A](
[ROWGUIDE] [uniqueidentifier] NOT NULL,
[TRANSACTION_ID] [int] NOT NULL,
[COMMAND_ID] [int] NOT NULL,
[TYPE] [int] NOT NULL,
[STATUS] [int] NOT NULL,
[TEXT_FIELD] [varchar](4000) NULL
CONSTRAINT [PK_TABLE_A] PRIMARY KEY NONCLUSTERED
(
[ROWGUIDE] ASC
) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[TABLE_A] ADD DEFAULT (newsequentialid()) FOR [ROWGUIDE]
GO