InterSystems, Transaksi Global IRIS

IRIS dan transaksi AntarSistem Antar Sistem IRIS DBMS mendukung struktur penyimpanan data yang aneh - global. Bahkan, ini adalah kunci multi-level dengan berbagai keuntungan tambahan dalam bentuk transaksi, fungsi cepat untuk melintasi pohon data, kunci dan bahasa ObjectScript mereka sendiri.

Lebih lanjut tentang global dalam seri artikel β€œGlobal - Pedang-Mason untuk Penyimpanan Data”:

Pohon-pohon. Bagian 1
Pohon-pohon. Bagian 2
Array yang jarang. Bagian 3

Menjadi menarik bagi saya bagaimana transaksi diimplementasikan di global, fitur apa saja yang ada. Bagaimanapun, ini adalah struktur yang sama sekali berbeda untuk menyimpan data dari tabel yang biasa. Tingkat yang jauh lebih rendah.

Seperti yang Anda ketahui dari teori basis data relasional, implementasi transaksi yang baik harus memenuhi persyaratan ACID :

A - Atomic (atomicity). Semua perubahan yang dilakukan pada transaksi atau tidak ada sama sekali dicatat.

C - Konsistensi. Setelah transaksi selesai, keadaan logis dari basis data harus konsisten secara internal. Dalam banyak hal, persyaratan ini berlaku untuk programmer, tetapi dalam kasus database SQL, ini juga berlaku untuk kunci asing.

I - Isolate (isolasi). Transaksi paralel seharusnya tidak saling mempengaruhi.

D - Tahan lama. Setelah transaksi berhasil diselesaikan, masalah di tingkat yang lebih rendah (kegagalan daya, misalnya) seharusnya tidak mempengaruhi data yang diubah oleh transaksi.

Global adalah struktur data non-relasional. Mereka diciptakan untuk pekerjaan ultrafast pada perangkat keras yang sangat terbatas. Mari kita memahami implementasi transaksi di global menggunakan gambar docker IRIS resmi .

Untuk mendukung transaksi dalam IRIS, perintah berikut digunakan: TSTART , TCOMMIT , TROLLBACK .

1. Atomicity


Cara termudah untuk memeriksa atomicity. Memeriksa dari konsol basis data.

Kill ^a TSTART Set ^a(1) = 1 Set ^a(2) = 2 Set ^a(3) = 3 TCOMMIT 

Kemudian kami menyimpulkan:

 Write ^a(1), β€œ ”, ^a(2), β€œ ”, ^a(3) 

Kami mendapatkan:

 1 2 3 

Semuanya baik-baik saja. Atomicity teramati: semua perubahan dicatat.

Kami menyulitkan tugas, menyebabkan kesalahan dan melihat bagaimana transaksi disimpan, sebagian atau tidak sama sekali.

Mari kita periksa atomicity sekali lagi:

 Kill ^A TSTART Set ^a(1) = 1 Set ^a(2) = 2 Set ^a(3) = 3 

Kemudian dengan paksa menghentikan wadah, mulai dan lihat.

 docker kill my-iris 

Perintah ini hampir sama dengan mematikan daya secara paksa, karena mengirimkan sinyal untuk segera menghentikan proses SIGKILL.

Mungkin transaksi itu sebagian disimpan?

 WRITE ^a(1), ^a(2), ^a(3) ^ <UNDEFINED> ^a(1) 

- Tidak, tidak diawetkan.

Uji perintah rollback:

 Kill ^A TSTART Set ^a(1) = 1 Set ^a(2) = 2 Set ^a(3) = 3 TROLLBACK WRITE ^a(1), ^a(2), ^a(3) ^ <UNDEFINED> ^a(1) 

Tidak ada yang dipertahankan.

2. Konsistensi


Karena dalam basis data global, kunci juga dibuat pada global (saya ingat bahwa global adalah struktur tingkat lebih rendah untuk menyimpan data dari tabel relasional), untuk memenuhi persyaratan konsistensi, Anda harus memasukkan perubahan kunci dalam transaksi yang sama dengan perubahan global.

Sebagai contoh, kami memiliki orang global tempat kami menyimpan kepribadian dan kami menggunakan TIN sebagai kunci.

 ^person(1234567, 'firstname') = 'Sergey' ^person(1234567, 'lastname') = 'Kamenev' ^person(1234567, 'phone') = '+74995555555 ... 

Untuk melakukan pencarian cepat dengan nama belakang dan nama depan, kami membuat kunci ^ indeks.

 ^index('Kamenev', 'Sergey', 1234567) = 1 

Agar basis disetujui, kita harus menambahkan kepribadian seperti ini:

 TSTART ^person(1234567, 'firstname') = 'Sergey' ^person(1234567, 'lastname') = 'Kamenev' ^person(1234567, 'phone') = '+74995555555 ^index('Kamenev', 'Sergey', 1234567) = 1 TCOMMIT 

Karenanya, saat menghapus, kita juga harus menggunakan transaksi:

 TSTART Kill ^person(1234567) ZKill ^index('Kamenev', 'Sergey', 1234567) TCOMMIT 

Dengan kata lain, pemenuhan persyaratan konsistensi sepenuhnya berada di tangan programmer. Tetapi ketika datang ke global, ini normal, karena sifatnya yang rendah.

3. Isolasi


Di sinilah alam liar dimulai. Banyak pengguna secara bersamaan bekerja pada database yang sama, memodifikasi data yang sama.

Situasi ini sebanding dengan situasi ketika banyak pengguna secara bersamaan bekerja dengan repositori yang sama dengan kode dan mencoba untuk melakukan perubahan pada banyak file sekaligus di dalamnya.

Basis data harus menyelesaikan ini secara real time. Mengingat bahwa di perusahaan yang serius bahkan ada orang khusus yang bertanggung jawab untuk kontrol versi (untuk menggabungkan cabang, menyelesaikan konflik, dll.), Dan database harus melakukan semua ini secara real time, kompleksitas tugas dan desain database yang benar dan kode yang menyajikannya.

Basis data tidak dapat memahami arti dari tindakan yang dilakukan oleh pengguna untuk mencegah konflik jika mereka bekerja pada data yang sama. Itu hanya dapat membatalkan satu transaksi yang bertentangan dengan yang lain atau menjalankannya secara berurutan.

Masalah lain adalah bahwa selama pelaksanaan transaksi (sebelum komit), keadaan database mungkin tidak konsisten, sehingga diharapkan bahwa transaksi lain tidak memiliki akses ke keadaan tidak konsisten dari database, yang dicapai dalam database relasional dalam banyak cara: membuat snapshot, baris multiversion dan dll.

Dalam pelaksanaan transaksi paralel, penting bagi kami bahwa mereka tidak saling mengganggu. Ini adalah properti isolasi.

SQL mendefinisikan 4 level isolasi:

  • BACA TIDAK DIKOMITMASI
  • BACA BERKOMITMEN
  • BACA ULANG
  • SERIALISASI

Mari kita pertimbangkan setiap level secara terpisah. Biaya pelaksanaan setiap tingkat tumbuh hampir secara eksponensial.

READ UNCOMMITTED adalah tingkat isolasi terendah, tetapi tercepat. Transaksi dapat membaca perubahan yang dilakukan oleh satu sama lain.

READ COMMITTED adalah tingkat isolasi berikutnya, yang merupakan kompromi. Transaksi tidak dapat membaca perubahan yang dilakukan oleh satu sama lain sebelum komit, tetapi dapat membaca setiap perubahan yang dilakukan setelah komit.

Jika kita memiliki transaksi panjang T1, di mana ada komit dalam transaksi T2, T3 ... Tn yang bekerja dengan data yang sama dengan T1, maka ketika kita meminta data di T1, kita akan mendapatkan hasil yang berbeda setiap kali. Fenomena ini disebut bacaan non-berulang.

REPEATABLE READ - di tingkat isolasi ini, kami tidak memiliki fenomena pembacaan yang tidak dapat diulang, karena fakta bahwa untuk setiap permintaan membaca data, snapshot dari data hasil dibuat dan ketika digunakan kembali dalam transaksi yang sama, data dari snapshot digunakan. Namun, pada tingkat isolasi ini, data hantu dapat dibaca. Ini mengacu pada membaca baris baru yang ditambahkan oleh transaksi yang dilakukan bersamaan.

SERIALIZABLE adalah tingkat isolasi tertinggi. Hal ini ditandai dengan fakta bahwa data yang digunakan dengan cara apa pun dalam transaksi (membaca atau mengubah) menjadi tersedia untuk transaksi lain hanya setelah penyelesaian transaksi pertama.

Pertama, mari kita cari tahu apakah ada isolasi operasi dalam transaksi dari utas utama. Mari kita buka 2 terminal windows.
 Kill ^t Write ^t(1) 2 
 TSTART Set ^t(1)=2 

Tidak ada isolasi. Satu utas melihat apa yang dilakukan oleh orang kedua yang membuka transaksi.

Mari kita lihat apakah transaksi dari aliran yang berbeda melihat apa yang terjadi di dalamnya.

Kami membuka 2 terminal windows dan membuka 2 transaksi secara paralel.
 kill ^t TSTART Write ^t(1) 3 
 TSTART Set ^t(1)=3 

Transaksi bersamaan melihat data masing-masing. Jadi, kami mendapatkan yang paling sederhana, tetapi juga tingkat isolasi tercepat.

Pada prinsipnya, ini bisa diharapkan untuk orang-orang global, yang kecepatan selalu menjadi yang terpenting.

Tetapi bagaimana jika kita membutuhkan tingkat isolasi yang lebih tinggi dalam operasi global?

Di sini Anda perlu memikirkan mengapa tingkat isolasi diperlukan dan bagaimana cara kerjanya.

Level isolasi tertinggi SERIALIZE berarti bahwa hasil transaksi yang dilakukan bersamaan adalah setara dengan eksekusi berurutannya, yang menjamin tidak adanya tabrakan.

Kita dapat melakukan ini dengan bantuan kunci yang kompeten di ObjectScript, yang memiliki banyak cara aplikasi yang berbeda: Anda dapat melakukan beberapa kunci biasa, bertahap, banyak dengan perintah LOCK .

Level isolasi yang lebih rendah merupakan trade-off yang dirancang untuk meningkatkan kecepatan database.

Mari kita lihat bagaimana kita dapat mencapai tingkat isolasi yang berbeda menggunakan kunci.

Operator ini memungkinkan Anda untuk mengambil tidak hanya kunci eksklusif yang diperlukan untuk mengubah data, tetapi juga yang disebut bersama, yang dapat mengambil beberapa utas sekaligus, ketika mereka perlu membaca data yang tidak boleh diubah oleh proses lain selama membaca.

Informasi lebih lanjut tentang metode penguncian dua fase dalam bahasa Rusia dan Inggris:

β†’ Kunci dua fase
β†’ Penguncian dua fase

Kesulitannya adalah bahwa selama transaksi keadaan database mungkin tidak konsisten, namun, data yang tidak konsisten ini terlihat oleh proses lain. Bagaimana cara menghindarinya?

Dengan menggunakan kunci, kita akan membuat jendela visibilitas seperti itu di mana keadaan basis data akan disepakati. Dan semua panggilan ke jendela visibilitas seperti negara yang disepakati akan dikendalikan oleh kunci.

Kunci bersama dari data yang sama dapat digunakan kembali - beberapa proses dapat mengambilnya. Kunci ini mencegah proses lain dari mengubah data, mis. mereka digunakan untuk membentuk windows dari kondisi database yang konsisten.

Kunci eksklusif digunakan untuk memodifikasi data - hanya satu proses yang dapat mengambil kunci tersebut. Pemblokiran eksklusif dapat dilakukan:

  1. Segala proses jika data gratis
  2. Hanya proses yang memiliki kunci bersama pada data ini dan yang pertama meminta kunci eksklusif.



Semakin sempit jendela visibilitas, semakin lama waktu yang dibutuhkan untuk menunggu proses lain, tetapi semakin konsisten kondisi database di dalamnya.

READ_COMMITED - esensi level ini adalah bahwa kami hanya melihat data dari aliran lain yang dikunci. Jika data dalam transaksi lain belum dilakukan, maka kami melihat versi lamanya.

Ini memungkinkan kita untuk memaralelkan pekerjaan alih-alih menunggu kunci dilepaskan.

Tanpa trik khusus, kita tidak akan dapat melihat versi lama data di IRIS, jadi kita harus lakukan dengan kunci.

Oleh karena itu, kita harus menggunakan kunci bersama untuk memungkinkan pembacaan data hanya pada saat-saat konsistensi.

Misalkan kita memiliki basis pengguna ^ orang yang saling mentransfer uang.

Momen pemindahan dari orang 123 ke orang 242:

 LOCK +^person(123), +^person(242) Set ^person(123, amount) = ^person(123, amount) - amount Set ^person(242, amount) = ^person(242, amount) + amount LOCK -^person(123), -^person(242) 

Momen meminta jumlah uang dari orang 123 sebelum mendebit harus disertai dengan kunci eksklusif (secara default):

 LOCK +^person(123) Write ^person(123) 

Dan jika Anda perlu menunjukkan status akun di akun Anda, Anda dapat menggunakan kunci bersama atau tidak menggunakannya sama sekali:

 LOCK +^person(123)#”S” Write ^person(123) 

Namun, jika kita mengasumsikan bahwa operasi database dilakukan hampir secara instan (saya ingat bahwa global adalah struktur level yang jauh lebih rendah daripada tabel relasional), maka kebutuhan untuk level ini turun.

REPEATABLE READ - Dalam tingkat isolasi ini, diasumsikan bahwa ada beberapa pembacaan data yang dapat dimodifikasi oleh transaksi bersamaan.

Oleh karena itu, kita harus meletakkan kunci bersama pada pembacaan data yang kita ubah dan kunci eksklusif pada data yang kita ubah.

Untungnya, operator LOCK memungkinkan satu operator untuk mendaftar secara terperinci semua kunci yang diperlukan, yang bisa sangat banyak.

 LOCK +^person(123, amount)#”S”  ^person(123, amount) 

operasi lain (saat ini, utas paralel mencoba mengubah ^ orang (123, jumlah), tetapi tidak dapat)

 LOCK +^person(123, amount)  ^person(123, amount) LOCK -^person(123, amount)  ^person(123, amount) LOCK -^person(123, amount)#”S” 

Saat daftar kunci dipisahkan oleh koma, mereka diambil secara berurutan, dan jika Anda melakukannya:

 LOCK +(^person(123),^person(242)) 

maka mereka diambil secara atom sekaligus.

SERIALIZE - kita harus mengatur kunci sehingga pada akhirnya semua transaksi yang memiliki data umum dieksekusi secara berurutan. Untuk pendekatan ini, sebagian besar kunci harus eksklusif dan dibawa ke area terkecil global untuk kinerja.

Jika kita berbicara tentang penghapusbukuan orang global, maka hanya tingkat isolasi SERIALIZE yang dapat diterima untuknya, karena uang harus dihabiskan secara ketat secara berurutan, jika tidak mungkin untuk menghabiskan jumlah yang sama beberapa kali.

4. Daya tahan


Saya melakukan tes dengan keras memotong wadah

 docker kill my-iris 

Pangkalan itu menoleransi mereka dengan baik. Tidak ada masalah yang diidentifikasi.

Kesimpulan


Untuk global, InterSystems IRIS memiliki dukungan transaksi. Mereka benar-benar atom, dapat diandalkan. Untuk memastikan konsistensi basis data pada global, upaya programmer dan penggunaan transaksi diperlukan, karena tidak ada konstruksi built-in yang kompleks seperti kunci asing.

Level isolasi global tanpa menggunakan kunci DIBACA TIDAK DIKUNCI, dan ketika menggunakan kunci, dapat dipastikan hingga tingkat SERIALIZE.

Kebenaran dan kecepatan transaksi pada global sangat tergantung pada keterampilan programmer: semakin banyak kunci yang digunakan bersama saat membaca, semakin tinggi tingkat isolasi, dan semakin eksklusif kunci yang diambil, semakin besar kecepatannya.

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


All Articles