Level isolasi transaksional untuk yang terkecil



Hari ini saya ingin membawa bagian yang sangat menarik, tetapi sering kali ditutupi dengan rahasia untuk programmer programmer biasa dari database (DB) - tingkat isolasi transaksi. Seperti yang diperlihatkan oleh praktik, banyak orang yang terkait dengan IT, khususnya yang bekerja dengan database, memiliki sedikit pemahaman tentang mengapa level ini diperlukan dan bagaimana mereka dapat digunakan untuk keuntungan mereka sendiri.

Sedikit teori


Transaksi itu sendiri tidak memerlukan penjelasan khusus, suatu transaksi adalah N (N≄1) query ke database yang akan dieksekusi dengan sukses bersama-sama atau tidak sama sekali. Isolasi transaksi menunjukkan seberapa besar transaksi paralel saling memengaruhi.
Memilih tingkat transaksi, kami mencoba untuk mencapai konsensus dalam pilihan antara konsistensi tinggi data antara transaksi dan kecepatan transaksi ini.
Perlu dicatat bahwa kecepatan eksekusi tertinggi dan konsistensi terendah dibaca tanpa komitmen . Kecepatan eksekusi terendah dan konsistensi tertinggi adalah serial .

Persiapan lingkungan


Sebagai contoh, DBMS MySQL dipilih. PostgreSQL juga dapat digunakan, tetapi tidak mendukung tingkat isolasi tanpa komitmen read , dan sebaliknya menggunakan level read commit . Dan ternyata, DBMS yang berbeda merasakan tingkat isolasi secara berbeda. Mereka dapat memiliki berbagai nuansa dalam memastikan isolasi, memiliki level tambahan atau belum terkenal.

Buat lingkungan menggunakan gambar MySQL yang sudah selesai dengan Docker Hub. Dan isi databasenya.

docker-compose.yaml
version: '3.4' services: db: image: mysql:8 environment: - MYSQL_ROOT_PASSWORD=12345 command: --init-file /init.sql volumes: - data:/var/lib/mysql - ./init.sql:/init.sql expose: - "3306" ports: - "3309:3306" volumes: data: 


Mengisi basis data
 create database if not exists bank; use bank; create table if not exists accounts ( id int unsigned auto_increment primary key, login varchar(255) not null, balance bigint default 0 not null, created_at timestamp default now() ) collate=utf8mb4_unicode_ci; insert into accounts (login, balance) values ('petya', 1000); insert into accounts (login, balance) values ('vasya', 2000); insert into accounts (login, balance) values ('mark', 500); 


Mari kita pertimbangkan bagaimana level bekerja dan fitur-fiturnya.
Kami akan mengeksekusi contoh pada 2 transaksi yang dijalankan secara bersamaan. Secara kondisional, transaksi di jendela kiri akan disebut transaksi 1 (T1), di jendela kanan - transaksi 2 (T2).

Baca tanpa komitmen


Tingkat dengan konsistensi data terburuk, tetapi kecepatan transaksi tertinggi. Nama level berbicara untuk dirinya sendiri - setiap transaksi melihat perubahan yang tidak terikat pada transaksi lain (fenomena pembacaan kotor ). Mari kita lihat bagaimana transaksi seperti itu saling mempengaruhi.

Langkah 1. Kami memulai 2 transaksi paralel.



Langkah 2. Kami melihat informasi apa yang kami miliki di awal.



Langkah 3. Sekarang kita melakukan operasi INSERT, DELETE, UPDATE di T1, dan melihat apa yang dilihat transaksi lain sekarang.



T2 melihat data dari transaksi lain yang belum dilakukan.

Langkah 4. Dan T2 bisa mendapatkan beberapa data.



Langkah 5. Saat Anda mengembalikan perubahan ke T1, data yang diterima oleh T2 akan salah.



Pada tingkat ini, tidak mungkin untuk menggunakan data berdasarkan kesimpulan dan keputusan kritis yang penting untuk aplikasi dibuat karena kesimpulan ini bisa jauh dari kenyataan.
Level ini dapat digunakan, misalnya, untuk perkiraan perhitungan sesuatu. Hasilnya COUNT (*) atau MAX (*) dapat digunakan dalam beberapa laporan yang tidak ketat.
Contoh lain adalah mode debug. Ketika selama transaksi, Anda ingin melihat apa yang terjadi pada basis data.

Baca berkomitmen


Untuk level ini, transaksi yang dilakukan secara bersamaan hanya melihat perubahan yang dilakukan dari transaksi lain. Dengan demikian, level ini memberikan perlindungan terhadap pembacaan yang kotor .

Langkah 1 dan Langkah 2 mirip dengan contoh sebelumnya.

Langkah 3. Kami juga melakukan 3 operasi sederhana dengan tabel akun (T1) dan membuat pilihan lengkap dari tabel ini di kedua transaksi.



Dan kita akan melihat bahwa fenomena membaca kotor tidak ada di T2.

Langkah 4. Kami memperbaiki perubahan di T1 dan memeriksa apa yang sekarang dilihat T2.



Sekarang T2 melihat semua yang telah dilakukan T1. Ini adalah fenomena yang disebut pembacaan tidak berulang ketika kita melihat baris yang diperbarui dan dihapus (UPDATE, DELETE), dan fenomena membaca hantu ketika kita melihat catatan tambahan (INSERT).

Baca berulang


Level untuk mencegah fenomena pembacaan yang tidak berulang . Yaitu kami tidak melihat catatan yang diubah dan dihapus dalam transaksi lain dengan transaksi lain. Tapi kami masih melihat catatan yang dimasukkan dari transaksi lain. Membaca hantu tidak kemana-mana.

Ulangi Langkah 1 dan Langkah 2 lagi.

Langkah 3. Di T1, kami menjalankan kueri INSERT, UPDATE, dan DELETE. Setelah itu, di T2 kami mencoba memperbarui baris yang sama dengan yang diperbarui di T1.



Dan kami mendapatkan kunci: T2 akan menunggu sampai T1 melakukan perubahan atau memutar kembali.

Langkah 4. Perbaiki perubahan yang dilakukan oleh T1. Dan baca lagi data dari tabel akun di T2.



Seperti yang Anda lihat, fenomena pembacaan yang tidak berulang dan pembacaan hantu tidak diamati. Bagaimana mungkin, secara default, bacaan berulang memungkinkan kita untuk mencegah hanya fenomena bacaan yang tidak berulang ?

Bahkan, MySQL tidak memiliki efek membaca hantu untuk tingkat membaca yang berulang . Dan di PostgreSQL, mereka juga menyingkirkannya untuk level ini. Meskipun dalam representasi klasik tingkat ini, kita harus mengamati efek ini.

Contoh abstrak kecil adalah layanan menghasilkan sertifikat hadiah (kode) dan penggunaannya. Misalnya, penyerang membuat kode sertifikat untuk dirinya sendiri dan mencoba mengaktifkannya, mencoba mengirim beberapa permintaan berturut-turut untuk mengaktifkan kupon. Dalam hal ini, kami akan memulai beberapa transaksi yang dilaksanakan bersamaan dengan kupon yang sama. Dan dalam beberapa situasi, aktivasi kupon dua kali atau bahkan tiga kali lipat dapat terjadi (pengguna akan menerima bonus 2x / 3x). Dengan pembacaan berulang, dalam hal ini, kunci akan terjadi dan aktivasi akan dilakukan satu kali, dan pada 2 level sebelumnya, beberapa aktivasi dimungkinkan. Masalah serupa juga dapat diselesaikan dengan menggunakan kueri SELECT FOR UPDATE , yang juga akan memblokir catatan yang diperbarui (kupon).

Serializable


Tingkat di mana transaksi berperilaku seolah-olah tidak ada yang lain, tidak ada pengaruh satu sama lain. Dalam representasi klasik, level ini menghilangkan efek membaca hantu .

Langkah 1. Mulai transaksi.

Langkah 2. T2 kita membaca tabel akun, lalu T1 kita mencoba memperbarui data yang dibaca oleh T2.



Kami mendapatkan kunci: kami tidak dapat mengubah data dalam satu transaksi baca di yang lain.

Langkah 3. INSERT dan DELETE membawa kita ke kunci di T1.



Sampai T2 menyelesaikan pekerjaannya, kami tidak akan dapat bekerja dengan data yang telah dibaca. Kami mendapatkan konsistensi data maksimum, tidak ada data tambahan yang akan direkam. Harga untuk ini adalah kecepatan transaksi yang lambat karena sering terkunci, jadi dengan arsitektur aplikasi yang buruk ini dapat memainkan trik pada Anda.

Kesimpulan


Di sebagian besar aplikasi, tingkat isolasi jarang berubah dan nilai default digunakan (misalnya, di MySQL itu dapat dibaca berulang-ulang , di PostgreSQL itu dibaca berkomitmen ).

Tetapi secara berkala ada masalah di mana menemukan keseimbangan yang lebih baik antara konsistensi data tinggi atau kecepatan transaksi dapat membantu menyelesaikan beberapa masalah aplikasi.

Source: https://habr.com/ru/post/id469415/


All Articles