df -h. Hasilnya sering membingungkan, karena partisi root masih lega dan disk kelihatannya tidak penuh. Error-nya tetap muncul, umumnya saat query berat, import database, atau ALTER TABLE di tabel besar. Dua hal ini bisa sama-sama benar. Kita telusuri penyebabnya satu per satu.
Apa sebenarnya arti Errcode 28
ENOSPC, dan MySQL hanya meneruskannya. Artinya kernel menolak permintaan menulis karena filesystem tujuan tidak bisa menampung data baru.
Ada dua hal penting di sini. Pertama, kernel mengembalikan ENOSPC tidak hanya saat blok data habis, tapi juga saat filesystem kehabisan inode untuk membuat file baru. Kedua, yang penuh adalah filesystem tempat file itu ditulis, dan itu belum tentu partisi yang kamu lihat di baris paling atas output df -h.
Petunjuk pertama ada di pesan error-nya sendiri. Perhatikan path file yang disebut:
TEXT
# contoh format output, bukan dari insiden nyata
ERROR 3 (HY000): Error writing file '/tmp/MYfd=57' (Errcode: 28 - No space left on device)
Can't create/write to file '/tmp/#sql_1f2e_0.MAI' (Errcode: 28 'No space left on device')
# contoh format output perintah perror 28
OS error code 28: No space left on device/tmp, bukan ke datadir seperti /var/lib/mysql. File berawalan #sql biasanya temporary table yang dibuat server saat query butuh tempat kerja di disk. File MY... biasanya file sementara untuk proses sorting. Kalau ragu dengan arti suatu kode, utilitas perror yang ikut terpasang bersama MySQL/MariaDB bisa menerjemahkannya.
Cek dulu di mana tmpdir berada dan filesystem mana yang menampungnya
tmpdir adalah variabel server yang menentukan direktori untuk file sementara, seperti temporary table di disk, file sorting, dan sebagian operasi ALTER TABLE. Kalau tidak diatur di my.cnf, nilainya biasanya diambil dari environment atau default sistem, dan sering berakhir di /tmp.
Salah langkah yang paling umum adalah mengecek df -h tanpa argumen lalu hanya membaca baris /. Padahal /tmp bisa berupa partisi terpisah yang jauh lebih kecil, bisa tmpfs yang tinggal di RAM, atau di beberapa setup control panel berupa file image kecil yang di-mount sebagai loop device. Karena itu, cek filesystem yang benar-benar menampung path tersebut:
BASH
# 1. Lokasi tmpdir yang dipakai server (tambahkan -u root -p kalau perlu kredensial)
mysql -e 'SELECT @@tmpdir'
# 2. Filesystem mana yang menampung path itu (ganti /tmp sesuai hasil langkah 1)
findmnt -T /tmp
# 3. Sisa ruang dan sisa inode khusus untuk filesystem tersebut
df -h /tmp
df -i /tmp
# 4. Folder dengan jumlah file terbanyak, tetap di satu filesystem (-x)
# du --inodes butuh coreutils 8.22 ke atas
du --inodes -x -d 1 / 2>/dev/null | sort -n | tail -15SOURCE dan FSTYPE dari findmnt langsung menunjukkan apakah /tmp ikut partisi root, partisi sendiri, tmpfs, atau loop device. Kalau ternyata /tmp adalah partisi kecil, cukup satu query yang membuat temporary table besar untuk menghabiskannya, meskipun partisi root masih punya ruang yang sangat lega.
Kalau ruang masih ada tapi inode habis
df -h untuk filesystem yang tepat masih menunjukkan banyak ruang, lihat kolom IUse% di output df -i. Angka 100% di sana menjelaskan kenapa error tetap muncul.
Inode adalah record metadata yang dimiliki setiap file dan direktori. Isinya pemilik, permission, ukuran, dan lokasi blok datanya. Setiap file butuh satu inode, berapa pun ukurannya. File 0 byte tetap memakai satu inode.
Di ext4, jumlah inode ditetapkan saat filesystem dibuat dan tidak bertambah sendiri. Jadi server yang menyimpan banyak sekali file kecil bisa kehabisan inode jauh sebelum kehabisan ruang. XFS mengalokasikan inode secara dinamis, sehingga kasus ini lebih jarang, meskipun tetap ada batasnya. Begitu inode habis, MySQL tidak bisa membuat file temporary baru, dan kernel melaporkannya dengan kode yang sama: no space left on device.
Penyebabnya hampir selalu folder berisi file kecil yang menumpuk tanpa dibersihkan. Yang sering muncul di server hosting antara lain file session PHP, folder cache plugin WordPress, mail queue yang tersendat, Maildir dengan banyak pesan kecil, output cron yang ditulis ke file baru setiap kali jalan, dan direktori upload sementara aplikasi.
Langkah keempat di blok perintah di atas membantu mencari lokasinya. du --inodes menghitung jumlah inode, bukan ukuran. -x membatasi pencarian ke satu filesystem, dan -d 1 hanya menampilkan satu level folder. Ambil folder dengan angka terbesar, lalu ulangi perintah yang sama dengan path folder itu sebagai pengganti /, sampai ketemu direktori yang benar-benar berisi tumpukan file. Di filesystem yang sangat penuh, perintah ini bisa lama, jadi jalankan saat beban server tidak sedang puncak.
Red herring yang sering bikin salah arah
GROUP BY, ORDER BY, atau DISTINCT di tabel besar bisa membuat temporary table yang membesar sampai /tmp penuh. Setelah query gagal, MySQL menghapus file itu. Saat kamu mengecek df beberapa menit kemudian, /tmp sudah kosong lagi, dan kesannya disk tidak pernah penuh. Untuk membuktikannya, pantau df -h dan df -i untuk filesystem tmpdir setiap detik (misalnya dengan watch) di terminal lain sambil menjalankan ulang query atau proses yang gagal. Kalau angkanya naik cepat lalu turun lagi setelah error, penyebabnya bukan file yang menumpuk, tapi ukuran tmpdir yang memang tidak cukup untuk query tersebut.
Kondisi kedua adalah file yang sudah dihapus tapi masih dibuka proses. Polanya justru terbalik: df bilang penuh, tapi du tidak menemukan file besar. lsof +L1 biasanya membantu menemukannya. Kalau df kamu memang lega, ini bukan kasusnya.
Kondisi ketiga adalah reserved blocks di ext4. Secara default ada porsi blok yang dicadangkan untuk root. Besarnya tergantung opsi saat filesystem dibuat. mysqld berjalan sebagai user mysql, bukan root, jadi yang relevan adalah kolom Avail, bukan selisih Size dan Used.
Kondisi keempat: kalau path di error mengarah ke datadir, bukan /tmp, masalahnya bisa ada di partisi data. Di MySQL 8.0, sebagian internal temporary table bisa ditulis ke temporary tablespace di datadir, tergantung engine dan konfigurasinya. Kalau partisi data yang penuh dan penyebabnya binary log, pembahasannya ada di Why MySQL Crashes When Binary Logs Silently Fill Your Disk.
Memperbaiki dan memastikan sudah beres
rm langsung di folder queue. Setelah itu, cari tahu kenapa file bisa menumpuk, supaya masalah yang sama tidak muncul lagi bulan depan.
Kalau penyebabnya /tmp yang terlalu kecil untuk temporary table, ada dua jalan. Pertama, periksa query-nya. Query yang terus membuat temporary table raksasa sering memang perlu index atau penulisan ulang. Kedua, pindahkan tmpdir ke filesystem yang lebih besar:
INI
# /etc/my.cnf atau file di /etc/mysql/ (lokasi tergantung distro dan paket)
[mysqld]
tmpdir = /var/lib/mysql-tmpmysql:mysql, dan beri permission yang tidak terbuka untuk user lain. Di Ubuntu/Debian, profil AppArmor bisa memblokir mysqld menulis ke direktori baru. Di keluarga RHEL/CentOS, SELinux bisa melakukan hal yang sama kalau konteks direktorinya belum disesuaikan. Error log MySQL biasanya menunjukkan kalau ini terjadi.
Perubahan tmpdir butuh restart service database. Restart berarti koneksi aplikasi putus sebentar, jadi jadwalkan di luar jam sibuk dan pastikan service bisa start lagi dengan bersih sebelum meninggalkan server. Kalau mysqld gagal start, kembalikan baris tmpdir ke nilai semula sambil mencari penyebabnya di error log.
Verifikasinya sederhana. Jalankan lagi SELECT @@tmpdir dan pastikan nilainya sudah berubah. Ulangi query atau proses yang tadinya gagal sambil memantau df -h dan df -i di filesystem baru. Setelah itu cek ulang dalam beberapa hari untuk melihat apakah inode di folder yang tadi dibersihkan mulai naik lagi.
Dari sisi support, kebiasaan yang paling membantu adalah membaca path di pesan error sebelum membaca df. Path itu menunjukkan filesystem mana yang perlu dicek. Setelah itu urutannya pendek: cek df -h dan df -i khusus untuk filesystem tersebut, cari folder boros inode dengan du --inodes, dan pantau saat query berjalan kalau semuanya terlihat normal. Errcode 28 dengan disk yang tampak lega hampir selalu berarti kita mengecek filesystem yang salah, atau mengecek ruang padahal yang habis adalah inode.