Halaman ini menjelaskan cara menggunakan klausa FOR UPDATE
dalam isolasi yang dapat diserialisasi.
Mekanisme penguncian klausa FOR UPDATE
berbeda untuk
repeatable read dan
serializable isolation. Dengan menggunakan
isolasi yang dapat diserialisasi, saat Anda menggunakan kueri SELECT
untuk memindai tabel, menambahkan
klausa FOR UPDATE
akan mengaktifkan kunci eksklusif di persimpangan
tingkat perincian baris dan kolom, atau dikenal sebagai tingkat sel. Kunci
tetap ada selama masa pakai transaksi baca-tulis. Selama waktu ini, klausa FOR UPDATE
mencegah transaksi lain mengubah sel yang dikunci hingga transaksi saat ini selesai.
Untuk mempelajari cara menggunakan klausa FOR UPDATE
, lihat panduan referensi
GoogleSQL
dan PostgreSQL
FOR UPDATE
.
Alasan menggunakan klausa FOR UPDATE
Di database dengan tingkat isolasi yang kurang ketat, klausa FOR UPDATE
mungkin diperlukan untuk memastikan bahwa transaksi serentak tidak memperbarui data antara pembacaan data dan commit transaksi. Karena Spanner
menerapkan serialisabilitas secara default, transaksi dijamin hanya
berhasil di-commit jika data yang diakses dalam transaksi tidak basi pada
waktu commit. Oleh karena itu, klausa FOR UPDATE
tidak diperlukan untuk memastikan
kebenaran transaksi di Spanner.
Namun, dalam kasus penggunaan dengan pertentangan penulisan yang tinggi, seperti saat beberapa transaksi secara bersamaan membaca dan menulis ke data yang sama, transaksi serentak dapat menyebabkan peningkatan pembatalan. Hal ini karena ketika beberapa transaksi serentak memperoleh kunci bersama, lalu mencoba mengupgrade ke kunci eksklusif, transaksi tersebut menyebabkan kebuntuan. Deadlock memblokir transaksi secara permanen karena setiap transaksi menunggu transaksi lain melepaskan resource yang dibutuhkan. Untuk membuat progres, Spanner membatalkan semua transaksi kecuali satu transaksi untuk menyelesaikan kebuntuan. Untuk mengetahui informasi selengkapnya, lihat Penguncian.
Transaksi yang menggunakan klausa FOR UPDATE
akan mendapatkan kunci eksklusif secara proaktif dan melanjutkan eksekusi, sementara transaksi lain menunggu giliran untuk mendapatkan kunci. Meskipun Spanner mungkin masih membatasi throughput
karena transaksi yang bertentangan hanya dapat dilakukan satu per satu, tetapi
karena Spanner hanya membuat progres pada satu transaksi, Spanner
menghemat waktu yang seharusnya digunakan untuk membatalkan dan mencoba ulang transaksi.
Oleh karena itu, jika mengurangi jumlah transaksi yang dibatalkan dalam skenario permintaan penulisan serentak penting, Anda dapat menggunakan klausa FOR UPDATE
untuk mengurangi jumlah pembatalan secara keseluruhan dan meningkatkan efisiensi eksekusi workload.
Perbandingan dengan petunjuk LOCK_SCANNED_RANGES
Klausa FOR UPDATE
memiliki fungsi yang mirip dengan
petunjuk LOCK_SCANNED_RANGES=exclusive
.
Ada dua perbedaan utama:
Jika Anda menggunakan petunjuk
LOCK_SCANNED_RANGES
, transaksi akan mendapatkan kunci eksklusif pada rentang yang dipindai untuk seluruh pernyataan. Anda tidak dapat memperoleh kunci eksklusif pada subkueri. Menggunakan petunjuk kunci dapat mengakibatkan perolehan lebih banyak kunci daripada yang diperlukan dan berkontribusi pada persaingan kunci dalam workload. Contoh berikut menunjukkan cara menggunakan petunjuk kunci:@{lock_scanned_ranges=exclusive} SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) AS a ON a.SingerId = s.SingerId;
Di sisi lain, Anda dapat menggunakan klausa
FOR UPDATE
dalam subkueri seperti yang ditunjukkan dalam contoh berikut:SELECT s.SingerId, s.FullName FROM Singers AS s JOIN (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000) FOR UPDATE AS a ON a.SingerId = s.SingerId;
Anda dapat menggunakan petunjuk
LOCK_SCANNED_RANGES
dalam pernyataan DML, sedangkan Anda hanya dapat menggunakan klausaFOR UPDATE
dalam pernyataanSELECT
.
Semantik kunci
Untuk mengurangi permintaan penulisan serentak dan biaya transaksi yang dibatalkan
akibat kebuntuan, Spanner mengunci data di tingkat sel jika
memungkinkan. Tingkat sel adalah tingkat data paling terperinci dalam tabel - titik data di persimpangan baris dan kolom. Saat menggunakan klausa
FOR UPDATE
, Spanner mengunci sel tertentu yang dipindai oleh kueri SELECT
.
Dalam contoh berikut, sel MarketingBudget
di baris SingerId = 1
dan
AlbumId = 1
dikunci secara eksklusif dalam tabel Albums
, sehingga mencegah
transaksi serentak mengubah sel tersebut hingga transaksi ini
di-commit atau di-roll back. Namun, transaksi serentak masih dapat memperbarui sel AlbumTitle
di baris tersebut.
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1
FOR UPDATE;
Transaksi serentak mungkin diblokir saat membaca data yang dikunci
Jika satu transaksi telah memperoleh kunci eksklusif pada rentang yang dipindai, transaksi serentak dapat memblokir pembacaan data tersebut. Spanner menerapkan serialisabilitas sehingga data hanya dapat dibaca jika dijamin tidak berubah oleh transaksi lain dalam masa aktif transaksi. Transaksi serentak yang mencoba membaca data yang sudah dikunci mungkin harus menunggu hingga transaksi yang memegang kunci di-commit, di-roll back, atau waktunya habis.
Dalam contoh berikut, Transaction 1
mengunci sel MarketingBudget
untuk
1 <= AlbumId < 5
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
Transaction 2
, yang mencoba membaca MarketingBudget
untuk
AlbumId = 1
, diblokir hingga Transaction 1
di-commit atau di-roll
back.
-- Transaction 2
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId = 1;
-- Blocked by Transaction 1
Demikian pula, transaksi yang mencoba mengunci rentang yang dipindai dengan FOR UPDATE
akan diblokir oleh transaksi serentak yang mengunci rentang yang dipindai yang tumpang-tindih.
Transaction 3
dalam contoh berikut juga diblokir karena Transaction 1
telah mengunci sel MarketingBudget
untuk 3 <= AlbumId < 5
, yang merupakan
rentang yang dipindai yang tumpang-tindih dengan Transaction 3
.
-- Transaction 3
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 3 and AlbumId < 10
FOR UPDATE;
-- Blocked by Transaction 1
Membaca indeks
Pembacaan serentak mungkin tidak diblokir jika kueri yang mengunci rentang yang dipindai mengunci baris dalam tabel dasar, tetapi transaksi serentak membaca dari indeks.
Transaction 1
berikut mengunci sel SingerId
dan SingerInfo
untuk
SingerId = 1
.
-- Transaction 1
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = 1
FOR UPDATE;
Transaction 2
hanya baca tidak diblokir oleh kunci yang diperoleh di
Transaction 1
, karena mengkueri tabel indeks.
-- Transaction 2
SELECT SingerId FROM Singers;
Transaksi serentak tidak memblokir operasi DML pada data yang sudah dikunci
Jika satu transaksi telah memperoleh kunci pada rentang sel dengan petunjuk kunci eksklusif, transaksi serentak yang mencoba melakukan penulisan tanpa membaca data terlebih dahulu pada sel yang dikunci dapat dilanjutkan. Transaksi diblokir pada commit hingga transaksi yang memegang kunci di-commit atau di-roll back.
Transaction 1
berikut mengunci sel MarketingBudget
untuk
1 <= AlbumId < 5
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 5
FOR UPDATE;
Jika Transaction 2
mencoba memperbarui tabel Albums
, Transaction 2
akan diblokir untuk melakukannya hingga Transaction 1
melakukan commit atau roll back.
-- Transaction 2
UPDATE Albums
SET MarketingBudget = 200000
WHERE SingerId = 1 and AlbumId = 1;
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
Baris dan celah yang ada dikunci saat rentang yang dipindai dikunci
Jika satu transaksi telah memperoleh kunci eksklusif pada rentang yang dipindai, transaksi serentak tidak dapat menyisipkan data di celah dalam rentang tersebut.
Transaction 1
berikut mengunci sel MarketingBudget
untuk
1 <= AlbumId < 10
.
-- Transaction 1
SELECT MarketingBudget
FROM Albums
WHERE SingerId = 1 and AlbumId >= 1 and AlbumId < 10
FOR UPDATE;
Jika Transaction 2
mencoba menyisipkan baris untuk AlbumId = 9
yang belum ada, Transaction 2
akan diblokir untuk melakukannya hingga Transaction 1
melakukan commit atau roll back.
-- Transaction 2
INSERT INTO Albums (SingerId, AlbumId, AlbumTitle, MarketingBudget)
VALUES (1, 9, "Hello hello!", 10000);
> Query OK, 1 rows affected
COMMIT;
-- Blocked by Transaction 1
Peringatan akuisisi kunci
Semantik kunci yang dijelaskan memberikan panduan umum, tetapi tidak menjamin
cara tepat kunci dapat diperoleh saat Spanner menjalankan transaksi yang menggunakan klausa FOR UPDATE
. Mekanisme pengoptimalan kueri Spanner juga dapat memengaruhi kunci mana yang diperoleh. Klausul ini mencegah transaksi lain mengubah sel yang dikunci hingga transaksi saat ini selesai.
Sintaksis kueri
Bagian ini memberikan panduan tentang sintaksis kueri saat menggunakan klausa FOR UPDATE
.
Penggunaan yang paling umum adalah dalam pernyataan SELECT
tingkat teratas. Contoh:
SELECT SingerId, SingerInfo
FROM Singers WHERE SingerID = 5
FOR UPDATE;
Contoh ini menunjukkan cara menggunakan klausa FOR UPDATE
dalam pernyataan SELECT
untuk mengunci secara eksklusif sel SingerId
dan SingerInfo
dari WHERE SingerID = 5
.
Penggunaan dalam pernyataan WITH
Klausa FOR UPDATE
tidak mendapatkan kunci untuk pernyataan WITH
saat
Anda menentukan FOR UPDATE
dalam kueri tingkat luar dari pernyataan WITH
.
Dalam kueri berikut, tidak ada kunci yang diperoleh oleh tabel Singers
, karena
niat untuk mengunci tidak diteruskan ke kueri ekspresi tabel umum (CTE).
WITH s AS (SELECT SingerId, SingerInfo FROM Singers WHERE SingerID > 5)
SELECT * FROM s
FOR UPDATE;
Jika klausa FOR UPDATE
ditentukan dalam kueri CTE, rentang yang dipindai dari
kueri CTE akan memperoleh kunci.
Dalam contoh berikut, sel SingerId
dan SingerInfo
untuk baris
tempat SingerId > 5
dikunci.
WITH s AS
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5 FOR UPDATE)
SELECT * FROM s;
Penggunaan dalam subkueri
Anda dapat menggunakan klausa FOR UPDATE
dalam kueri tingkat luar yang memiliki satu atau beberapa subkueri. Penguncian diperoleh oleh kueri tingkat teratas dan dalam subkueri,
kecuali dalam subkueri ekspresi.
Kueri berikut mengunci sel SingerId
dan SingerInfo
untuk baris dengan
SingerId > 5.
(SELECT SingerId, SingerInfo FROM Singers WHERE SingerId > 5) AS t
FOR UPDATE;
Kueri berikut tidak mengunci sel apa pun dalam tabel Albums
karena berada dalam subkueri ekspresi. Sel SingerId
dan SingerInfo
untuk
baris yang ditampilkan oleh subkueri ekspresi dikunci.
SELECT SingerId, SingerInfo
FROM Singers
WHERE SingerId = (SELECT SingerId FROM Albums WHERE MarketingBudget > 100000)
FOR UPDATE;
Digunakan untuk membuat kueri tampilan
Anda dapat menggunakan klausa FOR UPDATE
untuk membuat kueri tampilan seperti yang ditunjukkan dalam contoh berikut:
CREATE VIEW SingerBio AS SELECT SingerId, FullName, SingerInfo FROM Singers;
SELECT * FROM SingerBio WHERE SingerId = 5 FOR UPDATE;
Anda tidak dapat menggunakan klausa FOR UPDATE
saat menentukan tampilan.
Kasus penggunaan yang tidak didukung
Kasus penggunaan FOR UPDATE
berikut tidak didukung:
- Sebagai mekanisme eksklusi timbal balik untuk menjalankan kode di luar Spanner: Jangan gunakan penguncian di Spanner untuk memastikan akses eksklusif ke resource di luar Spanner. Transaksi dapat dibatalkan oleh Spanner, misalnya, jika transaksi dicoba lagi, baik secara eksplisit oleh kode aplikasi atau secara implisit oleh kode klien, seperti driver JDBC Spanner, hanya dijamin bahwa kunci dipegang selama upaya yang dilakukan.
- Jika dikombinasikan dengan petunjuk
LOCK_SCANNED_RANGES
: Anda tidak dapat menggunakan klausaFOR UPDATE
dan petunjukLOCK_SCANNED_RANGES
dalam kueri yang sama, atau Spanner akan menampilkan error. - Dalam kueri penelusuran teks lengkap: Anda tidak dapat menggunakan klausa
FOR UPDATE
dalam kueri yang menggunakan indeks penelusuran teks lengkap. - Dalam transaksi hanya baca: Klausa
FOR UPDATE
hanya valid dalam kueri yang berjalan dalam transaksi baca-tulis. - Dalam pernyataan DDL: Anda tidak dapat menggunakan klausa
FOR UPDATE
dalam kueri dalam pernyataan DDL, yang disimpan untuk dieksekusi nanti. Misalnya, Anda tidak dapat menggunakan klausaFOR UPDATE
saat menentukan tampilan. Jika penguncian diperlukan, klausaFOR UPDATE
dapat ditentukan saat mengkueri tampilan.
Langkah Berikutnya
- Pelajari cara menggunakan klausa
FOR UPDATE
di GoogleSQL dan PostgreSQL. - Pelajari cara Menggunakan SELECT FOR UPDATE dalam isolasi baca yang dapat diulang.
- Pelajari saran
LOCK_SCANNED_RANGES
. - Pelajari Penguncian di Spanner.
- Pelajari serialisabilitas Spanner.