Error 1040: Too many connections, respons pertama kebanyakan pengelola server adalah membuka file konfigurasi my.cnf lalu menaikkan parameter max_connections. Nilai default yang awalnya 151 sering langsung diubah menjadi 500, 1000, atau bahkan lebih tinggi dengan harapan sistem tidak lagi menolak pengunjung. Masalahnya, langkah ini sering kali berakhir dengan server yang mendadak tidak responsif, kehabisan memori fisik, lalu proses mysqld mati dibunuh oleh kernel Linux.
Di meja dukungan teknis hosting, pola seperti ini sangat sering terlihat. Menaikkan batas koneksi tanpa memahami alokasi memori MySQL ibarat memperlebar pintu masuk ke dalam ruangan yang kapasitas lantainya tetap sama. Begitu lonjakan pengunjung benar-benar datang, MySQL bukan sekadar melayani lebih banyak orang, melainkan mengalokasikan ruang memori baru untuk setiap sambungan yang masuk sampai sistem operasi kehabisan RAM.
Perbedaan memori global dan alokasi per-thread
innodb_buffer_pool_size, yang berfungsi menyimpan cache data dan indeks tabel di dalam RAM agar sistem tidak perlu terus-menerus membaca disk. Kita sudah pernah membahas cara mengukur kebutuhan kapasitas ini secara detail di panduan Cara Memastikan Ukuran InnoDB Buffer Pool di Server Sudah Pas. Komponen global lainnya meliputi key_buffer_size untuk MyISAM dan query cache pada versi MySQL lawas.
Di sisi lain, ada yang disebut per-thread buffers. Setiap kali aplikasi web seperti WordPress atau Laravel membuka satu koneksi ke MySQL, server database akan membuat satu thread (jalur eksekusi proses) khusus untuk melayani koneksi tersebut. Thread ini tidak gratis; ia membawa alokasi memorinya sendiri. Alokasi ini mencakup buffer dasar seperti thread_stack, net_buffer_length, serta beberapa buffer dinamis yang dipanggil saat query tertentu dijalankan, seperti sort_buffer_size untuk operasi pengurutan data (ORDER BY), join_buffer_size untuk penggabungan tabel tanpa indeks, dan read_rnd_buffer_size.
Jika kamu menetapkan max_connections = 1000, maka secara teoritis MySQL diizinkan membuka hingga 1000 thread sekaligus. Bila masing-masing thread rata-rata menghabiskan memori gabungan beberapa megabyte ketika menjalankan query yang berat, konsumsi memori per-thread bisa melonjak dari hitungan ratusan megabyte menjadi belasan gigabyte hanya dalam hitungan detik. Ketika total penggunaan RAM melewati kapasitas fisik dan ruang swap habis, mekanisme Out of Memory (OOM) Killer pada kernel Linux akan turun tangan dan menghentikan proses dengan penggunaan memori terbesar, yang umumnya adalah mysqld.
Langkah 1: Memeriksa riwayat koneksi riil dan thread aktif
BASH
mysql -e "SHOW GLOBAL STATUS WHERE Variable_name IN ('Max_used_connections', 'Threads_connected', 'Threads_running');"TEXT
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 151 |
| Threads_connected | 42 |
| Threads_running | 3 |
+----------------------+-------+Threads_connectedmenunjukkan berapa banyak koneksi yang saat ini sedang terbuka antara aplikasi dan database server. Ini mencakup koneksi yang sedang aktif mengeksekusi query maupun yang sedang menganggur (idle atau sleep).Threads_runningmenunjukkan berapa banyak koneksi yang detik ini benar-benar sedang memproses perintah secara aktif. Pada server yang sehat dengan CPU multi-core, angka ini biasanya berada di angka satuan atau belasan kecil, jarang sekali menyentuh puluhan secara bersamaan.Max_used_connectionsmencatat titik puncak koneksi tertinggi yang pernah tercapai sejak MySQL terakhir kali dinyalakan. Jika nilai ini sama persis dengan batasanmax_connectionsyang kamu tetapkan, berarti server memang sempat membentur batas atas.
Threads_connected tinggi tetapi Threads_running sangat rendah (misalnya hanya 1 atau 2), masalah utamanya bukan karena server kekurangan kapasitas komputasi, melainkan koneksi aplikasi yang dibiarkan terbuka terlalu lama tanpa ditutup dengan rapi.
Langkah 2: Mengaudit daftar query dan kondisi lock
BASH
mysql -e "SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE command != 'Sleep' ORDER BY time DESC LIMIT 10;"time dan state pada hasil output. Jika kamu melihat banyak query dengan status Waiting for table metadata lock, Copying to tmp table, atau Sorting result yang memiliki nilai time puluhan detik, query-query inilah biang keladinya. Koneksi baru yang datang akan terus bertumpuk di belakang antrean menunggu giliran, hingga akhirnya kuota max_connections habis.
Dalam beberapa kasus, beban query lambat ini bahkan membuat antrean I/O penyimpanan menjadi macet parah. Situasi seperti ini kerap membingungkan karena seperti yang diulas pada artikel Kenapa VPS Tetap Lemot Padahal Pemakaian CPU Masih Rendah, bottleneck disk bisa menahan ratusan proses tanpa membuat grafik pemakaian prosesor tampak penuh.
Langkah 3: Menyelaraskan batas koneksi dengan pool aplikasi
max_connections di MySQL tetapi tidak membatasi proses di lapisan aplikasi, kamu hanya memindahkan titik kegagalan dari pesan error aplikasi menjadi crash server total. Database tidak bekerja di ruang hampa; ia menerima koneksi dari web server dan application server.
Sebagai contoh, jika kamu memakai PHP-FPM di server yang sama, jumlah koneksi maksimal yang bisa dibuat oleh PHP secara bersamaan dibatasi oleh parameter pm.max_children. Jika total pm.max_children di seluruh pool PHP kamu hanya berjumlah 80 worker, maka PHP secara fisik tidak akan pernah bisa membuat lebih dari 80 koneksi bersamaan ke MySQL (kecuali jika aplikasi menggunakan koneksi persisten yang tidak terkelola atau ada skrip eksternal lain).
Menetapkan max_connections = 1000 di server yang hanya menjalankan worker aplikasi berjumlah puluhan adalah tindakan sia-sia yang berisiko tinggi. Aturan praktis yang lebih aman adalah menghitung:
Kapasitas max_connections = (Total worker aplikasi aktif) + (Koneksi cadangan untuk maintenance)
MySQL secara bawaan menyediakan satu slot koneksi ekstra di atas max_connections khusus untuk pengguna dengan hak akses CONNECTION_ADMIN atau SUPER. Jadi jika limit tercapai, administrator masih bisa login via terminal untuk melakukan investigasi dan mematikan query yang bermasalah (kill thread).
Selain itu, periksa parameter wait_timeout dan interactive_timeout. Nilai bawaan MySQL untuk kedua variabel ini biasanya adalah 28800 detik (8 jam). Untuk aplikasi web dinamis, membiarkan koneksi yang sudah selesai bekerja berada dalam status Sleep selama berjam-jam hanya akan membuang slot koneksi dan menahan alokasi memori. Menurunkannya ke angka yang lebih masuk akal—seperti 30 hingga 60 detik—sering kali langsung membebaskan puluhan slot koneksi tanpa perlu mengubah konfigurasi lainnya.
Menyelesaikan error Too many connections membutuhkan penelusuran dari akar penyebab: optimasi indeks pada slow query, pemangkasan timeout koneksi menganggur, dan pembatasan konkurensi di tingkat web server. Anggap max_connections sebagai sekring pengaman, bukan keran penambah tenaga; membesarkan ukuran sekring tanpa memperbaiki korsleting di dalam sirkuit hanya akan membakar seluruh perangkat yang terhubung.