Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Baca Tingkat Isolasi Tanpa Komitmen

[ Lihat indeks untuk keseluruhan seri ]

Baca tanpa komitmen adalah yang terlemah dari empat tingkat isolasi transaksi yang ditentukan dalam Standar SQL (dan dari enam yang diterapkan di SQL Server). Ini memungkinkan ketiga yang disebut "fenomena konkurensi", pembacaan kotor , pembacaan yang tidak dapat diulang , dan hantu:

Kebanyakan orang database menyadari fenomena ini, setidaknya secara garis besar, tetapi tidak semua orang menyadari bahwa mereka tidak sepenuhnya menggambarkan jaminan isolasi yang ditawarkan; mereka juga tidak secara intuitif menggambarkan perilaku berbeda yang dapat diharapkan dalam implementasi tertentu seperti SQL Server. Lebih lanjut tentang itu nanti.

Isolasi Transaksi – 'I' di ACID

Setiap perintah SQL dijalankan dalam suatu transaksi (eksplisit, implisit, atau komit otomatis). Setiap transaksi memiliki tingkat isolasi terkait, yang menentukan seberapa terisolasinya dari efek transaksi bersamaan lainnya. Konsep yang agak teknis ini memiliki implikasi penting untuk cara kueri dijalankan dan kualitas hasil yang dihasilkan.

Pertimbangkan kueri sederhana yang menghitung semua baris dalam tabel. Jika kueri ini dapat dieksekusi secara instan (atau tanpa modifikasi data bersamaan), hanya ada satu jawaban yang benar:jumlah baris yang ada secara fisik dalam tabel pada saat itu. Pada kenyataannya, mengeksekusi kueri akan memakan waktu tertentu, dan hasilnya akan bergantung pada berapa banyak baris yang benar-benar ditemukan oleh mesin eksekusi saat melintasi struktur fisik apa pun yang dipilih untuk mengakses data.

Jika baris ditambahkan ke (atau dihapus dari) tabel oleh transaksi bersamaan saat operasi penghitungan sedang berlangsung, hasil yang berbeda mungkin diperoleh tergantung pada apakah transaksi penghitungan baris menemukan semua, beberapa, atau tidak ada perubahan bersamaan – yang pada gilirannya tergantung pada tingkat isolasi transaksi penghitungan baris.

Bergantung pada tingkat isolasi, detail fisik, dan waktu operasi bersamaan, transaksi penghitungan kami bahkan dapat menghasilkan hasil yang tidak pernah benar-benar mencerminkan status komitmen tabel pada titik waktu mana pun selama transaksi.

Contoh

Pertimbangkan transaksi penghitungan baris yang dimulai pada waktu T1, dan memindai tabel dari awal hingga akhir (dalam urutan kunci indeks berkerumun, demi argumen). Pada saat itu, ada 100 baris berkomitmen dalam tabel. Beberapa waktu kemudian (pada waktu T2), transaksi penghitungan kami menemukan 50 baris tersebut. Pada saat yang sama, transaksi bersamaan memasukkan dua baris ke tabel, dan melakukan beberapa saat kemudian pada waktu T3 (sebelum transaksi penghitungan berakhir). Salah satu baris yang disisipkan kebetulan berada di dalam setengah dari struktur indeks berkerumun yang telah diproses oleh transaksi penghitungan kami, sementara baris yang disisipkan lainnya berada di bagian yang tidak terhitung.

Ketika transaksi penghitungan baris selesai, itu akan melaporkan 101 baris dalam skenario ini; 100 baris awalnya dalam tabel ditambah satu baris yang disisipkan yang ditemukan selama pemindaian. Hasil ini bertentangan dengan riwayat tabel yang berkomitmen:ada 100 baris berkomitmen pada waktu T1 dan T2, kemudian 102 baris berkomitmen pada waktu T3. Tidak pernah ada waktu ketika ada 101 baris yang berkomitmen.

Hal yang mengejutkan (mungkin, tergantung pada seberapa dalam Anda telah memikirkan hal-hal ini sebelumnya) adalah bahwa hasil ini dimungkinkan pada tingkat isolasi komitmen baca default (penguncian), dan bahkan di bawah isolasi baca berulang. Kedua tingkat isolasi tersebut dijamin hanya membaca data yang dikomit, namun kami memperoleh hasil yang menunjukkan tidak ada status komit dari database!

Analisis

Satu-satunya tingkat isolasi transaksi yang menyediakan isolasi lengkap dari efek konkurensi adalah serializable. Implementasi SQL Server dari tingkat isolasi serialisasi berarti transaksi akan melihat data komitmen terbaru, sejak data pertama kali dikunci untuk akses. Selain itu, kumpulan data yang ditemui di bawah isolasi serializable dijamin tidak akan mengubah keanggotaannya sebelum transaksi berakhir.

Contoh penghitungan baris menyoroti aspek mendasar dari teori basis data:kita harus jelas tentang apa arti hasil yang "benar" untuk basis data yang mengalami modifikasi bersamaan, dan kita perlu memahami pertukaran yang kita buat saat memilih isolasi tingkat lebih rendah dari serializable.

Jika kita memerlukan tampilan point-in-time dari status komitmen database, kita harus menggunakan isolasi snapshot (untuk jaminan tingkat transaksi) atau membaca isolasi snapshot berkomitmen (untuk jaminan tingkat pernyataan). Perhatikan bahwa tampilan point-in-time berarti kami tidak harus beroperasi pada kondisi database yang berkomitmen saat ini; pada dasarnya, kami mungkin menggunakan informasi yang kedaluwarsa. Di sisi lain, jika kami senang dengan hasil berdasarkan data yang dikomit saja (walaupun mungkin dari titik waktu yang berbeda), kami dapat memilih untuk tetap menggunakan tingkat isolasi baca berkomitmen penguncian default.

Untuk memastikan menghasilkan hasil (dan membuat keputusan!) berdasarkan kumpulan data komitmen terbaru, untuk beberapa riwayat operasi serial terhadap database, kita memerlukan isolasi transaksi yang dapat diserialisasi. Tentu saja opsi ini biasanya yang paling mahal dalam hal penggunaan sumber daya dan konkurensi yang lebih rendah (termasuk risiko kebuntuan yang lebih tinggi).

Dalam contoh penghitungan baris, kedua tingkat isolasi snapshot (SI dan RCSI) akan memberikan hasil 100 baris, yang mewakili jumlah baris yang dikomit di awal pernyataan (dan transaksi dalam kasus ini). Menjalankan kueri saat mengunci baca berkomitmen atau isolasi baca berulang dapat menghasilkan hasil 100, 101, atau 102 baris – bergantung pada waktu, perincian kunci, posisi penyisipan baris, dan metode akses fisik yang dipilih. Di bawah isolasi serial, hasilnya akan menjadi 100 atau 102 baris, tergantung pada mana dari dua transaksi bersamaan yang dianggap telah dieksekusi terlebih dahulu.

Seberapa Buruk Read Uncommitted?

Setelah memperkenalkan isolasi baca tanpa komitmen sebagai level isolasi terlemah yang tersedia, Anda seharusnya mengharapkannya untuk menawarkan jaminan isolasi yang lebih rendah daripada mengunci komitmen baca (level isolasi tertinggi berikutnya). Memang benar; tetapi pertanyaannya adalah:seberapa lebih buruk daripada mengunci baca, isolasi berkomitmen?

Jadi kita mulai dengan konteks yang benar, berikut adalah daftar efek konkurensi utama yang dapat dialami di bawah penguncian default SQL Server, baca tingkat isolasi berkomitmen:

  • Baris yang dikomit tidak ada
  • Baris ditemui beberapa kali
  • Versi berbeda dari baris yang sama ditemukan dalam satu rencana pernyataan/kueri
  • Data kolom yang dikomit dari titik waktu yang berbeda pada baris yang sama (contoh)

Efek konkurensi ini semua karena implementasi penguncian read commit hanya mengambil kunci bersama jangka pendek saat membaca data. Tingkat isolasi read uncommited melangkah lebih jauh, dengan tidak mengambil kunci bersama sama sekali, yang menghasilkan kemungkinan tambahan "pembacaan kotor".

Bacaan Kotor

Sebagai pengingat cepat, "bacaan kotor" mengacu pada membaca data yang sedang diubah oleh transaksi bersamaan lainnya (di mana "perubahan" menggabungkan operasi penyisipan, pembaruan, penghapusan, dan penggabungan). Dengan kata lain, pembacaan kotor terjadi ketika suatu transaksi membaca data yang telah dimodifikasi oleh transaksi lain, sebelum transaksi yang memodifikasi telah melakukan atau membatalkan perubahan tersebut.

Keuntungan dan Kerugian

Keuntungan utama dari isolasi read uncommited adalah berkurangnya potensi pemblokiran dan kebuntuan karena kunci yang tidak kompatibel (termasuk pemblokiran yang tidak perlu karena eskalasi penguncian), dan kemungkinan peningkatan kinerja (dengan menghindari kebutuhan untuk memperoleh dan melepaskan kunci bersama).

Kelemahan potensial yang paling jelas dari isolasi read uncommited adalah (seperti namanya) bahwa kita mungkin membaca data uncommited (bahkan data yang tidak pernah berkomitmen, dalam kasus rollback transaksi). Dalam database di mana rollback relatif jarang, pertanyaan tentang membaca data yang tidak dikomit mungkin dilihat sebagai masalah waktu belaka, karena data yang dipermasalahkan pasti akan dikomit pada tahap tertentu, dan mungkin segera. Kami telah melihat inkonsistensi terkait waktu dalam contoh penghitungan baris (yang beroperasi pada tingkat isolasi yang lebih tinggi) sehingga orang mungkin mempertanyakan seberapa besar kekhawatiran membaca data "terlalu cepat".

Jelas jawabannya tergantung pada prioritas dan konteks lokal, tetapi keputusan yang tepat untuk menggunakan isolasi baca tanpa komitmen tampaknya mungkin. Ada lebih untuk berpikir tentang meskipun. Implementasi SQL Server dari tingkat isolasi read uncommited mencakup beberapa perilaku halus yang perlu kita waspadai sebelum membuat "pilihan yang tepat".

Pemindaian Urutan Alokasi

Menggunakan isolasi baca tanpa komitmen dianggap oleh SQL Server sebagai sinyal bahwa kami siap menerima ketidakkonsistenan yang mungkin timbul sebagai hasil pemindaian dengan urutan alokasi.

Biasanya, mesin penyimpanan hanya dapat memilih pemindaian berdasarkan alokasi jika data dasarnya dijamin tidak akan berubah selama pemindaian (karena, misalnya, database hanya-baca, atau petunjuk penguncian tabel telah ditentukan). Namun, ketika isolasi membaca tanpa komitmen sedang digunakan, mesin penyimpanan masih dapat memilih pemindaian yang diurutkan berdasarkan alokasi bahkan jika data yang mendasarinya mungkin dimodifikasi oleh transaksi bersamaan.

Dalam keadaan ini, pemindaian yang diurutkan alokasi dapat melewatkan beberapa data yang dikomit sepenuhnya, atau menemukan data yang dikomit lainnya lebih dari sekali. Penekanannya ada pada komitmen yang hilang atau dihitung ganda data (tidak membaca data yang tidak dikomit) sehingga ini bukan kasus "pembacaan kotor" seperti itu. Keputusan desain ini (untuk mengizinkan pemindaian dengan urutan alokasi di bawah isolasi baca yang tidak terikat) dipandang oleh beberapa orang sebagai agak kontroversial.

Sebagai peringatan, saya harus menjelaskan bahwa risiko yang lebih umum dari kehilangan atau penghitungan ganda baris yang dikomit tidak terbatas pada membaca isolasi yang tidak dikomit. Hal ini tentu mungkin untuk melihat efek serupa di bawah penguncian membaca berkomitmen dan membaca berulang (seperti yang kita lihat sebelumnya) tetapi ini terjadi melalui mekanisme yang berbeda. Baris yang dikomit tidak ada atau menemuinya beberapa kali karena pemindaian berdasarkan alokasi atas perubahan data khusus untuk menggunakan isolasi baca tanpa komitmen.

Membaca Data "Korup"

Hasil yang tampaknya menentang logika (dan bahkan memeriksa batasan!) dimungkinkan di bawah penguncian baca yang dilakukan isolasi (sekali lagi, lihat artikel ini oleh Craig Freedman untuk beberapa contoh). Untuk meringkas, intinya adalah bahwa penguncian baca yang dilakukan dapat melihat data yang dikomit dari titik waktu yang berbeda – bahkan untuk satu baris jika, misalnya, rencana kueri menggunakan teknik seperti persimpangan indeks.

Hasil ini mungkin tidak terduga, tetapi sepenuhnya sesuai dengan jaminan untuk hanya membaca data yang dikomit. Fakta bahwa jaminan konsistensi data yang lebih tinggi memerlukan tingkat isolasi yang lebih tinggi.

Contoh-contoh itu bahkan mungkin cukup mengejutkan, jika Anda belum pernah melihatnya sebelumnya. Hasil yang sama dimungkinkan di bawah isolasi baca tanpa komitmen, tentu saja, tetapi mengizinkan pembacaan kotor menambah dimensi ekstra:hasilnya mungkin mencakup data yang dikomit dan tidak dikomit dari titik waktu yang berbeda, bahkan untuk baris yang sama.

Lebih jauh lagi, transaksi read uncommit bisa dilakukan untuk membaca nilai kolom tunggal dalam keadaan campuran data berkomitmen dan tidak terikat. Ini dapat terjadi saat membaca nilai LOB (misalnya, xml, atau salah satu jenis 'maks') jika nilai disimpan di beberapa halaman data. Pembacaan yang tidak dikomit dapat menemukan data yang dikomit atau tidak dikomit dari titik waktu yang berbeda pada halaman yang berbeda, menghasilkan nilai kolom tunggal akhir yang merupakan campuran nilai!

Sebagai contoh, pertimbangkan satu kolom varchar(max) yang awalnya berisi 10.000 karakter 'x'. Transaksi bersamaan memperbarui nilai ini menjadi 10.000 karakter 'y'. Transaksi read uncommited dapat membaca karakter 'x' dari satu halaman LOB, dan karakter 'y' dari halaman lain, menghasilkan nilai baca akhir yang berisi campuran karakter 'x' dan 'y'. Sulit untuk membantah bahwa ini tidak mewakili pembacaan data yang "rusak".

Demo

Buat tabel berkerumun dengan satu baris data LOB:

CREATE TABLE dbo.Test
(
    RowID integer PRIMARY KEY,
    LOB varchar(max) NOT NULL,
);
 
INSERT dbo.Test
    (RowID, LOB)
VALUES
    (1, REPLICATE(CONVERT(varchar(max), 'X'), 16100));

Dalam sesi terpisah, jalankan skrip berikut untuk membaca nilai LOB pada isolasi baca yang tidak terikat:

-- Run this in session 2
SET NOCOUNT ON;
 
DECLARE 
    @ValueRead varchar(max) = '',
    @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
    @AllYs varchar(max) = REPLICATE(CONVERT(varchar(max), 'Y'), 16100);
 
WHILE 1 = 1
BEGIN
    SELECT @ValueRead = T.LOB
    FROM dbo.Test AS T WITH (READUNCOMMITTED)
    WHERE T.RowID = 1;
 
    IF @ValueRead NOT IN (@AllXs, @AllYs)
    BEGIN
    	PRINT LEFT(@ValueRead, 8000);
        PRINT RIGHT(@ValueRead, 8000);
        BREAK;
    END
END;

Di sesi pertama, jalankan skrip ini untuk menulis nilai bergantian ke kolom LOB:

-- Run this in session 1
SET NOCOUNT ON;
 
DECLARE 
    @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
    @AllYs varchar(max) = REPLICATE(CONVERT(varchar(max), 'Y'), 16100);
 
WHILE 1 = 1
BEGIN
    UPDATE dbo.Test
    SET LOB = @AllYs
    WHERE RowID = 1;
 
    UPDATE dbo.Test
    SET LOB = @AllXs
    WHERE RowID = 1;
END;

Setelah beberapa saat, skrip di sesi dua akan berhenti, setelah membaca status campuran untuk nilai LOB, misalnya:

Masalah khusus ini terbatas pada pembacaan nilai kolom LOB yang tersebar di beberapa halaman, bukan karena jaminan apa pun yang diberikan oleh tingkat isolasi, tetapi karena SQL Server kebetulan menggunakan kait tingkat halaman untuk memastikan integritas fisik. Efek samping dari detail implementasi ini adalah mencegah pembacaan data yang "rusak" seperti itu jika data untuk operasi pembacaan tunggal berada pada satu halaman.

Bergantung pada versi SQL Server yang Anda miliki, jika data "keadaan campuran" dibaca untuk kolom xml, Anda akan mendapatkan kesalahan yang dihasilkan dari kemungkinan hasil xml yang salah, tidak ada kesalahan sama sekali, atau kesalahan khusus yang tidak dikomit 601 , "tidak dapat melanjutkan pemindaian dengan NOLOCK karena perpindahan data." Membaca data keadaan campuran untuk jenis LOB lain umumnya tidak menghasilkan pesan kesalahan; aplikasi atau kueri yang mengonsumsi tidak memiliki cara untuk mengetahui bahwa itu baru saja mengalami jenis pembacaan kotor terburuk. Untuk melengkapi analisis, baris keadaan campuran non-LOB yang dibaca sebagai hasil dari perpotongan indeks tidak pernah dilaporkan sebagai kesalahan.

Pesan di sini adalah bahwa jika Anda menggunakan isolasi baca yang tidak dikomit, Anda menerima bahwa pembacaan kotor mencakup kemungkinan membaca nilai LOB keadaan campuran yang "rusak".

Petunjuk NOLOCK

Saya kira tidak ada diskusi tentang tingkat isolasi baca yang tidak terikat yang akan lengkap tanpa setidaknya menyebutkan petunjuk tabel ini (banyak digunakan secara berlebihan dan disalahpahami). Petunjuk itu sendiri hanyalah sinonim dari petunjuk tabel READUNCOMMITTED. Ia melakukan fungsi yang persis sama:objek yang diterapkannya diakses menggunakan semantik isolasi read uncommited (meskipun ada pengecualian).

Sejauh menyangkut nama "NOLOCK", itu berarti tidak ada kunci bersama yang diambil saat membaca data . Kunci lainnya (stabilitas skema, kunci eksklusif untuk modifikasi data, dan sebagainya) masih digunakan seperti biasa.

Secara umum, petunjuk NOLOCK harus sama umum dengan petunjuk tabel tingkat isolasi per objek lainnya seperti SERIALIZABLE dan READCOMMITTEDLOCK. Artinya:tidak terlalu umum sama sekali, dan hanya digunakan jika tidak ada alternatif yang baik, tujuan yang jelas, dan lengkap pemahaman tentang konsekuensinya.

Salah satu contoh penggunaan NOLOCK (atau READUNCOMMITTED) yang sah adalah ketika mengakses DMV atau tampilan sistem lainnya, di mana tingkat isolasi yang lebih tinggi dapat menyebabkan pertentangan yang tidak diinginkan pada struktur data non-pengguna. Contoh kasus tepi lain mungkin di mana kueri perlu mengakses sebagian besar tabel besar, yang dijamin tidak akan pernah mengalami perubahan data saat kueri yang diisyaratkan dijalankan. Perlu ada alasan yang baik untuk tidak menggunakan snapshot atau membaca isolasi snapshot yang dilakukan sebagai gantinya, dan peningkatan kinerja yang diharapkan perlu diuji, divalidasi, dan dibandingkan dengan, katakanlah, menggunakan satu petunjuk kunci tabel bersama.

Penggunaan NOLOCK yang paling tidak diinginkan adalah yang sayangnya paling umum:menerapkannya ke setiap objek dalam kueri sebagai semacam sakelar ajaib yang lebih cepat. Dengan keinginan terbaik di dunia, tidak ada cara yang lebih baik untuk membuat kode SQL Server terlihat sangat amatir. Jika Anda secara sah perlu membaca isolasi tanpa komitmen untuk kueri, blok kode, atau modul, mungkin lebih baik untuk mengatur tingkat isolasi sesi dengan tepat, dan memberikan komentar untuk membenarkan tindakan tersebut.

Pemikiran Terakhir

Baca tanpa komitmen adalah pilihan yang sah untuk tingkat isolasi transaksi, tetapi itu perlu menjadi pilihan yang tepat. Sebagai pengingat, berikut adalah beberapa fenomena konkurensi yang mungkin terjadi di bawah penguncian default SQL Server, baca isolasi berkomitmen:

  • Baris yang sebelumnya dikomit tidak ada
  • Baris yang dikomit ditemukan beberapa kali
  • Versi commit yang berbeda dari baris yang sama ditemukan dalam satu pernyataan/rencana kueri
  • Data yang dikomit dari titik waktu yang berbeda pada baris yang sama (tetapi kolom berbeda)
  • Pembacaan data yang dilakukan yang tampaknya bertentangan dengan batasan yang diaktifkan dan diperiksa

Bergantung pada sudut pandang Anda, itu mungkin daftar kemungkinan inkonsistensi yang cukup mengejutkan untuk tingkat isolasi default. Untuk daftar itu, baca penambahan isolasi yang tidak terikat:

  • Pembacaan kotor (menemukan data yang belum, dan mungkin tidak akan pernah, dilakukan)
  • Baris yang berisi campuran data yang dikomit dan tidak dikomit
  • Baris yang terlewat/duplikat karena pemindaian berdasarkan alokasi
  • Nilai LOB individu (kolom tunggal) keadaan campuran ("korup")
  • Kesalahan 601 – "tidak dapat melanjutkan pemindaian dengan NOLOCK karena perpindahan data" (contoh).

Jika masalah transaksional utama Anda adalah tentang efek samping penguncian isolasi read-commit – pemblokiran, penguncian overhead, pengurangan konkurensi karena eskalasi penguncian, dan sebagainya – Anda mungkin lebih baik dilayani oleh tingkat isolasi versi baris seperti isolasi snapshot berkomitmen baca (RCSI) atau snapshot isolasi (SI). Namun, ini tidak gratis, dan pembaruan di bawah RCSI khususnya memiliki beberapa perilaku kontra-intuitif.

Untuk skenario yang menuntut tingkat jaminan konsistensi tertinggi, serializable tetap menjadi satu-satunya pilihan yang aman. Untuk operasi kritis-kinerja pada data hanya-baca (misalnya, database besar yang secara efektif hanya-baca di antara jendela ETL), secara eksplisit mengatur database ke READ_ONLY juga bisa menjadi pilihan yang baik (kunci bersama tidak diambil saat database hanya baca, dan tidak ada risiko inkonsistensi).

Juga akan ada sejumlah kecil aplikasi yang membaca isolasi tanpa komitmen adalah pilihan yang tepat. Aplikasi ini harus puas dengan hasil perkiraan dan kemungkinan data yang terkadang tidak konsisten, tampaknya tidak valid (dalam hal kendala), atau "bisa dibilang rusak". Jika data berubah relatif jarang, risiko inkonsistensi ini juga lebih rendah.

[ Lihat indeks untuk keseluruhan seri ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menguji Lapisan ODBC

  2. Cara Mudah Menyebarkan TimescaleDB

  3. Tutorial PL/SQL :Semua yang Perlu Anda Ketahui Tentang PL/SQL

  4. Unggah Dokumen ke Azure Data Lake dan Ekspor Data menggunakan SSIS

  5. SQL IN vs SQL ADA