Berbagi teknologi

Bagaimana cara menangani resolusi konflik untuk pembaruan data secara bersamaan di PostgreSQL?

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

garis pemisah yang indah

Bahasa pemrograman Bahasa pemrograman PostgreSQL


Dalam lingkungan operasi database bersamaan, beberapa transaksi yang mencoba memperbarui data yang sama pada saat yang sama dapat menyebabkan konflik. PostgreSQL menyediakan serangkaian mekanisme untuk menangani konflik pembaruan bersamaan ini untuk memastikan konsistensi dan integritas data.

garis pemisah yang indah

1. Skenario konflik pembaruan secara bersamaan

Konflik pembaruan serentak dapat terjadi ketika dua atau lebih transaksi mencoba mengubah baris data yang sama pada waktu yang sama. Skenario umum meliputi:

  1. Ubah kolom berbeda dari baris yang sama secara bersamaan
  2. Perbarui nilai berbeda dari kolom yang sama secara bersamaan

garis pemisah yang indah

2. Mekanisme kontrol konkurensi di PostgreSQL

PostgreSQL terutama menggunakan MVCC (Multiversion Concurrency Control) untuk menangani transaksi bersamaan. MVCC memungkinkan transaksi membaca versi data yang memenuhi persyaratan tingkat isolasi tanpa mengunci untuk memblokir operasi baca transaksi lainnya. Namun, konflik mungkin masih terjadi selama operasi penulisan.

(1) Mekanisme pemblokiran

PostgreSQL menggunakan beberapa jenis kunci untuk mengontrol akses bersamaan ke data. Jenis kunci yang umum meliputi:

  1. Kunci Bersama: Mengizinkan transaksi lain memperoleh kunci bersama, namun mencegah perolehan kunci eksklusif. Biasa digunakan untuk operasi baca.
  2. Kunci Eksklusif: Mencegah transaksi lain memperoleh jenis kunci apa pun, yang sering digunakan untuk operasi tulis.

Perincian kunci dapat berupa tingkat baris (Tingkat Baris), tingkat halaman (Tingkat Halaman), dan tingkat tabel (Tingkat Tabel).

(2) Tingkat isolasi transaksi

PostgreSQL mendukung empat tingkat isolasi transaksi:

  1. Baca Tanpa Komitmen: Ini adalah tingkat isolasi terendah. Suatu transaksi dapat membaca modifikasi data yang tidak dikomit dari transaksi lain, yang dapat menyebabkan masalah seperti pembacaan kotor, pembacaan yang tidak dapat diulang, dan pembacaan bayangan.
  2. Baca Berkomitmen: Transaksi hanya dapat membaca data yang dikirimkan, menghindari pembacaan kotor, namun pembacaan yang tidak dapat diulang dan pembacaan bayangan masih dapat terjadi.
  3. Bacaan yang Dapat Diulang: Membaca data yang sama beberapa kali dalam suatu transaksi akan mendapatkan hasil yang sama, menghindari pembacaan yang tidak dapat diulang, tetapi pembacaan bayangan dapat terjadi.
  4. Dapat diserialkan: Tingkat isolasi tertinggi, memastikan eksekusi transaksi serial melalui kontrol konkurensi yang ketat, menghindari pembacaan kotor, pembacaan yang tidak dapat diulang, dan pembacaan hantu.

garis pemisah yang indah

3. Solusi untuk konflik pembaruan secara bersamaan

(1) Mekanisme coba lagi

Pendekatan sederhananya adalah dengan mencoba ulang transaksi ketika terjadi konflik. Contohnya adalah sebagai berikut:

DO
$$
DECLARE
    conflict_detected BOOLEAN := FALSE;
BEGIN
    LOOP
        -- 尝试执行更新操作
        UPDATE products SET price = 100 WHERE id = 1;

        -- 检查是否有冲突(例如,通过检查受影响的行数)
        IF NOT FOUND THEN
            conflict_detected := TRUE;
        ELSE
            EXIT;
        END IF;

        -- 若有冲突,等待一段时间并重试
        IF conflict_detected THEN
            PERFORM pg_sleep(1);
        END IF;
    END LOOP;
END;
$$;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Dalam contoh di atas, jika operasi pembaruan tidak mempengaruhi baris apa pun (menunjukkan bahwa mungkin ada konflik), sebuah tanda disetel, menunggu selama jangka waktu tertentu, lalu mencoba lagi.

(2) Gunakan kontrol konkurensi optimis

Pengendalian konkurensi optimis mengasumsikan bahwa konflik konkurensi jarang terjadi. Dalam metode ini, transaksi tidak dikunci saat memperbarui data, tetapi memeriksa apakah data telah diubah oleh transaksi lain saat dilakukan. Jika tidak ada konflik, transaksi berhasil dilakukan; jika ada konflik, transaksi dibatalkan dan dicoba lagi sesuai kebutuhan.

-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;

-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
  • 1
  • 2
  • 3
  • 4
  • 5

Dalam contoh di atas, operasi pembaruan hanya berhasil jika data belum diubah oleh transaksi lain.

(3) Gunakan kontrol konkurensi pesimis

Kontrol konkurensi pesimistis mengasumsikan bahwa konflik konkurensi mungkin terjadi, dan memperoleh kunci yang diperlukan selama eksekusi transaksi untuk memblokir transaksi lain yang berpotensi menimbulkan konflik.

BEGIN;

-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;

-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;

COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(4) Bidang versi aplikasi

Tambahkan bidang versi ke tabel untuk melacak perubahan pada data.

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    version INT DEFAULT 0
);
  • 1
  • 2
  • 3
  • 4
  • 5

Saat memperbarui data, tambah juga kolom versi:

UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
  • 1

Jika jumlah baris yang terpengaruh oleh pembaruan adalah 0, terjadi konflik karena versi yang diharapkan tidak konsisten dengan versi sebenarnya.

(5) Penyelesaian konflik berdasarkan stempel waktu

Tambahkan bidang stempel waktu ke setiap baris data untuk mencatat waktu modifikasi terakhir data.

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    price DECIMAL(10, 2),
    last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • 1
  • 2
  • 3
  • 4
  • 5

Saat memperbarui, hanya perbarui data dengan stempel waktu yang lebih lama dari stempel waktu yang dibaca oleh transaksi saat ini:

UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
  • 1

garis pemisah yang indah

4. Pertimbangan dalam Penerapan Praktis

(1) Dampak kinerja

  1. Metode penyelesaian konflik yang berbeda mempunyai dampak yang berbeda terhadap kinerja database. Misalnya, penggunaan pemblokiran dapat menyebabkan transaksi lain menunggu, meningkatkan waktu pemblokiran sistem, dan karenanya memengaruhi konkurensi. Kontrol konkurensi optimis berkinerja lebih baik ketika konflik jarang terjadi, namun ketika konflik sering terjadi, hal ini dapat menyebabkan percobaan ulang transaksi dalam jumlah besar, sehingga meningkatkan waktu eksekusi keseluruhan.
  2. Menerapkan kolom versi atau metode berbasis stempel waktu mungkin memerlukan ruang penyimpanan tambahan untuk mempertahankan informasi versi atau stempel waktu, serta penilaian dan pemrosesan tambahan saat memperbarui.

(2) Kemampuan beradaptasi logika bisnis

  1. Skenario bisnis tertentu mungkin lebih cocok untuk metode penyelesaian konflik tertentu. Misalnya, jika bisnis memiliki persyaratan yang sangat tinggi untuk konsistensi data dan tidak dapat mentolerir ketidakkonsistenan apa pun, maka kontrol konkurensi yang pesimistis atau tingkat isolasi serialisasi mungkin merupakan pilihan yang lebih baik.
  2. Kontrol konkurensi optimis mungkin lebih sesuai untuk skenario di mana konflik lebih jarang terjadi dan persyaratan waktu respons lebih tinggi.

(3) Distribusi data dan pola akses

  1. Jika akses ke data sangat bersamaan, dan banyak transaksi sering kali mengakses baris data yang sama pada saat yang sama, metode penyelesaian konflik perlu dipilih dengan lebih hati-hati untuk menghindari pemblokiran dan konflik yang berlebihan.
  2. Untuk situasi dimana distribusi data relatif seragam dan kemungkinan konflik rendah, metode yang relatif sederhana dan efisien dapat digunakan, seperti kontrol konkurensi optimis.

garis pemisah yang indah

5. Contoh analisis

Misalkan kita memiliki sistem manajemen inventaris untuk toko online dengan a inventory meja untuk menyimpan jumlah persediaan barang.

CREATE TABLE inventory (
    product_id INT PRIMARY KEY,
    quantity INT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  • 1
  • 2
  • 3
  • 4
  • 5

Sekarang ada dua transaksi bersamaan:

Transaksi 1:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设读取到的数量为 10
UPDATE inventory SET quantity = 5 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5

Transaksi 2:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设也读取到的数量为 10
UPDATE inventory SET quantity = 8 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
  • 1
  • 2
  • 3
  • 4
  • 5

Jika kedua transaksi ini dijalankan pada waktu yang hampir bersamaan, konflik dapat terjadi.

Jika kami menggunakan resolusi konflik berdasarkan stempel waktu:

  1. Transaksi 1 memperoleh stempel waktu saat ini saat membaca data (T1)。
  2. Transaksi 2 memperoleh stempel waktu sedikit lebih lambat saat membaca data (T2)。

Ketika transaksi 1 mencoba memperbarui, jika tidak ada transaksi lain yang mengubah data sejak transaksi tersebut membacanya (yaitu last_updated <= T1), pembaruan berhasil.

Ketika transaksi 2 mencoba memperbarui, jika menemukan data itu last_updated lebih dari ituT2(Menunjukkan bahwa itu diubah setelah transaksi 2 membacanya), pembaruan gagal, dan transaksi 2 dapat memilih untuk memutar kembali dan mencoba lagi, atau melakukan pemrosesan lain sesuai dengan logika bisnis.


garis pemisah yang indah

🎉相关推荐

Bahasa pemrograman Bahasa pemrograman PostgreSQL