3121 kata
16 menit

Index dan Transaction MySQL: Bikin Query Lebih Efisien dan Aman

Pada Bab 6, kita sudah berhasil membuat berbagai laporan menggunakan GROUP BY, HAVING, dan subquery.

Kita bisa mengetahui produk terlaris, menghitung total pembelian pelanggan, dan mencari produk dengan harga di atas rata-rata.

Tetapi ada dua masalah baru yang perlu dipikirkan.

Pertama, bagaimana jika jumlah data semakin besar?

Ketika tabel hanya memiliki sepuluh baris, hampir semua query terasa cepat. Namun, ketika jumlahnya mencapai ratusan ribu atau jutaan baris, pencarian dan penggabungan data dapat membutuhkan waktu lebih lama.

Kedua, bagaimana jika proses transaksi gagal di tengah jalan?

Bayangkan pelanggan membeli sebuah keyboard.

Aplikasi berhasil mengurangi stok, tetapi gagal menyimpan pesanan karena koneksi database terputus.

Akibatnya, stok berkurang, tetapi tidak ada catatan pembelian.

Pemilik toko kebingungan. Pelanggan juga kebingungan. Keyboard-nya mungkin ikut bingung.

Pada Bab 7 Series Database BapakGabut ini, kita akan mempelajari dua fitur penting MySQL:

  1. Index, untuk membantu database menemukan data secara lebih efisien.
  2. Transaction, untuk mengelola beberapa perubahan data sebagai satu kesatuan pekerjaan.

Kita juga akan mengenal EXPLAIN, konsep ACID, START TRANSACTION, COMMIT, ROLLBACK, dan contoh transaksi pembelian dengan pengurangan stok.

Persiapan Database#

Kita tetap menggunakan database dari Bab 3 sampai Bab 6.

Buka DBeaver, hubungkan ke MySQL, lalu jalankan:

USE db_toko_bapakgabut;

Database kita mempunyai empat tabel:

TabelFungsi
pelangganInformasi pelanggan
produkInformasi produk dan stok
pesananInformasi utama pesanan
detail_pesananProduk dan jumlah pada setiap pesanan

Relasinya:

pelanggan (1) --- (N) pesanan
pesanan (1) --- (N) detail_pesanan
produk (1) --- (N) detail_pesanan

Seluruh tabel pada contoh sebelumnya menggunakan mesin penyimpanan InnoDB.

Ini penting karena InnoDB mendukung transaksi dan foreign key.

Kamu bisa memeriksa struktur tabel dengan:

SHOW CREATE TABLE produk;

Untuk melihat mesin penyimpanan seluruh tabel:

SELECT
TABLE_NAME,
ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'db_toko_bapakgabut'
AND TABLE_NAME IN (
'pelanggan',
'produk',
'pesanan',
'detail_pesanan'
);

Pastikan keempat tabel menggunakan InnoDB sebelum melanjutkan latihan transaksi.

Apa Itu Index pada Database?#

Bayangkan kamu sedang mencari sebuah topik di buku yang mempunyai seribu halaman.

Tanpa daftar isi atau indeks, kamu mungkin harus memeriksa banyak halaman sampai menemukan topik yang dicari.

Dengan indeks, kamu mempunyai petunjuk untuk menemukan bagian yang relevan.

Konsep index pada database kurang lebih seperti itu.

Index adalah struktur data yang membantu MySQL menemukan baris tertentu tanpa harus selalu memeriksa seluruh isi tabel.

Pada InnoDB, index biasa menggunakan struktur B-tree.

Struktur tersebut membantu pencarian berdasarkan nilai kolom tertentu.

Namun, index bukan tombol ajaib yang membuat semua query otomatis lebih cepat.

Efektivitasnya bergantung pada kondisi query, jumlah data, distribusi nilai, dan keputusan query optimizer.

Primary Key Sudah Memiliki Index#

Sebelum membuat index baru, perlu diketahui bahwa primary key sudah mempunyai index.

Pada tabel produk, kita memiliki:

id_produk INT AUTO_INCREMENT PRIMARY KEY

Karena id_produk adalah primary key, MySQL sudah membuat index untuk kolom tersebut.

Jadi, kita tidak perlu membuat index tambahan dengan kolom yang sama hanya untuk pencarian berdasarkan ID.

Untuk melihat index yang tersedia:

SHOW INDEX FROM produk;

Hasilnya menampilkan informasi index, termasuk nama index, kolom, dan urutan kolom di dalam index.

Pada tabel InnoDB, primary key juga digunakan sebagai clustered index yang menentukan organisasi penyimpanan baris.

Membuat Index pada Kolom#

Misalnya, pengguna toko online sering mencari produk berdasarkan nama.

Query-nya:

SELECT
id_produk,
nama_produk,
harga,
stok
FROM produk
WHERE nama_produk = 'Keyboard Mechanical';

Kita dapat mempertimbangkan index pada kolom nama_produk.

CREATE INDEX idx_produk_nama
ON produk (nama_produk);

Nama idx_produk_nama merupakan nama index yang kita tentukan sendiri.

Setelah index dibuat, periksa:

SHOW INDEX FROM produk;

Sekarang tabel produk mempunyai index tambahan pada kolom nama.

Apakah Index Selalu Mempercepat Query?#

Tidak selalu.

Pada tabel yang hanya berisi beberapa produk, MySQL mungkin memilih membaca seluruh tabel karena biayanya lebih rendah.

Pada tabel yang jauh lebih besar, index dapat lebih bermanfaat untuk pencarian yang selektif.

Selain itu, setiap index membutuhkan ruang penyimpanan dan perlu dipelihara ketika data berubah.

Jadi, membuat index pada setiap kolom bukan strategi yang tepat.

Mengenal EXPLAIN#

Bagaimana cara mengetahui strategi yang dipilih MySQL ketika menjalankan query?

Kita bisa menggunakan EXPLAIN.

Contohnya:

EXPLAIN
SELECT
id_produk,
nama_produk,
harga,
stok
FROM produk
WHERE nama_produk = 'Keyboard Mechanical';

EXPLAIN menampilkan rencana eksekusi yang diperkirakan oleh optimizer.

Beberapa kolom penting yang sering muncul:

KolomKeterangan
tableTabel yang diakses
typeJenis metode akses tabel
possible_keysIndex yang berpotensi digunakan
keyIndex yang dipilih
rowsPerkiraan jumlah baris yang diperiksa
ExtraInformasi tambahan mengenai rencana query

Misalnya, jika kolom key berisi idx_produk_nama, berarti optimizer memilih index tersebut dalam rencana eksekusinya.

Jika key bernilai NULL, tidak ada index yang dipilih untuk akses tersebut.

Namun, hal ini tidak otomatis berarti query buruk.

Pada tabel kecil, membaca seluruh tabel bisa saja merupakan pilihan paling efisien.

Memahami Nilai Type#

Beberapa nilai type yang sering ditemui adalah:

TypeGambaran Umum
constAkses yang menghasilkan maksimal satu baris melalui kondisi tertentu pada key unik
eq_refPencarian satu baris berdasarkan seluruh bagian key unik pada setiap kombinasi baris sebelumnya
refPencarian menggunakan index nonunik atau bagian tertentu dari key
rangePencarian pada rentang nilai index
indexPemindaian index
ALLPemindaian seluruh tabel

Nilai tersebut membantu kita memahami pola akses.

Tetapi jangan hanya melihat satu kolom lalu menyimpulkan performa query.

Perhatikan juga jumlah baris, kondisi filter, ukuran data, dan waktu eksekusi.

Membandingkan Query Sebelum dan Sesudah Index#

Jika ingin melakukan eksperimen yang lebih terukur, gunakan tabel latihan yang cukup besar dan bandingkan query dengan kondisi yang sama.

Alurnya:

  1. Siapkan data uji dengan jumlah baris yang memadai.
  2. Jalankan EXPLAIN sebelum membuat index.
  3. Catat rencana eksekusi dan waktu pengujian.
  4. Buat index yang relevan.
  5. Jalankan kembali query yang sama.
  6. Bandingkan hasilnya.

Pada MySQL 8.0.18 atau lebih baru, tersedia juga:

EXPLAIN ANALYZE
SELECT
id_produk,
nama_produk,
harga
FROM produk
WHERE nama_produk = 'Keyboard Mechanical';

Berbeda dengan EXPLAIN biasa, EXPLAIN ANALYZE menjalankan query dan menampilkan informasi eksekusi aktual.

Gunakan dengan hati-hati, terutama untuk query berat.

Contoh pada artikel ini berupa SELECT, sehingga tidak mengubah data. Namun, eksekusinya tetap dapat menggunakan sumber daya server.

Mengenal Composite Index#

Selain index pada satu kolom, MySQL mendukung index yang terdiri dari beberapa kolom.

Index seperti ini disebut composite index atau index gabungan.

Misalnya, aplikasi sering mencari pesanan berdasarkan pelanggan dan tanggal pesanan.

Query-nya:

SELECT
id_pesanan,
id_pelanggan,
tanggal_pesanan,
status_pesanan
FROM pesanan
WHERE id_pelanggan = 1
ORDER BY tanggal_pesanan DESC;

Angka 1 hanya contoh ID pelanggan. Sesuaikan dengan data yang tersedia.

Kita dapat membuat index gabungan:

CREATE INDEX idx_pesanan_pelanggan_tanggal
ON pesanan (id_pelanggan, tanggal_pesanan);

Index tersebut menyusun key berdasarkan id_pelanggan terlebih dahulu, kemudian tanggal_pesanan.

Urutan Kolom pada Index Penting#

Composite index memiliki konsep leftmost prefix.

Untuk index:

(id_pelanggan, tanggal_pesanan)

Index tersebut dapat membantu query yang memfilter berdasarkan id_pelanggan.

Index tersebut juga berpotensi membantu query yang memfilter berdasarkan id_pelanggan sekaligus menggunakan tanggal_pesanan.

Namun, query yang hanya memfilter tanggal_pesanan belum tentu dapat memanfaatkan index tersebut secara efektif.

Optimizer pada versi MySQL tertentu mempunyai strategi tambahan, sehingga kita tetap perlu memeriksa EXPLAIN daripada menganggap suatu index pasti digunakan atau tidak digunakan.

Apakah Composite Index Menggantikan Semua Index Tunggal?#

Tidak selalu.

Index gabungan dapat membuat sebagian index tunggal menjadi redundan, terutama jika kolom awalnya sama.

Tetapi keputusan menghapus index harus mempertimbangkan kebutuhan query lain.

Selain itu, InnoDB dapat membuat index untuk mendukung foreign key apabila belum tersedia index yang sesuai.

Karena itu, sebelum menambahkan atau menghapus index, periksa struktur yang sudah ada:

SHOW INDEX FROM pesanan;

Jangan membuat index baru hanya karena nama kolom sering muncul dalam query.

Kapan Sebaiknya Membuat Index?#

Index biasanya layak dipertimbangkan pada kolom yang sering digunakan untuk:

  • Kondisi pencarian pada WHERE.
  • Penggabungan tabel melalui JOIN.
  • Pengurutan pada ORDER BY.
  • Pengelompokan tertentu pada GROUP BY.

Namun, tidak semua kondisi tersebut otomatis membutuhkan index baru.

Beberapa faktor yang perlu diperhatikan:

Selektivitas. Kolom yang mempunyai banyak nilai berbeda sering lebih berguna untuk pencarian selektif dibandingkan kolom yang hanya berisi sedikit variasi nilai.

Ukuran tabel. Pada tabel kecil, full table scan mungkin lebih murah.

Beban perubahan data. Setiap INSERT, UPDATE, dan DELETE dapat menambah pekerjaan pemeliharaan index.

Ruang penyimpanan. Index memerlukan ruang tambahan.

Pola query nyata. Index sebaiknya dibuat berdasarkan kebutuhan aplikasi, bukan sekadar perkiraan.

Kesimpulannya, index adalah alat optimasi, bukan koleksi yang harus dilengkapi semuanya.

Masalah Kedua: Perubahan Data yang Tidak Konsisten#

Sekarang kita beralih dari performa query ke keamanan perubahan data.

Bayangkan Andi membeli satu Keyboard Mechanical.

Aplikasi perlu melakukan beberapa pekerjaan:

  1. Memastikan stok tersedia.
  2. Membuat pesanan.
  3. Membuat detail pesanan.
  4. Mengurangi stok produk.

Jika seluruh langkah berhasil, transaksi dapat dianggap sukses.

Tetapi bagaimana jika langkah ketiga gagal setelah langkah kedua berhasil?

Kita bisa mempunyai pesanan tanpa detail.

Atau bagaimana jika stok sudah berkurang, tetapi pesanan gagal tersimpan?

Data menjadi tidak konsisten.

Untuk mengatasi masalah tersebut, kita membutuhkan database transaction.

Apa Itu Transaction?#

Transaction adalah satu unit pekerjaan yang terdiri dari satu atau lebih operasi database.

Pada database transaksional seperti InnoDB, perubahan yang termasuk dalam satu transaction dapat diselesaikan bersama melalui COMMIT atau dibatalkan melalui ROLLBACK, selama perubahan tersebut memang mendukung transaksi.

Contoh sederhananya:

Mulai transaksi
|
Buat pesanan
|
Buat detail pesanan
|
Kurangi stok
|
Semua berhasil?
/ \
Ya Tidak
| |
COMMIT ROLLBACK

Dengan transaction, kita bisa menjaga agar perubahan data tidak berhenti dalam keadaan setengah selesai.

Mengenal Konsep ACID#

Transaction pada database relasional sering dijelaskan menggunakan empat sifat yang disebut ACID.

1. Atomicity#

Atomicity berarti seluruh perubahan dalam satu transaction diperlakukan sebagai satu kesatuan.

Jika transaksi dibatalkan, perubahan transaksional yang belum di-commit dapat dikembalikan.

Contohnya, pembuatan pesanan dan pengurangan stok seharusnya berhasil bersama.

2. Consistency#

Consistency berarti transaksi harus menjaga aturan konsistensi yang ditentukan database dan aplikasi.

Misalnya:

  • Foreign key harus valid.
  • Primary key tidak boleh duplikat.
  • Stok tidak boleh menjadi negatif menurut aturan bisnis kita.

Perlu diperhatikan bahwa tidak semua aturan bisnis otomatis dijamin oleh database.

Jika aplikasi mengizinkan stok negatif tanpa constraint atau validasi yang tepat, transaction saja tidak akan memperbaiki aturan tersebut.

3. Isolation#

Isolation berkaitan dengan bagaimana transaksi yang berjalan bersamaan saling berinteraksi.

Misalnya, dua pelanggan mencoba membeli produk terakhir pada waktu yang hampir sama.

Database perlu mengelola konkurensi agar transaksi tidak saling mengganggu secara tidak semestinya.

InnoDB menyediakan beberapa isolation level dengan karakteristik berbeda.

Pada artikel ini, kita menggunakan transaksi dan penguncian baris sebagai pengenalan.

4. Durability#

Durability berarti setelah transaksi berhasil di-commit, perubahan dirancang untuk tetap tersimpan meskipun terjadi kegagalan sistem, sesuai jaminan dan konfigurasi durability database.

Jaminan praktisnya tetap bergantung pada konfigurasi, media penyimpanan, dan mekanisme pemulihan.

Keempat sifat tersebut membantu menjelaskan mengapa transaction menjadi bagian penting dalam aplikasi yang mengelola data bisnis.

START TRANSACTION, COMMIT, dan ROLLBACK#

Tiga perintah dasar yang perlu kita pahami adalah:

PerintahFungsi
START TRANSACTIONMemulai transaksi baru
COMMITMenyimpan perubahan transaksi secara permanen
ROLLBACKMembatalkan perubahan yang belum di-commit

Mari kita praktikkan dengan contoh sederhana.

Contoh COMMIT#

Kita akan menambahkan produk latihan.

START TRANSACTION;
INSERT INTO produk (
nama_produk,
harga,
stok
)
VALUES (
'Produk Latihan Transaksi',
10000,
5
);
COMMIT;

Setelah COMMIT, data tersebut tersimpan.

Periksa:

SELECT *
FROM produk
WHERE nama_produk = 'Produk Latihan Transaksi';

Jika kamu menjalankan contoh ini berulang kali, beberapa produk dengan nama sama dapat terbentuk.

Karena itu, cukup jalankan sekali untuk latihan.

Contoh ROLLBACK#

Sekarang kita mencoba mengubah harga produk latihan, tetapi kemudian membatalkannya.

START TRANSACTION;
UPDATE produk
SET harga = 99999
WHERE nama_produk = 'Produk Latihan Transaksi';
SELECT
nama_produk,
harga
FROM produk
WHERE nama_produk = 'Produk Latihan Transaksi';
ROLLBACK;

Periksa kembali:

SELECT
nama_produk,
harga
FROM produk
WHERE nama_produk = 'Produk Latihan Transaksi';

Jika tidak ada perubahan lain yang memengaruhi baris tersebut, harga kembali ke nilai sebelum transaksi dimulai.

Inilah fungsi ROLLBACK.

Catatan DBeaver: Jalankan rangkaian perintah transaksi pada koneksi atau sesi yang sama. Jangan memulai transaksi pada satu koneksi lalu menjalankan COMMIT atau ROLLBACK pada koneksi berbeda.

Penting: ROLLBACK Tidak Membatalkan Semua Jenis Perintah#

Ada hal yang perlu diperhatikan.

Pada MySQL, banyak perintah DDL seperti CREATE TABLE, ALTER TABLE, dan DROP TABLE menyebabkan implicit commit.

Artinya, jangan menganggap semua perubahan struktur database bisa dibatalkan menggunakan ROLLBACK seperti perubahan data InnoDB biasa.

Contoh latihan transaction kita menggunakan operasi DML seperti INSERT dan UPDATE.

Ketika melakukan perubahan struktur database, periksa dokumentasi dan siapkan strategi migrasi serta backup yang sesuai.

Simulasi Transaksi Pembelian yang Aman#

Sekarang kita membuat contoh yang lebih mendekati aplikasi nyata.

Skenarionya:

Andi membeli satu Keyboard Mechanical.

Sistem perlu:

  1. Mengambil ID pelanggan dan produk.
  2. Memastikan stok tersedia.
  3. Membuat pesanan.
  4. Membuat detail pesanan.
  5. Mengurangi stok.
  6. Melakukan COMMIT hanya jika seluruh langkah berhasil.

Kita akan menggunakan SQL pada MySQL 8 dengan InnoDB.

Penting: Contoh berikut adalah latihan manual yang dijalankan bertahap dalam satu sesi database. Jangan menjalankan semua perintah tanpa memeriksa hasil di setiap tahap.

Langkah 1 — Memeriksa Pelanggan dan Produk#

Cari pelanggan:

SELECT
id_pelanggan,
nama_pelanggan,
email
FROM pelanggan
WHERE email = 'andi@example.com';

Cari produk:

SELECT
id_produk,
nama_produk,
harga,
stok
FROM produk
WHERE nama_produk = 'Keyboard Mechanical';

Pastikan pelanggan dan produk yang dimaksud tersedia.

Jika ada beberapa baris dengan nama atau email sama, identifikasi data yang benar terlebih dahulu.

Catat ID yang akan digunakan.

Langkah 2 — Memulai Transaksi dan Mengunci Produk#

Pada satu sesi koneksi MySQL, jalankan:

START TRANSACTION;

Kemudian kunci baris produk yang akan dibeli:

SELECT
id_produk,
nama_produk,
harga,
stok
FROM produk
WHERE id_produk = 1
FOR UPDATE;

Ganti 1 dengan ID produk yang sebenarnya.

FOR UPDATE meminta InnoDB mengunci baris yang dipilih untuk operasi pembaruan selama transaksi berlangsung.

Hal ini membantu mencegah transaksi lain mengubah baris yang sama secara bersamaan tanpa menunggu mekanisme locking InnoDB.

Pastikan stok minimal satu unit.

Jika stok tidak tersedia, jalankan:

ROLLBACK;

Lalu hentikan latihan transaksi.

Jika stok tersedia, lanjutkan.

Langkah 3 — Membuat Pesanan#

Gunakan ID pelanggan yang sudah diperiksa.

Misalnya:

INSERT INTO pesanan (
id_pelanggan,
status_pesanan
)
VALUES (
1,
'baru'
);

Ganti 1 dengan ID pelanggan yang benar.

Setelah INSERT berhasil, ambil ID pesanan baru:

SET @id_pesanan_baru = LAST_INSERT_ID();

LAST_INSERT_ID() memberikan nilai AUTO_INCREMENT terakhir yang dihasilkan oleh operasi yang relevan pada koneksi tersebut.

Karena itu, pastikan langkah-langkah ini dijalankan pada sesi koneksi yang sama.

Langkah 4 — Membuat Detail Pesanan#

Sekarang kita menyimpan detail pembelian.

Contoh:

INSERT INTO detail_pesanan (
id_pesanan,
id_produk,
jumlah,
harga_satuan
)
VALUES (
@id_pesanan_baru,
1,
1,
325000
);

Ganti ID produk 1 dengan ID produk yang benar.

Harga 325000 merupakan harga transaksi contoh.

Pada aplikasi nyata, harga transaksi harus ditentukan dari sumber tepercaya dan aturan harga yang berlaku, bukan langsung mempercayai angka dari pengguna.

Langkah 5 — Mengurangi Stok#

Sekarang kita mengurangi stok satu unit.

Gunakan query:

UPDATE produk
SET stok = stok - 1
WHERE id_produk = 1
AND stok >= 1;

Ganti ID produk 1 dengan ID yang sama seperti langkah sebelumnya.

Kondisi stok >= 1 penting agar pengurangan hanya dilakukan ketika stok masih tersedia.

Setelah UPDATE, periksa jumlah baris yang terpengaruh:

SELECT ROW_COUNT() AS jumlah_baris_diubah;

Untuk transaksi pembelian satu produk, hasil yang diharapkan adalah 1.

Jika hasilnya 0, stok mungkin tidak mencukupi atau ID produk tidak cocok.

Dalam kondisi tersebut, jangan melakukan COMMIT.

Jalankan:

ROLLBACK;

Langkah 6 — COMMIT Jika Seluruh Proses Berhasil#

Jika pembuatan pesanan, pembuatan detail, dan pengurangan stok semuanya berhasil, jalankan:

COMMIT;

Dengan begitu, seluruh perubahan transaksi disimpan bersama.

Namun, jika ada error pada salah satu langkah, lakukan:

ROLLBACK;

Perlu diingat: MySQL tidak otomatis membatalkan seluruh transaksi setiap kali sebuah statement gagal. Dalam aplikasi, penanganan error harus secara eksplisit menentukan kapan ROLLBACK dilakukan.

Jika koneksi terputus sebelum COMMIT, InnoDB umumnya akan membatalkan transaksi yang belum selesai, tetapi aplikasi tetap perlu menangani status hasil transaksi yang mungkin tidak pasti jika gangguan terjadi di sekitar proses COMMIT.

Kenapa Tidak Cukup Menggunakan UPDATE Stok Saja?#

Mungkin kamu bertanya:

Bukankah kita bisa langsung menjalankan:

UPDATE produk
SET stok = stok - 1
WHERE id_produk = 1
AND stok >= 1;

Query tersebut memang membantu mencegah stok berkurang ketika jumlahnya tidak mencukupi.

Namun, pembelian tidak hanya mengubah stok.

Kita juga perlu membuat pesanan dan detail pesanan.

Tanpa transaction, ketiga perubahan tersebut dapat berhasil atau gagal secara terpisah.

Dengan transaction, kita bisa mengelola semuanya sebagai satu proses bisnis.

Pada implementasi backend, operasi tersebut biasanya dijalankan menggunakan satu koneksi database yang sama, dengan COMMIT ketika sukses dan ROLLBACK ketika gagal.

Mengenal Locking pada InnoDB#

Saat membahas transaction, kita juga perlu mengenal locking.

Locking merupakan mekanisme yang membantu database mengatur akses bersamaan terhadap data.

Misalnya, stok keyboard tinggal satu unit.

Andi dan Budi sama-sama menekan tombol beli.

Jika aplikasi membaca stok secara terpisah tanpa pengendalian konkurensi yang tepat, keduanya bisa sama-sama menganggap barang masih tersedia.

Penguncian baris dan UPDATE bersyarat dapat membantu mencegah kondisi tersebut.

Contohnya:

START TRANSACTION;
SELECT stok
FROM produk
WHERE id_produk = 1
FOR UPDATE;

Selama transaksi memegang lock yang relevan, transaksi lain yang membutuhkan lock yang bertentangan mungkin harus menunggu.

Setelah transaksi melakukan COMMIT atau ROLLBACK, lock transaksional tersebut dilepaskan.

Namun, locking juga memiliki konsekuensi.

Jika transaksi dibiarkan terbuka terlalu lama, transaksi lain dapat menunggu dan performa aplikasi menurun.

Karena itu, transaksi sebaiknya dibuat sesingkat mungkin.

Jangan membiarkan transaksi database terbuka sambil menunggu pengguna menyelesaikan pembayaran atau mengisi formulir selama beberapa menit.

Mengenal Deadlock#

Deadlock terjadi ketika dua atau lebih transaksi saling menunggu lock yang dipegang transaksi lainnya sehingga tidak ada yang bisa melanjutkan tanpa intervensi.

Contoh sederhana:

  • Transaksi A mengunci produk 1, lalu mencoba mengunci produk 2.
  • Transaksi B mengunci produk 2, lalu mencoba mengunci produk 1.

Keduanya dapat saling menunggu.

InnoDB mempunyai mekanisme pendeteksian deadlock dan dapat memilih salah satu transaksi sebagai korban untuk dibatalkan.

Aplikasi perlu menangani kondisi tersebut, misalnya dengan melakukan retry secara terbatas terhadap keseluruhan unit transaksi yang aman untuk diulang.

Beberapa praktik yang membantu mengurangi risiko deadlock:

  • Mengakses tabel dan baris dalam urutan yang konsisten.
  • Menjaga transaksi tetap singkat.
  • Menggunakan index yang sesuai agar operasi locking lebih terarah.
  • Menghindari transaksi yang melakukan terlalu banyak pekerjaan sekaligus.
  • Menyiapkan penanganan retry untuk error yang memang layak dicoba kembali.

Transaction membuat perubahan data lebih aman, tetapi tetap perlu dirancang dengan baik.

Autocommit pada MySQL#

Secara umum, MySQL menggunakan mode autocommit yang aktif secara default.

Dalam mode tersebut, setiap statement yang berhasil biasanya di-commit secara otomatis jika tidak berada dalam transaksi eksplisit.

Periksa pengaturannya:

SELECT @@autocommit;

Jika hasilnya 1, autocommit aktif.

Ketika kita menjalankan:

START TRANSACTION;

Kita memulai transaksi eksplisit yang dapat diselesaikan dengan COMMIT atau ROLLBACK.

Perlu diperhatikan bahwa beberapa database client, termasuk DBeaver, mempunyai pengaturan transaction mode tersendiri.

Karena itu, pahami apakah editor sedang menggunakan auto-commit atau manual commit sebelum melakukan perubahan data penting.

Latihan Mandiri#

Sekarang waktunya mencoba.

Gunakan database db_toko_bapakgabut.

Latihan 1 — Membuat Index#

Buat index pada kolom email di tabel pelanggan dengan nama:

idx_pelanggan_email

Kemudian periksa hasilnya menggunakan SHOW INDEX.

Latihan 2 — Menggunakan EXPLAIN#

Jalankan EXPLAIN untuk query berikut:

SELECT *
FROM pelanggan
WHERE email = 'andi@example.com';

Perhatikan kolom possible_keys, key, type, dan rows.

Apakah index yang kamu buat digunakan?

Jika tidak, coba pikirkan mengapa optimizer mungkin memilih strategi lain.

Latihan 3 — Composite Index#

Buat composite index pada tabel pesanan untuk kolom:

(status_pesanan, tanggal_pesanan)

Gunakan nama:

idx_pesanan_status_tanggal

Latihan 4 — ROLLBACK#

Buat sebuah transaksi yang menaikkan harga salah satu produk sebesar Rp10.000.

Kemudian jalankan ROLLBACK.

Pastikan harga produk kembali seperti sebelum transaksi.

Latihan 5 — Transaksi Pembelian#

Jelaskan urutan proses pembelian satu produk menggunakan transaction.

Apa yang harus dilakukan jika:

  • INSERT pesanan berhasil.
  • INSERT detail pesanan berhasil.
  • UPDATE stok gagal karena stok tidak mencukupi?

Apakah transaksi boleh di-COMMIT?

Coba jawab sebelum melihat pembahasan.

Pembahasan Latihan#

Jawaban Latihan 1#

CREATE INDEX idx_pelanggan_email
ON pelanggan (email);

Periksa:

SHOW INDEX FROM pelanggan;

Index pada email membantu menyediakan jalur akses untuk pencarian berdasarkan kolom tersebut.

Perlu diingat, index biasa tidak otomatis membuat nilai email menjadi unik.

Jika aplikasi membutuhkan email unik, aturan UNIQUE harus dirancang secara eksplisit setelah memastikan data memenuhi ketentuannya.

Jawaban Latihan 2#

EXPLAIN
SELECT *
FROM pelanggan
WHERE email = 'andi@example.com';

Periksa apakah kolom key menampilkan idx_pelanggan_email.

Jika tidak, belum tentu index tersebut salah.

Optimizer bisa memilih full table scan ketika tabel masih sangat kecil.

Jawaban Latihan 3#

CREATE INDEX idx_pesanan_status_tanggal
ON pesanan (status_pesanan, tanggal_pesanan);

Periksa:

SHOW INDEX FROM pesanan;

Index ini dapat dipertimbangkan untuk query yang sering memfilter status pesanan dan menggunakan tanggal pesanan.

Tetap gunakan EXPLAIN untuk mengevaluasi manfaatnya.

Jawaban Latihan 4#

Pertama, tentukan ID produk yang akan diuji.

Misalnya, setelah memeriksa ID produk, jalankan:

START TRANSACTION;
UPDATE produk
SET harga = harga + 10000
WHERE id_produk = 1;
SELECT
id_produk,
nama_produk,
harga
FROM produk
WHERE id_produk = 1;
ROLLBACK;

Ganti 1 dengan ID produk yang benar.

Setelah ROLLBACK, periksa kembali harga produk:

SELECT
id_produk,
nama_produk,
harga
FROM produk
WHERE id_produk = 1;

Jika transaksi belum di-commit dan tidak ada perubahan lain yang memengaruhi baris tersebut, harga kembali ke nilai sebelumnya.

Jawaban Latihan 5#

Jika pembuatan pesanan dan detail berhasil, tetapi pengurangan stok gagal, transaksi tidak boleh di-COMMIT sebagai pembelian berhasil.

Aplikasi harus menjalankan ROLLBACK untuk membatalkan perubahan yang sudah dilakukan dalam transaksi tersebut.

Setelah itu, aplikasi dapat mengembalikan pesan bahwa stok tidak mencukupi atau transaksi gagal.

Dengan begitu, database tidak menyimpan pesanan baru yang seolah berhasil ketika stok sebenarnya tidak tersedia.

Kesimpulan#

Pada Bab 7 ini, kita sudah mempelajari dua konsep penting dalam pengembangan database.

Index membantu MySQL menemukan data dengan lebih efisien pada kondisi yang sesuai.

Kita mengenal index tunggal, composite index, konsep leftmost prefix, dan penggunaan EXPLAIN untuk memeriksa rencana eksekusi query.

Kita juga memahami bahwa terlalu banyak index dapat menambah biaya penyimpanan dan perubahan data.

Sementara itu, transaction membantu mengelola beberapa operasi database sebagai satu kesatuan.

Kita sudah mempelajari konsep ACID, START TRANSACTION, COMMIT, ROLLBACK, locking, dan deadlock.

Melalui simulasi pembelian, kita melihat mengapa pembuatan pesanan, pencatatan detail, dan pengurangan stok perlu dikelola secara konsisten.

Sekarang database toko online kita bukan hanya mampu menyimpan data dan menghasilkan laporan.

Kita juga mulai memahami cara membuat akses data lebih efisien dan proses perubahan data lebih aman.

Pada Bab 8, kita akan menutup series ini dengan studi kasus lengkap:

Studi Kasus MySQL: Membuat Database Toko Online Sederhana.

Kita akan menyusun kembali struktur database, relasi, data contoh, query laporan, serta alur transaksi menjadi satu studi kasus yang bisa dipraktikkan dari awal.

Sampai jumpa di bab terakhir, Gabuters!

Bagian 7 dari 8Tulisan ini bagian dari seri

Bagikan Artikel

Buat poster berbagi atau salin tautan artikel ini.

Lanjut membaca

Bacaan terkait

Berdasarkan tag dan kategori yang sama

Coba jalur lain

Pilihan konsisten dari artikel lain