๐Ÿ‘จโ€๐Ÿ’ป
Sammi
  • Hello
  • About Me
    • Links
    • My Daily Uses
  • PostgreSQL โ†’ Partitioning
  • Belajar System Programming Menggunakan Go
    • Mengingat Kembali Tentang Concurrency dan Parallelism
  • Memory Management
  • Explore
    • Testing 1: Load and performance testing
    • Data Structure 1: Bloom Filter
    • System Design 1: Back of The Envelope Estimation
    • System Design 2: A Framework For System Design Interviews
    • System Design 3: Design a URL Shortener
    • Belajar RabbitMQ
  • Belajar Kubernetes
  • Notes
    • Permasalahan Penggunaan JWT dan Solusinya dengan PASETO
    • First Principle Thinking
    • The Over-Engineering Pendulum
    • Data-Oriented Programming
  • CAP Theorem
  • Go Series: Safer Enum
  • Go Series: Different types of for loops in Golang?
  • Go Series: Mutex & RWMutex
  • Setup VM Production Ready Best Practice
  • BEHAVIOUR QUESTION
  • Invert, always invert
  • Mengapa Tidak Menggunakan Auto-Increment ID?
  • I Prefix dan Impl Suffix
  • ACID
  • MVCC Di Postgres
  • Implicit Interface di Go
  • Transaction di Postgres
  • Kriteria Kolom yang Cocok Dijadikan Index
  • Misc
    • Go Project
    • Talks
    • Medium Articles
  • PostgreSQL
    • Introduction
  • English
    • Vocab
Powered by GitBook
On this page
  • ๐Ÿ”„ Konsep Transaksi di PostgreSQL
  • ๐Ÿ”’ Apa itu FOR UPDATE?
  • ๐ŸŽฏ Contoh dan Ilustrasi
  • ๐Ÿงช Praktek Langsung: Transfer Saldo
  • ๐Ÿง  Kesimpulan
  • โš ๏ธ Apa Itu Deadlock?
  • ๐Ÿงช Simulasi Deadlock dengan FOR UPDATE
  • ๐Ÿ› ๏ธ Kenapa Bisa Deadlock Walau Pakai FOR UPDATE?
  • โœ… Solusi: Gunakan FOR NO KEY UPDATE
  • ๐Ÿ“Š Perbandingan Lock Mode
  • ๐Ÿงช Simulasi dengan FOR NO KEY UPDATE
  • โœ… Best Practice Hindari Deadlock
  • ๐Ÿ”š Kesimpulan
  • ๐Ÿ” Apa Maksudnya "Pengambilan Kunci Secara Konsisten"?
  • ๐Ÿ” Contoh Deadlock karena Kunci Tidak Konsisten
  • โœ… Solusi: Ambil Kunci Berdasarkan Urutan Tetap
  • ๐Ÿงช Contoh Praktis: Transfer Saldo Aman
  • ๐Ÿง  Ringkasan Tips

Transaction di Postgres

Transaksi di PostgreSQL adalah unit kerja logis yang terdiri dari satu atau lebih pernyataan SQL yang dijalankan sebagai satu kesatuan. Tujuan utamanya adalah untuk menjaga konsistensi, isolasi, dan integritas data.


๐Ÿ”„ Konsep Transaksi di PostgreSQL

1. ACID

Transaksi harus memenuhi sifat ACID:

  • Atomicity: Semua pernyataan dalam transaksi berhasil semua atau dibatalkan semua.

  • Consistency: Data tetap konsisten sebelum dan sesudah transaksi.

  • Isolation: Transaksi tidak terlihat oleh transaksi lain sampai selesai.

  • Durability: Setelah commit, data tersimpan permanen.

2. Perintah Dasar

BEGIN;           -- Memulai transaksi
-- Perintah SQL
COMMIT;          -- Menyimpan perubahan
ROLLBACK;        -- Membatalkan semua perubahan

๐Ÿ”’ Apa itu FOR UPDATE?

FOR UPDATE digunakan untuk mengunci baris yang dipilih dalam transaksi agar tidak bisa diubah oleh transaksi lain sampai transaksi selesai (commit/rollback).

Tujuannya:

  • Mencegah kondisi race condition (misalnya dua pengguna mengedit data yang sama).

  • Memastikan data yang dibaca tidak berubah sebelum transaksi selesai.


๐ŸŽฏ Contoh dan Ilustrasi

1. Setup Tabel

CREATE TABLE akun (
    id SERIAL PRIMARY KEY,
    nama VARCHAR(50),
    saldo INTEGER
);

INSERT INTO akun (nama, saldo) VALUES
('Alice', 1000),
('Bob', 1000);

2. Simulasi Transaksi tanpa FOR UPDATE

Terminal 1:

BEGIN;
SELECT saldo FROM akun WHERE nama = 'Alice';  -- dapat saldo 1000
-- bayangkan proses lanjut menghitung dan menyimpan
UPDATE akun SET saldo = 900 WHERE nama = 'Alice';
-- COMMIT nanti

Terminal 2 (dijalankan sebelum commit Terminal 1):

BEGIN;
SELECT saldo FROM akun WHERE nama = 'Alice'; -- juga dapat saldo 1000!
UPDATE akun SET saldo = 800 WHERE nama = 'Alice';
COMMIT;

Masalah: Kedua transaksi menganggap saldo awal 1000 dan memperbarui berdasarkan data yang sudah kadaluarsa โ†’ terjadi inconsistency.


3. Simulasi Transaksi dengan FOR UPDATE

Terminal 1:

BEGIN;
SELECT saldo FROM akun WHERE nama = 'Alice' FOR UPDATE;
-- Alice dikunci barisnya sampai transaksi ini selesai
UPDATE akun SET saldo = 900 WHERE nama = 'Alice';
-- Jangan commit dulu

Terminal 2:

BEGIN;
SELECT saldo FROM akun WHERE nama = 'Alice' FOR UPDATE;
-- Akan menunggu sampai transaksi di Terminal 1 selesai (commit/rollback)

Keuntungan: Data tidak bisa dibaca dan diproses oleh transaksi lain sebelum transaksi pertama selesai โ†’ race condition dihindari.


๐Ÿงช Praktek Langsung: Transfer Saldo

Misal: Transfer 100 dari Alice ke Bob

BEGIN;

-- Kunci baris yang ingin dimodifikasi
SELECT * FROM akun WHERE nama IN ('Alice', 'Bob') FOR UPDATE;

-- Dapatkan saldo
SELECT saldo FROM akun WHERE nama = 'Alice';  -- 1000
SELECT saldo FROM akun WHERE nama = 'Bob';    -- 1000

-- Update
UPDATE akun SET saldo = saldo - 100 WHERE nama = 'Alice';
UPDATE akun SET saldo = saldo + 100 WHERE nama = 'Bob';

COMMIT;

Jika terjadi error, bisa langsung:

ROLLBACK;

๐Ÿง  Kesimpulan

Konsep
Penjelasan

Transaksi

Sekumpulan perintah SQL yang dieksekusi sebagai satu kesatuan

BEGIN/COMMIT

Memulai dan menyelesaikan transaksi

ROLLBACK

Membatalkan transaksi

FOR UPDATE

Mengunci baris yang dipilih untuk mencegah konflik antar transaksi


โš ๏ธ Apa Itu Deadlock?

Deadlock terjadi ketika dua transaksi saling menunggu satu sama lain untuk melepaskan kunci, sehingga tidak ada yang bisa lanjut.

๐Ÿ” Ilustrasi Kasus Deadlock

Misalkan kita punya tabel akun yang sama:

CREATE TABLE akun (
    id SERIAL PRIMARY KEY,
    nama VARCHAR(50),
    saldo INTEGER
);

๐Ÿงช Simulasi Deadlock dengan FOR UPDATE

Transaksi 1 (T1):

BEGIN;
SELECT * FROM akun WHERE nama = 'Alice' FOR UPDATE;
-- tunggu beberapa detik
SELECT * FROM akun WHERE nama = 'Bob' FOR UPDATE;

Transaksi 2 (T2):

BEGIN;
SELECT * FROM akun WHERE nama = 'Bob' FOR UPDATE;
-- tunggu beberapa detik
SELECT * FROM akun WHERE nama = 'Alice' FOR UPDATE;

๐Ÿ” Hasil:

  • T1 mengunci Alice โ†’ tunggu Bob

  • T2 mengunci Bob โ†’ tunggu Alice

  • Keduanya saling tunggu โ†’ deadlock

  • PostgreSQL akan memilih satu untuk dibatalkan (ERROR: deadlock detected)


๐Ÿ› ๏ธ Kenapa Bisa Deadlock Walau Pakai FOR UPDATE?

Karena FOR UPDATE mengunci baris secara eksklusif. Kalau dua transaksi mengunci dengan urutan berbeda, maka bisa saling tunggu. Urutan pengambilan kunci itu penting.


โœ… Solusi: Gunakan FOR NO KEY UPDATE

โ“ Apa Itu FOR NO KEY UPDATE?

  • FOR NO KEY UPDATE adalah varian lebih ringan dari FOR UPDATE.

  • Mengunci baris hanya untuk update nilai kolom, tanpa mengizinkan update kunci (misalnya kolom primary key).

  • Ini mengurangi kemungkinan deadlock karena kunci yang digunakan tidak terlalu agresif.


๐Ÿ“Š Perbandingan Lock Mode

Lock Mode
Deskripsi

FOR UPDATE

Mengunci baris untuk update penuh (termasuk kunci)

FOR NO KEY UPDATE

Mengunci baris untuk update non-kunci (lebih ringan)

FOR SHARE

Hanya baca data, bisa dibaca transaksi lain

FOR KEY SHARE

Bisa dibaca dan referensi foreign key, tapi tidak bisa dihapus/dikunci penuh


๐Ÿงช Simulasi dengan FOR NO KEY UPDATE

Jika transaksi hanya ingin update saldo (bukan id), maka lebih aman:

T1:

BEGIN;
SELECT * FROM akun WHERE nama = 'Alice' FOR NO KEY UPDATE;
-- tunggu
SELECT * FROM akun WHERE nama = 'Bob' FOR NO KEY UPDATE;

T2:

BEGIN;
SELECT * FROM akun WHERE nama = 'Bob' FOR NO KEY UPDATE;
-- tunggu
SELECT * FROM akun WHERE nama = 'Alice' FOR NO KEY UPDATE;

โš ๏ธ Hasil:

  • Deadlock masih mungkin terjadi kalau urutan ambil lock-nya berbeda, meskipun lebih jarang.

  • Maka kunci solusi utamanya tetap: urutkan pengambilan kunci secara konsisten!


โœ… Best Practice Hindari Deadlock

  1. Selalu akses resource dalam urutan yang sama (misal: urut berdasarkan id ASC).

  2. Gunakan lock mode serendah mungkin (FOR NO KEY UPDATE lebih baik dari FOR UPDATE).

  3. Gunakan timeout / pengecekan deadlock dan rollback otomatis jika perlu.


๐Ÿ”š Kesimpulan

  • FOR UPDATE kuat tapi agresif โ†’ rawan deadlock jika resource diakses tidak konsisten.

  • FOR NO KEY UPDATE lebih ringan โ†’ cocok jika hanya update kolom non-kunci.

  • Urutan pengambilan kunci lebih penting dari jenis lock-nya.


๐Ÿ” Apa Maksudnya "Pengambilan Kunci Secara Konsisten"?

Artinya: semua transaksi yang mengakses banyak baris/data harus mengunci data dalam urutan yang sama, misalnya:

  • berdasarkan id ASC

  • berdasarkan abjad nama

  • urutan waktu tertentu

โŒ Jika Transaksi A mengunci data dengan urutan A โ†’ B dan Transaksi B mengunci data dengan urutan B โ†’ A, โš ๏ธ maka potensi deadlock sangat tinggi.

โœ… Jika semua transaksi selalu mengunci A โ†’ B, maka tidak akan ada saling tunggu silang.


๐Ÿ” Contoh Deadlock karena Kunci Tidak Konsisten

Misal tabel akun:

id | nama   | saldo
---+--------+-------
1  | Alice  | 1000
2  | Bob    | 1000

Transaksi A

BEGIN;
SELECT * FROM akun WHERE id = 1 FOR UPDATE; -- kunci Alice
SELECT * FROM akun WHERE id = 2 FOR UPDATE; -- kunci Bob

Transaksi B

BEGIN;
SELECT * FROM akun WHERE id = 2 FOR UPDATE; -- kunci Bob
SELECT * FROM akun WHERE id = 1 FOR UPDATE; -- tunggu Alice

๐Ÿ” Keduanya saling tunggu โ†’ Deadlock!


โœ… Solusi: Ambil Kunci Berdasarkan Urutan Tetap

Cara Aman:

-- Ambil data dengan urutan konsisten (id ASC misalnya)
SELECT * FROM akun WHERE id IN (1, 2) ORDER BY id FOR UPDATE;

Jadi, transaksi A dan B:

BEGIN;
SELECT * FROM akun WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
-- lanjut proses transfer atau update
COMMIT;

โœ… Hasil: tidak ada saling tunggu silang, karena kedua transaksi:

  • mengakses id = 1 lebih dulu

  • baru id = 2 berikutnya


๐Ÿงช Contoh Praktis: Transfer Saldo Aman

-- Asumsikan kita mau transfer antar 2 akun
-- Ambil id akun pengirim dan penerima, lalu sort ASC

-- Misal id_pengirim = 2, id_penerima = 1

-- Sort dulu:
SELECT LEAST(2, 1) AS id1, GREATEST(2, 1) AS id2;

-- Gunakan urutan itu untuk locking
BEGIN;

-- Ambil dan kunci dua akun berdasarkan urutan id
SELECT * FROM akun WHERE id IN (1, 2) ORDER BY id FOR UPDATE;

-- Update saldo
UPDATE akun SET saldo = saldo - 100 WHERE id = 2;
UPDATE akun SET saldo = saldo + 100 WHERE id = 1;

COMMIT;

๐Ÿง  Ringkasan Tips

Tips Pengambilan Kunci Konsisten
Penjelasan

๐Ÿ” Gunakan urutan tetap

Gunakan ORDER BY berdasarkan id, nama, atau kriteria stabil lainnya

๐Ÿšซ Jangan kunci berbeda-beda urutan

Hindari transaksi A โ†’ B dan B โ†’ A

โš–๏ธ Gunakan LEAST / GREATEST

Untuk menentukan siapa dikunci lebih dulu secara deterministik

๐Ÿ” Gabungkan dalam satu query

Semakin sedikit query terpisah, semakin kecil peluang deadlock

๐Ÿ’ก Gunakan lock ringan (NO KEY UPDATE)

Kalau tidak perlu ubah PK atau referensi, pakai lock lebih ringan


PreviousImplicit Interface di GoNextKriteria Kolom yang Cocok Dijadikan Index

Last updated 13 days ago