Transaksi di InterSystems IRIS global

Transaksi di InterSystems IRIS globalDBMS IRIS InterSystems mendukung struktur menarik untuk menyimpan data - global. Pada dasarnya, ini adalah kunci multi-level dengan berbagai fasilitas tambahan dalam bentuk transaksi, fungsi cepat untuk melintasi pohon data, kunci, dan bahasa ObjectScript-nya sendiri.

Baca selengkapnya tentang global dalam rangkaian artikel “Global adalah pedang harta karun untuk menyimpan data”:

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

Saya menjadi tertarik dengan bagaimana transaksi diimplementasikan secara global, fitur apa saja yang ada. Bagaimanapun, ini adalah struktur penyimpanan data yang sama sekali berbeda dari tabel biasa. Tingkat yang jauh lebih rendah.

Sebagaimana diketahui dari teori database relasional, implementasi transaksi yang baik harus memenuhi persyaratan ACID:

A - Atom (atomisitas). Semua perubahan yang dilakukan dalam transaksi atau tidak sama sekali dicatat.

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

Saya - Mengisolasi. Transaksi yang berjalan secara paralel tidak boleh saling mempengaruhi.

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

Global adalah struktur data non-relasional. Mereka dirancang untuk berjalan super cepat pada perangkat keras yang sangat terbatas. Mari kita lihat implementasi transaksi secara global menggunakan gambar buruh pelabuhan IRIS resmi.

Untuk mendukung transaksi di IRIS digunakan perintah sebagai berikut: MULAI, TKOMIT, TROLLBACK.

1. Atomisitas

Cara termudah untuk memeriksanya adalah atomisitas. Kami memeriksa dari konsol database.

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)

Kita mendapatkan:

1 2 3

Semuanya baik-baik saja. Atomisitas dipertahankan: semua perubahan dicatat.

Mari kita perumit tugas, perkenalkan kesalahan dan lihat bagaimana transaksi disimpan, sebagian atau tidak sama sekali.

Mari kita periksa atomisitasnya lagi:

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

Kemudian kami akan menghentikan paksa wadah tersebut, meluncurkannya dan melihatnya.

docker kill my-iris

Perintah ini hampir setara dengan pematian paksa, karena mengirimkan sinyal SIGKILL untuk segera menghentikan proses.

Mungkin transaksinya terselamatkan sebagian?

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

- Tidak, itu tidak bertahan.

Mari kita coba 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 selamat juga.

2. Konsistensi

Karena dalam database berbasis global, kunci juga dibuat berdasarkan global (izinkan saya mengingatkan Anda bahwa global adalah struktur penyimpanan data tingkat lebih rendah daripada tabel relasional), untuk memenuhi persyaratan konsistensi, perubahan kunci harus disertakan dalam transaksi yang sama dengan perubahan global.

Misalnya, kita mempunyai orang global, yang di dalamnya kita menyimpan kepribadian dan kita menggunakan TIN sebagai kuncinya.

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

Untuk melakukan pencarian cepat berdasarkan nama belakang dan nama depan, kami membuat kunci ^index.

^index(‘Kamenev’, ‘Sergey’, 1234567) = 1

Agar databasenya konsisten, kita harus menambahkan persona seperti ini:

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

Oleh karena itu, 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 pundak programmer. Namun jika menyangkut global, hal ini wajar karena sifatnya yang levelnya rendah.

3. Isolasi

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

Situasinya sebanding dengan ketika banyak pengguna secara bersamaan bekerja dengan repositori kode yang sama dan mencoba melakukan perubahan secara bersamaan pada banyak file sekaligus.

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

Basis data tidak dapat memahami arti dari tindakan yang dilakukan oleh pengguna untuk menghindari konflik jika mereka mengerjakan data yang sama. Itu hanya dapat membatalkan satu transaksi yang bertentangan dengan transaksi lainnya, atau mengeksekusinya secara berurutan.

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

Saat melakukan transaksi secara paralel, penting bagi kami agar transaksi tersebut tidak saling mengganggu. Ini adalah sifat isolasi.

SQL mendefinisikan 4 tingkat isolasi:

  • BACA TANPA DIKOMITKAN
  • BACA BERKOMITMEN
  • BACAAN YANG DAPAT DIULANG
  • SERIALISASI

Mari kita lihat setiap level secara terpisah. Biaya penerapan setiap tingkat meningkat hampir secara eksponensial.

BACA TANPA DIKOMITKAN - ini adalah tingkat isolasi terendah, tetapi sekaligus tercepat. Transaksi dapat membaca perubahan yang dilakukan satu sama lain.

BACA BERKOMITMEN adalah tingkat isolasi berikutnya, yaitu kompromi. Transaksi tidak dapat membaca perubahan satu sama lain sebelum penerapan, namun dapat membaca perubahan apa pun yang dibuat setelah penerapan.

Jika kita memiliki transaksi panjang T1, yang selama itu dilakukan komit pada transaksi T2, T3 ... Tn, yang bekerja dengan data yang sama dengan T1, maka ketika meminta data di T1 kita akan mendapatkan hasil yang berbeda setiap kali. Fenomena ini disebut pembacaan yang tidak dapat diulang.

BACAAN YANG DAPAT DIULANG — di tingkat isolasi ini kita tidak memiliki fenomena pembacaan yang tidak dapat diulang, karena fakta bahwa untuk setiap permintaan untuk membaca data, snapshot dari data hasil dibuat dan ketika digunakan kembali dalam transaksi yang sama, data dari snapshot tersebut digunakan. Namun, data hantu dapat dibaca pada tingkat isolasi ini. Ini mengacu pada membaca baris baru yang ditambahkan oleh transaksi yang dilakukan secara paralel.

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

Pertama, mari kita cari tahu apakah ada isolasi operasi dalam suatu transaksi dari thread utama. Mari kita buka 2 jendela terminal.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Tidak ada isolasi. Satu thread melihat apa yang dilakukan thread kedua yang membuka transaksi.

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

Mari kita buka 2 jendela terminal dan buka 2 transaksi secara paralel.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Transaksi paralel melihat data satu sama lain. Jadi, kami mendapatkan tingkat isolasi yang paling sederhana, tetapi juga tercepat, READ UNCOMMITED.

Pada prinsipnya, hal ini dapat terjadi di negara-negara global, yang mana kinerja selalu menjadi prioritas.

Bagaimana jika kita memerlukan tingkat isolasi yang lebih tinggi dalam operasi global?

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

Tingkat isolasi tertinggi, SERIALIZE, berarti hasil transaksi yang dieksekusi secara paralel setara dengan eksekusi berurutannya, sehingga menjamin tidak adanya tabrakan.

Kita dapat melakukan ini menggunakan kunci pintar di ObjectScript, yang memiliki banyak kegunaan berbeda: Anda dapat melakukan penguncian berulang secara teratur, bertahap, dengan perintah LOCK.

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

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

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

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

Pemblokiran dua fase
Penguncian dua fase

Kesulitannya adalah 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 di mana status database akan konsisten. Dan semua akses ke jendela visibilitas negara yang disepakati akan dikontrol oleh kunci.

Kunci bersama pada data yang sama dapat digunakan kembali—beberapa proses dapat memerlukannya. Kunci ini mencegah proses lain mengubah data, mis. mereka digunakan untuk membentuk jendela dengan status database yang konsisten.

Kunci eksklusif digunakan untuk perubahan data - hanya satu proses yang dapat mengambil kunci tersebut. Kunci eksklusif dapat diambil dengan:

  1. Proses apa pun jika datanya gratis
  2. Hanya proses yang memiliki kunci bersama pada data ini dan merupakan yang pertama meminta kunci eksklusif.

Transaksi di InterSystems IRIS global

Semakin sempit jendela visibilitas, semakin lama proses lain harus menunggu, namun semakin konsisten keadaan database di dalamnya.

BACA_COMMITTED — inti dari level ini adalah kita hanya melihat data yang dikomit dari thread lain. Jika data dalam transaksi lain belum dikomit, maka kita melihat versi lamanya.

Hal ini memungkinkan kami untuk memparalelkan pekerjaan alih-alih menunggu kunci dibuka.

Tanpa trik khusus, kita tidak akan bisa melihat data versi lama di IRIS, jadi kita harus puas dengan kuncinya.

Oleh karena itu, kita harus menggunakan kunci bersama untuk memungkinkan data dibaca hanya pada saat-saat yang konsisten.

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

Momen perpindahan 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 permintaan sejumlah uang dari orang 123 sebelum pendebetan harus disertai dengan blok eksklusif (secara default):

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

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

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

Namun, jika kita berasumsi bahwa operasi database dilakukan hampir secara instan (izinkan saya mengingatkan Anda bahwa global adalah struktur tingkat yang jauh lebih rendah daripada tabel relasional), maka kebutuhan akan tingkat ini berkurang.

BACAAN YANG DAPAT DIULANG - Tingkat isolasi ini memungkinkan beberapa pembacaan data yang dapat dimodifikasi dengan transaksi bersamaan.

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

Untungnya, operator LOCK memungkinkan Anda membuat daftar secara rinci semua kunci yang diperlukan, yang jumlahnya bisa banyak, dalam satu pernyataan.

LOCK +^person(123, amount)#”S”
чтение ^person(123, amount)

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

LOCK +^person(123, amount)
изменение ^person(123, amount)
LOCK -^person(123, amount)

чтение ^person(123, amount)
LOCK -^person(123, amount)#”S”

Saat mencantumkan kunci yang dipisahkan dengan koma, kunci tersebut diambil secara berurutan, tetapi jika Anda melakukan ini:

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

kemudian mereka diambil secara atom sekaligus.

SERIALISASI — kita harus menyetel kunci sehingga pada akhirnya semua transaksi yang memiliki data umum dieksekusi secara berurutan. Untuk pendekatan ini, sebagian besar kunci harus bersifat eksklusif dan diterapkan pada area terkecil di dunia untuk mendapatkan kinerja.

Jika kita berbicara tentang mendebit dana di global ^person, maka hanya tingkat isolasi SERIALIZE yang dapat diterima, karena uang harus dibelanjakan secara berurutan, jika tidak, jumlah yang sama dapat dibelanjakan beberapa kali.

4. Daya Tahan

Saya melakukan pengujian dengan memotong wadah secara keras menggunakan

docker kill my-iris

Pangkalan menoleransi mereka dengan baik. Tidak ada masalah yang teridentifikasi.

Kesimpulan

Untuk global, InterSystems IRIS memiliki dukungan transaksi. Mereka benar-benar atom dan dapat diandalkan. Untuk memastikan konsistensi database berdasarkan global, diperlukan upaya pemrogram dan penggunaan transaksi, karena database tersebut tidak memiliki konstruksi bawaan yang rumit seperti kunci asing.

Tingkat isolasi global tanpa menggunakan kunci adalah READ UNCOMMITED, dan ketika menggunakan kunci dapat dipastikan hingga tingkat SERIALIZE.

Kebenaran dan kecepatan transaksi di global sangat bergantung pada keterampilan pemrogram: semakin banyak kunci bersama yang digunakan saat membaca, semakin tinggi tingkat isolasi, dan semakin sempit kunci eksklusif yang diambil, semakin cepat kinerjanya.

Sumber: www.habr.com

Tambah komentar