Bagikan melalui


Tingkat isolasi dan konflik penulisan di Azure Databricks

Tingkat isolasi tabel menentukan sejauh mana transaksi harus diisolasi dari modifikasi yang dilakukan oleh operasi bersamaan. Konflik penulisan di Azure Databricks bergantung pada tingkat isolasi.

Delta Lake memberikan jaminan transaksi ACID antara membaca dan menulis. Ini berarti bahwa:

  • Beberapa penulis di beberapa kluster dapat secara bersamaan memodifikasi partisi tabel. Penulis melihat tampilan snapshot tabel yang konsisten dan penulisan dilakukan dalam urutan serial.
    • Pembaca terus melihat tampilan snapshot yang konsisten dari tabel yang menjadi awal pekerjaan di Azure Databricks, bahkan ketika tabel dimodifikasi selama pelaksanaan pekerjaan tersebut.

Lihat Apa itu jaminan ACID di Azure Databricks?.

Catatan

Azure Databricks menggunakan Delta Lake untuk semua tabel secara default. Artikel ini menjelaskan perilaku untuk Delta Lake di Azure Databricks.

Penting

Perubahan metadata menyebabkan semua operasi tulis bersamaan gagal. Operasi ini mencakup perubahan pada protokol tabel, properti tabel, atau skema data.

Pembacaan streaming gagal ketika menemukan commit yang mengubah metadata tabel. Jika Anda ingin aliran berlanjut, Anda harus memulai ulang. Untuk metode yang direkomendasikan, lihat Pertimbangan produksi untuk Streaming Terstruktur.

Berikut ini adalah contoh kueri yang mengubah metadata:

-- Set a table property.
ALTER TABLE table-name SET TBLPROPERTIES ('delta.isolationLevel' = 'Serializable')

-- Enable a feature using a table property and update the table protocol.
ALTER TABLE table_name SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);

-- Drop a table feature.
ALTER TABLE table_name DROP FEATURE deletionVectors;

-- Upgrade to UniForm.
REORG TABLE table_name APPLY (UPGRADE UNIFORM(ICEBERG_COMPAT_VERSION=2));

-- Update the table schema.
ALTER TABLE table_name ADD COLUMNS (col_name STRING);

-- Remove column mapping (rewrites all files).
ALTER TABLE table_name SET TBLPROPERTIES ('delta.columnMapping.mode' = 'none')

Menulis konflik dengan konkurensi tingkat baris

Konkurensi tingkat baris mengurangi konflik antara operasi tulis bersamaan dengan mendeteksi perubahan di tingkat baris dan secara otomatis menyelesaikan konflik yang terjadi saat penulisan bersamaan memperbarui atau menghapus baris yang berbeda dalam file data yang sama.

Konkurensi tingkat baris umumnya tersedia pada Databricks Runtime 14.2 ke atas. Konkurensi tingkat baris didukung secara default untuk kondisi berikut:

  • Tabel yang vektor penghapusannya diaktifkan dan tanpa partisi.
  • Tabel dengan pengelompokan cairan, kecuali jika Anda telah menonaktifkan vektor penghapusan.

Tabel dengan partisi tidak mendukung konkurensi tingkat baris tetapi masih dapat menghindari konflik antara OPTIMIZE dan semua operasi tulis lainnya saat vektor penghapusan diaktifkan. Lihat Batasan untuk konkurensi tingkat baris.

Untuk versi Runtime Databricks lainnya, lihat Perilaku pratinjau konkurensi tingkat baris (warisan).

MERGE INTO dukungan konkurensi tingkat baris memerlukan Photon di Databricks Runtime 14.2. Dalam Databricks Runtime 14.3 LTS ke atas, Photon tidak diperlukan.

Tabel berikut menjelaskan pasangan operasi tulis mana yang dapat berkonflik di setiap tingkat isolasi dengan konkurensi tingkat baris diaktifkan.

Catatan

Tabel dengan kolom identitas tidak mendukung transaksi bersamaan. Lihat Menggunakan kolom identitas di Delta Lake.

INSERT (1) UPDATE, HAPUS, MERGE INTO OPTIMIZE
INSERT Tidak bisa berkonflik
UPDATEMENGHAPUS MERGE INTO Tidak dapat berkonflik dalam WriteSerializable. Dapat berkonflik di Serializable saat memodifikasi baris yang sama. Lihat Batasan untuk konkurensi tingkat baris. Dapat berkonflik saat mengubah baris yang sama. Lihat Batasan untuk konkurensi tingkat baris.
OPTIMIZE Tidak bisa berkonflik Dapat berkonflik saat ZORDER BY digunakan. Tidak dapat berkonflik dengan cara lain. Dapat berkonflik saat ZORDER BY digunakan. Tidak dapat berkonflik dengan cara lain.

Penting

(1) Semua INSERT operasi dalam tabel di atas menjelaskan operasi penambahan yang tidak membaca data apa pun dari tabel yang sama sebelum melakukan. INSERT operasi yang berisi subkueri yang membaca tabel yang sama mendukung konkurensi yang sama dengan MERGE.

REORG operasi memiliki semantik isolasi yang sama dengan OPTIMIZE ketika menulis ulang file data untuk mencerminkan perubahan yang dicatat dalam vektor penghapusan. Saat Anda menggunakan REORG untuk menerapkan peningkatan, protokol tabel berubah, yang bertentangan dengan semua operasi yang sedang berlangsung.

Menulis konflik tanpa konkurensi tingkat baris

Tabel berikut menjelaskan pasangan operasi tulis mana yang dapat bertentangan di setiap tingkat isolasi.

Tabel tidak mendukung konkurensi tingkat baris jika memiliki partisi yang ditentukan atau tidak mengaktifkan vektor penghapusan. Databricks Runtime 14.2 atau lebih tinggi diperlukan untuk konkurensi tingkat baris.

Catatan

Tabel dengan kolom identitas tidak mendukung transaksi bersamaan. Lihat Menggunakan kolom identitas di Delta Lake.

INSERT (1) UPDATE, HAPUS, MERGE INTO OPTIMIZE
INSERT Tidak bisa berkonflik
UPDATEMENGHAPUS MERGE INTO Tidak dapat berkonflik dalam WriteSerializable. Dapat berkonflik dalam Serializable. Lihat menghindari konflik dengan partisi. Dapat berkonflik dalam Serializable dan WriteSerializable. Lihat menghindari konflik dengan partisi.
OPTIMIZE Tidak bisa berkonflik Tidak dapat bertentangan dengan dalam tabel dengan vektor penghapusan diaktifkan, kecuali ZORDER BY digunakan. Bisa berkonflik dalam kondisi lain. Tidak dapat bertentangan dengan dalam tabel dengan vektor penghapusan diaktifkan, kecuali ZORDER BY digunakan. Bisa berkonflik dalam kondisi lain.

Penting

(1) Semua INSERT operasi dalam tabel di atas menjelaskan operasi penambahan yang tidak membaca data apa pun dari tabel yang sama sebelum melakukan. INSERT operasi yang berisi subkueri yang membaca tabel yang sama mendukung konkurensi yang sama dengan MERGE.

REORG operasi memiliki semantik isolasi yang sama dengan OPTIMIZE ketika menulis ulang file data untuk mencerminkan perubahan yang dicatat dalam vektor penghapusan. Saat Anda menggunakan REORG untuk menerapkan peningkatan, protokol tabel berubah, yang bertentangan dengan semua operasi yang sedang berlangsung.

Batasan untuk konkurensi tingkat baris

Beberapa batasan berlaku untuk konkurensi tingkat baris. Untuk operasi berikut, penyelesaian konflik mengikuti kesamaan waktu normal untuk konflik penulisan di Azure Databricks. Lihat Menulis konflik tanpa konkurensi tingkat baris.

  • Perintah dengan klausa kondisional yang kompleks, termasuk yang berikut ini:
    • Kondisi pada jenis data kompleks seperti struktur, array, atau peta.
    • Kondisi yang menggunakan ekspresi dan subkueri non-deterministik.
    • Kondisi yang berisi subkueri berkorelasi.
  • Di Databricks Runtime 14.2, MERGE perintah harus menggunakan predikat eksplisit pada tabel target untuk memfilter baris yang cocok dengan tabel sumber. Untuk resolusi penggabungan, filter memindai hanya baris-baris yang mungkin bertentangan berdasarkan kondisi filter selama operasi bersamaan.

Catatan

Deteksi konflik tingkat baris dapat meningkatkan total waktu eksekusi. Dalam kasus banyak transaksi bersamaan, penulis memprioritaskan latensi atas penyelesaian konflik dan konflik dapat terjadi.

Semua batasan untuk vektor penghapusan juga berlaku. Lihat Batasan.

Kapan Delta Lake berkomitmen tanpa membaca tabel?

Delta Lake INSERT atau operasi tambahan tidak membaca status tabel sebelum melakukan jika kondisi berikut ini terpenuhi:

  1. Logika diekspresikan menggunakan INSERT logika SQL atau mode tambahan.
  2. Logika tidak berisi subkueri atau kondisi yang mereferensikan tabel yang ditargetkan oleh operasi tulis.

Seperti dalam penerapan lain, Delta Lake memvalidasi dan menyelesaikan versi tabel pada penerapan menggunakan metadata dalam log transaksi, tetapi tidak ada versi tabel yang benar-benar dibaca.

Catatan

Banyak pola umum menggunakan MERGE operasi untuk menyisipkan data berdasarkan kondisi tabel. Meskipun mungkin untuk menulis ulang logika ini menggunakan INSERT pernyataan, jika ada ekspresi kondisional yang mereferensikan kolom dalam tabel target, pernyataan ini memiliki batasan konkurensi yang sama dengan MERGE.

Menulis tingkat isolasi yang dapat diserialisasi vs. yang dapat diserialisasi

Tingkat isolasi tabel menentukan sejauh mana transaksi harus diisolasi dari modifikasi yang dilakukan oleh transaksi bersamaan. Delta Lake di Azure Databricks mendukung dua tingkat isolasi: Serializable dan WriteSerializable.

  • Serializable: Tingkat isolasi terkuat. Ini memastikan bahwa operasi tulis yang telah dilakukan dan semua pembacaan adalah Serializable. Operasi diperbolehkan selama ada urutan serial mengeksekusinya satu per satu yang menghasilkan hasil yang sama seperti yang terlihat dalam tabel. Untuk operasi penulisan, urutan serial sama persis dengan yang terlihat dalam riwayat tabel.

  • WriteSerializable (Default): Tingkat isolasi yang lebih lemah daripada Serializable. Ini memastikan hanya operasi tulis (yaitu, bukan baca) yang dapat diserialisasi. Namun, ini masih lebih kuat dari isolasi Snapshot. WriteSerializable adalah tingkat isolasi default karena memberikan keseimbangan yang sangat baik antara konsistensi data dan ketersediaan untuk operasi yang paling umum.

    Dalam mode ini, konten tabel Delta mungkin berbeda dari yang diharapkan dari urutan operasi yang terlihat dalam riwayat tabel. Hal ini dikarenakan mode ini memungkinkan pasangan tertentu dari operasi yang berjalan bersamaan (misalnya, operasi X dan Y) berlanjut sedemikian rupa sehingga hasilnya tampak seolah-olah Y dilakukan sebelum X (dengan kata lain, dapat terjadi serialisasi di antara mereka) meskipun catatan akan menunjukkan bahwa Y diselesaikan setelah X. Untuk mencegah urutan ulang ini, atur tingkat isolasi tabel menjadi Serializable sehingga transaksi ini akan gagal.

Operasi pembacaan selalu menggunakan isolasi snapshot. Tingkat isolasi penulisan menentukan apakah mungkin bagi pembaca untuk melihat cuplikan tabel, yang menurut riwayat, "tidak pernah ada".

Untuk tingkat Serializable, pembaca selalu melihat hanya tabel yang sesuai dengan riwayat. Untuk tingkat WriteSerializable, pembaca dapat melihat tabel yang tidak ada di log Delta.

Misalnya, pertimbangkan skenario di mana transaksi penghapusan yang berlangsung lama dan transaksi sisipan dimulai pada saat yang sama untuk membaca versi v0. Transaksi sisipan melakukan komit terlebih dahulu dan membuat versi v1. Setelah itu, transaksi penghapusan mencoba mengonfirmasi v2:

t0: deleteTxn_START
t1: insertTxn_START
t2: insertTxn_COMMIT(v1)
t3: deleteTxn_COMMIT(v2)

Dalam skenario ini, deleteTxn tidak melihat data yang disisipkan oleh insertTxn dan sehingga tidak menghapusnya:

  • Di bawah Serializable isolasi, deleteTxn tidak diizinkan untuk melakukan, dan konflik terjadi.
  • Dalam WriteSerializable kondisi isolasi, deleteTxn diizinkan untuk menyelesaikan transaksi karena transaksi dapat diurutkan. Status tabel yang dihasilkan seolah-olah insertTxn terjadi setelah deleteTxn, sehingga baris yang disisipkan adalah bagian dari tabel. Namun, riwayat Delta menunjukkan urutan komitmen fisik dan bahwa insertTxn (v1) terjadi sebelum deleteTxn (v2).

Untuk informasi selengkapnya tentang jenis operasi mana yang saling bertentangan di setiap tingkat isolasi dan kesalahan yang mungkin terjadi, lihat Menghindari konflik menggunakan pemartisian dan kondisi perintah yang saling terpisah.

Atur tingkat isolasi

Anda mengatur tingkat isolasi menggunakan perintah ALTER TABLE.

ALTER TABLE <table-name> SET TBLPROPERTIES ('delta.isolationLevel' = <level-name>)

di mana <level-name> adalah Serializable atau WriteSerializable.

Misalnya, untuk mengubah tingkat isolasi dari WriteSerializable default ke Serializable, jalankan:

ALTER TABLE <table-name> SET TBLPROPERTIES ('delta.isolationLevel' = 'Serializable')

Hindari konflik menggunakan partisi dan kondisi perintah yang terpisah

Dalam semua kasus yang ditandai "bisa konflik", apakah kedua operasi akan bertentangan tergantung apakah kedua operasi tersebut beroperasi pada set file yang sama. Anda dapat membuat dua set file yang tak bertumpang tindih dengan mempartisi tabel menggunakan kolom yang sama seperti yang digunakan dalam kondisi dari operasi-operasi. Misalnya, dua perintah UPDATE table WHERE date > '2010-01-01' ... dan DELETE table WHERE date < '2010-01-01' akan bertentangan jika tabel tidak dipartisi berdasarkan tanggal, karena keduanya dapat mencoba memodifikasi kumpulan file yang sama. Mempartisi tabel dengan date akan menghindari konflik. Oleh karena itu, mempartisi tabel sesuai dengan kondisi yang umum digunakan pada perintah dapat mengurangi konflik secara signifikan. Namun, mempartisi tabel menurut kolom yang memiliki kardinalitas tinggi dapat menyebabkan masalah performa lainnya karena banyaknya subdirektori.

Pengecualian konflik

Saat terjadi konflik transaksi, Anda akan mengamati salah satu pengecualian berikut:

ConcurrentAppendException

Pengecualian ini terjadi ketika operasi bersamaan menambahkan file di partisi yang sama (atau di mana saja dalam tabel yang tidak dipartisi) yang dibaca oleh operasi Anda. Penambahan file dapat disebabkan oleh operasi INSERT, DELETE, UPDATE, atau MERGE.

Dengan tingkat isolasi default, file yang ditambahkan oleh operasi blind (yaitu, operasi yang menambahkan data secara membabi buta tanpa membaca data apa pun) tidak bertentangan dengan operasi apa pun, bahkan jika mereka menyentuh partisi yang sama (atau di mana saja dalam tabel yang tidak dipartisi). Jika tingkat isolasi diatur ke Serializable, maka penambahan tanpa pengawasan mungkin bertentangan.

Penting : Penambahan buta dapat berkonflik dalam mode WriteSerializable jika beberapa transaksi yang berjalan secara bersamaan, operasi DELETE, UPDATE, atau MERGE, mungkin mereferensikan nilai yang dimasukkan oleh penambahan buta. Untuk menghindari konflik ini, lakukan salah satu hal berikut:

  • Pastikan bahwa operasi DELETE, UPDATE, atau MERGE bersamaan tidak membaca data yang ditambahkan.
  • Memiliki paling banyak satu operasi DELETE, UPDATE, atau MERGE yang dapat membaca data yang ditambahkan.

Pengecualian ini sering dilemparkan saat operasi DELETE, UPDATE, atau MERGE yang bersamaan. Sementara operasi bersamaan mungkin secara fisik memperbarui direktori partisi yang berbeda, salah satu operasi tersebut mungkin membaca partisi yang sama yang sedang diperbarui oleh yang lain, sehingga menyebabkan konflik. Anda dapat menghindari hal ini dengan membuat pemisahan eksplisit dalam kondisi operasi. Pertimbangkan contoh berikut.

// Target 'deltaTable' is partitioned by date and country
deltaTable.as("t").merge(
    source.as("s"),
    "s.user_id = t.user_id AND s.date = t.date AND s.country = t.country")
  .whenMatched().updateAll()
  .whenNotMatched().insertAll()
  .execute()

Misalkan Anda menjalankan kode di atas secara bersamaan untuk tanggal atau negara yang berbeda. Karena setiap pekerjaan mengerjakan partisi yang berdiri sendiri pada tabel Delta sasaran, Anda tidak mengharapkan adanya konflik. Namun, kondisinya tidak cukup eksplisit dan dapat memindai seluruh tabel dan dapat bertentangan dengan operasi bersamaan yang memperbarui partisi lainnya. Sebagai gantinya, Anda dapat menulis ulang pernyataan Anda untuk menambahkan tanggal dan negara tertentu ke kondisi gabungan, seperti yang ditunjukkan dalam contoh berikut.

// Target 'deltaTable' is partitioned by date and country
deltaTable.as("t").merge(
    source.as("s"),
    "s.user_id = t.user_id AND s.date = t.date AND s.country = t.country AND t.date = '" + <date> + "' AND t.country = '" + <country> + "'")
  .whenMatched().updateAll()
  .whenNotMatched().insertAll()
  .execute()

Operasi ini sekarang aman untuk dijalankan secara bersamaan pada tanggal dan negara yang berbeda.

ConcurrentDeleteReadException

Pengecualian ini terjadi ketika operasi bersamaan menghapus file yang dibaca operasi Anda. Penyebab umum adalah operasi DELETE, UPDATE, atau MERGE yang menulis ulang file.

ConcurrentDeleteDeleteException

Pengecualian ini terjadi ketika operasi bersamaan menghapus file yang juga dihapus oleh operasi Anda. Ini bisa disebabkan oleh dua operasi pemadatan bersamaan yang menulis ulang file yang sama.

MetadataChangedException

Pengecualian ini terjadi ketika transaksi bersamaan memperbarui metadata tabel Delta. Penyebab umum adalah operasi ALTER TABLE atau penulisan ke tabel Delta Anda yang memperbarui skema tabel.

ConcurrentTransactionException

Jika kueri streaming yang menggunakan lokasi pos pemeriksaan yang sama dimulai berulangkali secara bersamaan dan mencoba menulis ke tabel Delta pada saat yang sama. Anda seharusnya tidak pernah memiliki dua kueri streaming menggunakan lokasi checkpoint yang sama dan dijalankan pada waktu yang bersamaan.

ProtocolChangedException (PengecualianPerubahanProtokol)

Pengecualian ini dapat terjadi dalam kasus-kasus berikut:

  • Saat tabel Delta Anda dimutakhirkan ke versi protokol baru. Agar operasi di masa mendatang berhasil, Anda mungkin perlu meningkatkan Runtime Databricks Anda.
  • Ketika beberapa penulis membuat atau mengganti tabel pada saat yang sama.
  • Ketika beberapa penulis menulis ke jalan kosong pada saat yang sama.

Lihat Kompatibilitas dan protokol fitur Delta Lake untuk detail selengkapnya.

Perilaku pratinjau konkurensi tingkat baris (warisan)

Bagian ini menjelaskan perilaku pratinjau untuk konkurensi tingkat baris di Databricks Runtime 14.1 ke bawah. Konkurensi tingkat baris selalu memerlukan vektor penghapusan.

Di Databricks Runtime 13.3 LTS dan versi yang lebih baru, tabel dengan fitur liquid clustering diaktifkan secara otomatis mengaktifkan konkurensi pada tingkat baris.

Di Databricks Runtime 14.0 dan 14.1, Anda dapat mengaktifkan konkurensi tingkat baris untuk tabel dengan vektor penghapusan dengan mengatur konfigurasi berikut untuk kluster atau SparkSession:

spark.databricks.delta.rowLevelConcurrencyPreview = true

Dalam Databricks Runtime 14.1 ke bawah, komputasi non-Photon hanya mendukung konkurensi tingkat baris untuk DELETE operasi.