Cara Betulkan Ralat SQLSTATE[HY000] [2002] Connection Refused

Anda jalankan php artisan migrate , tekan refresh dalam pelayar, atau deploy ke staging — dan bukannya aplikasi anda, anda dapat dinding merah yang menyeramkan:

SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: select * from information_schema.tables ...)

SQLSTATE[HY000] [2002] connection refused dalam Laravel bermaksud satu benda sahaja: PHP cuba membuka sambungan rangkaian ke pelayan pangkalan data anda, dan tiada apa menjawab pada host dan port itu. SQL anda okay. Migration anda okay. Pangkalan data memang tidak boleh dicapai dari tempat Laravel berjalan. Panduan ini merungkai setiap punca dunia sebenar yang saya nampak dalam production dan pembangunan tempatan, ikut urutan yang anda patut semak, dengan penyelesaian tepat untuk setiap satu.

Apa Sebenarnya Maksud “SQLSTATE[HY000] [2002] Connection Refused”

Pecahkan ralat ini satu per satu: SQLSTATE[HY000] ialah kod ralat generik pemandu PDO — “ralat umum”. [2002] ialah kod ralat klien MySQL untuk “tak dapat sambung ke pelayan”. “Connection refused” ialah sistem pengendalian memberitahu anda sambungan TCP ditolak secara aktif — atau tiada apa yang mendengar pada alamat dan port itu. Kunci pemahaman: ralat ini dilemparkan oleh pustaka klien MySQL sebelum satu byte SQL pun dihantar. Jadi jangan bazir masa meneliti query anda. Masalahnya sentiasa pada laluan sambungan: host, port, transport (socket vs TCP), handshake credentials, atau pelayan memang sedang mati.

Langkah 0: Ulangi Ia Di Luar Laravel

Sebelum sentuh fail .env anda, sahkan di mana kegagalan sebenar. Jalankan ini dari pelayan atau mesin yang menjalankan aplikasi anda:

# 1. Is anything listening on the MySQL port?
nc -zv 127.0.0.1 3306

# 2. Can the MySQL client connect with the same credentials Laravel uses?
mysql -h 127.0.0.1 -P 3306 -u your_db_user -p your_database

Jika nc kata sambungan ditolak, pelayan pangkalan data anda sedang mati atau mendengar di tempat lain — itu masalah pelayan, bukan masalah Laravel. Jika nc berjaya tetapi klien mysql gagal dengan access denied, pelayan sedang hidup dan credentials atau kebenaran host pengguna anda salah. Dalam kedua-dua keadaan, anda baru sahaja menyempitkan pencarian dengan ketara sebelum mengubah satu baris kod aplikasi pun.

Penyelesaian 1: Pelayan Pangkalan Data Tidak Berjalan

Punca paling biasa, dan paling memalukan. Selepas pelayan reboot, Docker restart, atau provision Homestead/Vagrant baru, MySQL/MariaDB mungkin memang belum dimulakan — atau sudah dimulakan tetapi tidak diaktifkan untuk bermula semasa boot.

# Check the status
sudo systemctl status mysql # Ubuntu/Debian
sudo systemctl status mariadb # if you run MariaDB

# Start it now AND enable it for the next reboot
sudo systemctl enable --now mysql

Benda yang ramai terlepas pandang ialah flag enable: start betulkan sekarang, enable betulkan selepas reboot seterusnya juga. Kalau anda guna macOS dengan alat macam DBngin atau MAMP, buka app dan pastikan penunjuk server berwarna hijau — alat-alat ini tak sentiasa restart server selepas kemas kini sistem. Lepas tu, pastikan Laravel dapat mengesan server ini:

php artisan migrate:status

Fix 2: localhost vs 127.0.0.1 — Perangkap Socket

Ini punca nombor satu ralat ‘connection refused’ MySQL dalam Laravel pada komputer lokal, dan ia mengelirukan semua orang sekurang-kurangnya sekali. Bila anda set DB_HOST=localhost, klien MySQL tak guna TCP. Ia cuba sambung melalui fail socket Unix (macam /var/run/mysqld/mysqld.sock). Bila anda set DB_HOST=127.0.0.1, ia guna TCP melalui interface loopback. Kalau laluan fail socket yang dijangka klien tak sama dengan tempat server sebenarnya cipta fail itu, anda akan dapat connection refused — walaupun server berjalan dengan sempurna.

Cara baiki: dalam .env anda, guna alamat IP untuk paksa TCP, supaya laluan socket tidak lagi menjadi masalah:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_db_user
DB_PASSWORD=your_password

Selepas edit .env, clear cache config (lebih lanjut dalam Fix 5) dan cuba semula. Kalau anda memang nak sambungan socket, set DB_SOCKET kepada laluan socket sebenar — cari guna mysqladmin variables | grep socket — tapi untuk 95% app, TCP pada 127.0.0.1 lebih mudah dan lebih boleh dijangka.

Fix 3: Port Tak Sepadan

MAMP guna port 8889 secara default, bukan 3306. Mapping Docker Compose boleh publish MySQL pada mana-mana port host. Laravel Sail guna 3306 dalam container, tapi host anda mungkin map port lain. Kalau alat database anda sambung lancar pada port custom tapi Laravel menolak, DB_PORT anda hampir pasti salah.

DB_PORT=8889  # MAMP default — adjust to whatever your server actually uses

Cari port sebenar: semak tetapan DBngin/MAMP/Docker anda, atau tanya server secara langsung:

sudo ss -tlnp | grep -E 'mysql|maria'

Ini menunjukkan dengan tepat port mana server sedang listen. Padankan DB_PORT dengan port itu.

Fix 4: Hostname Docker dan Sail

Dalam Docker, 127.0.0.1 bermaksud “container ini”, bukan laptop anda dan bukan juga container MySQL. Ini senario klasik ralat “connection refused” pada database Laravel dalam tetapan Docker Compose dan Laravel Sail. Aturannya mudah: dari dalam container (container aplikasi anda, queue workers, sail artisan), gunakan nama service sebagai host, contohnya DB_HOST=mysql. Dari mesin host anda (menjalankan php artisan secara tempatan, atau klien GUI), gunakan 127.0.0.1 dengan port yang dipublikasikan.

# .env for the app running INSIDE docker (Sail default)
DB_HOST=mysql
DB_PORT=3306

# .env for the app running on your HOST talking to a dockerized database
DB_HOST=127.0.0.1
DB_PORT=3306 # the published/host port from docker-compose.yml

Beri juga masa untuk container database benar-benar sedia. Container dimulakan secara selari, dan Laravel boleh boot sebelum MySQL selesai inisialisasi. Dalam pipeline CI, tambah health check atau langkah tunggu; secara tempatan, cuba lagi selepas 10–20 saat. Jika queue workers anda berjalan dalam container berasingan yang long-lived, restart queue workers selepas membetulkan .env — mereka cache config lama dalam memori.

Pembetulan 5: Config Anda Sudah Di-cache (Pembunuh Senyap)

Anda sudah betulkan .env, restart semua, semak port tiga kali — tetapi ralat masih berlarutan. Sembilan daripada sepuluh kali, Laravel langsung tidak membaca .env anda kerana konfigurasi sudah di-cache. php artisan config:cache akan menyimpan semua nilai ke dalam bootstrap/cache/config.php, dan selepas itu, sebarang suntingan .env akan diabaikan secara senyap.

php artisan config:clear
php artisan cache:clear

Kemudian, sahkan apa yang Laravel sebenarnya nampak:

php artisan tinker --execute="print_r(config('database.connections.mysql'));"

Jika output tidak sepadan dengan .env semasa anda, cache adalah puncanya. Dua peraturan yang perlu anda pegang:

  1. Jangan sekali-kali jalankan config:cache dalam pembangunan tempatan. Ia wujud untuk prestasi di production.
  2. Di production, skrip deploy anda perlu menjalankan config:clear, kemudian config:cache (ikut urutan itu) setiap kali deploy, dan restart queue workers dengan php artisan queue:restart supaya mereka mengambil config baharu.

Punca tunggal ini sahaja sudah menyumbang sebahagian besar thread sokongan “tetapi saya sudah betulkan .env!”.

Pembetulan 6: Database Jauh — Bind Address, Users, dan Firewall

Jika database anda berada pada server berbeza (managed database, droplet berasingan, RDS), connection refused biasanya bermaksud laluan rangkaian disekat:

# On the DATABASE server, check what MySQL is bound to
sudo grep -R "bind-address" /etc/mysql/

Jika bind-address = 127.0.0.1, server hanya menerima sambungan tempatan. Untuk akses jauh, ia perlu IP private server atau 0.0.0.0 (kemudian restart MySQL). Seterusnya, semak host yang dibenarkan untuk user MySQL:

SELECT user, host FROM mysql.user;

Pengguna yang dicipta sebagai ‘app’@’localhost’ tidak akan pernah boleh menyambung dari mesin lain — anda perlu ‘app’@’%’ atau ‘app’@’your-app-server-ip’. Terakhir, periksa firewall:

sudo ufw status

# allow MySQL from your app server only, never the whole internet
sudo ufw allow from 203.0.113.10 to any port 3306

Membuka port 3306 kepada dunia adalah insiden keselamatan yang hanya menunggu masa untuk berlaku. Hadkan aksesnya kepada IP pelayan app anda, atau lebih baik, letakkan pangkalan data pada rangkaian peribadi.

Urutan Diagnostik 2 Minit

Apabila ralat ini muncul dan anda belum tahu puncanya, jalankan langkah-langkah ini mengikut urutan. Kebanyakan kes selesai menjelang langkah 3:

# Step 1: what does Laravel THINK the connection settings are?
php artisan tinker --execute="print_r(config('database.connections.mysql'));"

# Step 2: is anything listening where Laravel is pointing?
nc -zv 127.0.0.1 3306

# Step 3: can the mysql client connect with those same settings?
mysql -h 127.0.0.1 -P 3306 -u your_db_user -p your_database -e "SELECT 1;"

# Step 4: still failing? nuke the caches and restart long-lived processes
php artisan config:clear && php artisan cache:clear && php artisan queue:restart

Tambahkan ini sebagai snippet ke runbook pasukan anda. Ia mengubah panik 45 minit menjadi rutin 2 minit.

Checklist Kesilapan Lazim

  • Mengedit .env pada pelayan yang salah. Dalam tetapan Forge/Vapor/CI, pastikan anda telah mengedit persekitaran mesin yang benar-benar menjalankan app.
  • DB_HOST ditetapkan kepada nama kontena pada host, atau 127.0.0.1 di dalam kontena. Padankan nilai host dengan lokasi kod dijalankan (Fix 4).
  • Typo pada DB_DATABASE. Pangkalan data yang tidak wujud memberi ralat berbeza (“unknown database”), tetapi nama pengguna yang tersalah taip memberi access denied — baca mesej itu dengan teliti, jangan andaikan connection refused.
  • SELinux atau AppArmor menyekat sambungan keluar pelayan web pada pelayan hardened — semak audit.log jika semua yang lain kelihatan betul.
  • Menjalankan php artisan serve dalam satu terminal dan mengedit .env dalam terminal lain — dev server memang membaca perubahan .env setiap permintaan, tetapi queue workers dan Octane tidak. Mulakan semula proses-proses itu.
  • Mengandaikan pembaikan berjaya tanpa menjalankan semula arahan yang gagal. Sentiasa jalankan semula php artisan migrate:status atau muat semula halaman untuk mengesahkan.

Cegah Ia Berulang

Sertakan sambungan pangkalan data dalam deployment dan monitoring anda supaya anda dapat mengesan masalah ini sebelum pengguna anda:

// routes/console.php — a scheduled health check
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
DB::connection()->getPdo(); // throws on connection failure
})->everyFiveMinutes()->name('db-health-check');

Padankan langkah itu dengan deploy script yang mengosongkan dan membina semula config cache pada setiap release, dan ralat ini akan menjadi sesuatu yang anda baca dalam blog orang lain, bukan debug pada pukul 2 pagi.

Kesimpulan

Connection refused itu Laravel memberitahu anda yang sebenar: tiada apa yang menjawab pada alamat yang anda berikan. Semak laluan sambungan — server, host, port, cache — dan pembaikannya hampir sentiasa hanya sejauh satu baris config atau satu kali memulakan perkhidmatan. Jika pasukan anda membazirkan masa kerana isu pangkalan data Laravel, sakit kepala deployment, atau API yang terus-menerus jatuh, saya bantu pengasas dan pasukan melancarkan aplikasi Laravel yang stabil. Lihat kerja saya dan hubungi saya di WebixyHub .

Asalnya diterbitkan di https://webixyhub.com/blog/fix-sqlstate-hy000-2002-connection-refused-laravel


Sumber Rujukan:

  • Artikel asal: Dev.to Laravel
  • Diterjemah daripada artikel asal ke Bahasa Melayu oleh AI Editorial – haqis.com
Comments

Similar Posts