Topik ini membahas connection pooling, yaitu penampung koneksi MySQL yang sudah terbuka dan dipinjamkan kepada kode aplikasi sesuai kebutuhan. Pembahasan mencakup biaya koneksi baru, cara pool bekerja, demo pool pada driver mysql2, serta intuisi menentukan ukurannya. Setelah menyelesaikannya, keputusan ukuran pool pada aplikasi web dapat diambil dengan dasar yang jelas alih-alih menebak.
Biaya koneksi baru pada beban web#
Membuka satu koneksi MySQL bukan operasi yang gratis. Prosesnya mencakup sambungan TCP, pertukaran paket autentikasi, dan alokasi sesi di server, yaitu sebuah thread beserta buffer sesinya. Pada server lokal, rangkaian itu biasanya selesai dalam milidetik, tetapi biayanya membengkak ketika diulang terus-menerus. Aplikasi web yang membuka koneksi baru untuk setiap permintaan HTTP pada situs sibuk membuka dan menutup koneksi ratusan ribu kali sehari. Padahal tiap koneksi dipakai hanya untuk beberapa query yang selesai dalam hitungan milidetik.
Server MySQL juga membatasi jumlah koneksi serentak melalui variabel max_connections, yang nilainya pada instalasi baru adalah 151. Aplikasi yang boros koneksi mudah menyentuh batas tersebut, sehingga server mulai menolak permintaan dengan error Too many connections. Pemborosan semacam ini dapat dihindari karena koneksi pada dasarnya dapat dipakai ulang.
Cara kerja pool koneksi#
Pool mengatasi pemborosan tersebut dengan membuat beberapa koneksi sekali di awal, menjaganya tetap terbuka, lalu meminjamkannya. Kode aplikasi meminta satu koneksi dari pool, memakainya untuk satu atau beberapa query, dan mengembalikannya begitu selesai. Selama koneksi dikembalikan dengan benar, koneksi yang sama dipakai ulang oleh permintaan berikutnya tanpa mengulang sambungan TCP dan autentikasi.
Apabila seluruh koneksi sedang dipakai, permintaan baru menunggu dalam antrean sampai ada koneksi yang kembali atau sampai batas waktu tunggu tercapai. Perilaku ini menjadikan pool sebagai katup penyeimbang, karena jumlah koneksi ke server tetap terkendali meskipun jumlah permintaan aplikasi berfluktuasi. Driver mysql2 menyediakan pool lewat createPool(), dan metode pool.query() pada pool tersebut otomatis meminjam koneksi, menjalankan query, lalu mengembalikannya. Konsep yang sama tersedia pada sisi Python, yaitu kelas MySQLConnectionPool milik Connector/Python dan komponen pool pada pustaka seperti SQLAlchemy, dengan pola pinjam dan kembalikan yang serupa.
Demo pool dengan mysql2#
Demo berikut membuat pool berukuran dua koneksi lalu mengirim enam query seolah-olah datang dari enam permintaan web yang bersamaan. Setiap query membaca satu baris dari tabel anggota.
# menyiapkan basis data latihan dari keadaan kosong
mysql -u root -e "DROP DATABASE IF EXISTS perpustakaan;"
mysql -u root -e "CREATE DATABASE perpustakaan;"
mysql -u root perpustakaan <<'SQL'
CREATE TABLE anggota (
id INT PRIMARY KEY,
nama VARCHAR(60) NOT NULL,
kota VARCHAR(40) NOT NULL
);
INSERT INTO anggota (id, nama, kota) VALUES
(1, 'Ali', 'Bandung'),
(2, 'Budi', 'Semarang'),
(3, 'Citra', 'Surabaya'),
(4, 'Dewi', 'Yogyakarta'),
(5, 'Eka', 'Medan'),
(6, 'Fajar', 'Makassar');
SQL// tidak-dijalankan
const mysql = require("mysql2/promise");
async function utama() {
const opsi = {
host: "127.0.0.1",
user: "root",
database: "perpustakaan",
connectionLimit: 2,
};
const pool = mysql.createPool(opsi);
console.log("Ukuran pool:", opsi.connectionLimit);
const janji = [];
for (let id = 1; id <= 6; id++) {
janji.push(
pool
.query("SELECT nama FROM anggota WHERE id = ?", [id])
.then(([baris]) => baris[0].nama)
);
}
const nama = await Promise.all(janji);
nama.forEach((satu, indeks) => console.log(`Tugas ${indeks + 1}: ${satu}`));
await pool.end();
}
utama();Objek opsi menyimpan connectionLimit: 2 yang menjadi ukuran maksimum pool, dan nilai itu dicetak pada baris pertama keluaran. Enam pemanggilan pool.query() dijanjikan bersamaan melalui Promise.all, sedangkan urutan pencetakan tetap mengikuti urutan tugas karena Promise.all menyusun hasil sesuai urutan janji yang dikirim, bukan sesuai waktu setiap janji selesai. Dengan demikian keluaran enam tugas itu terurut rapi meskipun eksekusinya bertumpuk.
# menjalankan demo pool
node pool.jsUkuran pool: 2
Tugas 1: Ali
Tugas 2: Budi
Tugas 3: Citra
Tugas 4: Dewi
Tugas 5: Eka
Tugas 6: FajarPada setiap saat, aplikasi ini membuka paling banyak dua koneksi ke server karena pool membagi enam query itu ke dua koneksi yang tersedia dan menahan sisanya dalam antreannya. Perilaku inilah yang diinginkan pada aplikasi web, yaitu jumlah permintaan boleh melonjak, tetapi jumlah koneksi ke basis data tetap setara ukuran pool.
Menentukan ukuran pool#
Ukuran pool adalah keseimbangan antara dua kondisi ekstrem yang saling berlawanan. Pool yang terlalu kecil membuat permintaan menunggu lama dalam antrean padahal server basis data belum sibuk, sehingga latensi aplikasi naik tanpa alasan. Pool yang terlalu besar memaksa server memelihara banyak thread dan buffer sesi sekaligus, dan jumlah query yang bersaing memperebutkan CPU serta disk justru memperlambat semuanya.
Titik awal yang lazim adalah ukuran sekitar dua kali jumlah inti CPU server basis data, lalu disesuaikan dengan pengamatan pada beban nyata. Apabila aplikasi web dijalankan sebagai beberapa proses, kapasitas server diperhitungkan terhadap total ukuran pool seluruh proses, sehingga jumlah proses ikut dihitung. Bila server basis data dikelola sendiri, nilai max_connections menjadi batas atas bagi seluruh aplikasi yang berbagi server tersebut.
Kesalahan umum#
Koneksi pinjaman tidak dikembalikan. Pool yang dikelola manual dengan getConnection() menuntut pemanggilan release(). Apabila alur kode melempar error sebelum koneksi dikembalikan, koneksi itu menganggur selamanya, pool kehabisan koneksi, dan permintaan berikutnya menunggu sampai batas waktu tunggunya. Pola try/finally berikut menjaga pengembaliannya.
// tidak-dijalankan
const koneksi = await pool.getConnection();
try {
await koneksi.query("SELECT 1");
} finally {
koneksi.release();
}Metode pool.query() yang dipakai pada demo menghindari kelalaian ini karena pinjam dan kembalikannya ditangani driver secara internal.
Pool dibuat baru pada setiap permintaan. Pool hanya bermanfaat bila hidup sepanjang proses aplikasi, sehingga koneksinya dapat dipakai ulang. Membuat pool di dalam fungsi yang dipanggil per permintaan sama saja dengan membuka koneksi baru, ditambah sisa koneksi yang tertahan sampai batas waktunya. Pool dideklarasikan sekali ketika aplikasi dimulai, lalu digunakan bersama oleh seluruh permintaan.
Ringkasan#
- Pool menjaga sekelompok koneksi tetap terbuka dan meminjamkannya, sehingga sambungan TCP dan autentikasi tidak diulang pada setiap permintaan.
- Bila semua koneksi sedang dipakai, permintaan menunggu dalam antrean pool, dan jumlah koneksi ke server tetap setara ukuran pool.
- Demo mysql2 dengan enam query bersamaan pada pool berukuran dua menunjukkan antrean bekerja tanpa menambah koneksi.
- Ukuran pool dimulai dari sekitar dua kali jumlah inti CPU server basis data, lalu disesuaikan dengan pengamatan beban dan jumlah proses aplikasi.
Referensi#
- mysql2 — npm
— Dokumentasi
createPoolbeserta opsiconnectionLimitdan perilaku antreannya. - Server System Variables — Oracle
— Uraian variabel
max_connectionsyang membatasi jumlah koneksi serentak. - MySQL Connectors and APIs — Oracle — Konektor resmi MySQL, termasuk dukungan pooling pada Connector/Python.