Menghubungkan ke MySQL dari PHP sangat lambat

19

Saya baru saja menginstal XAMPP. Ketika pertama kali membuka PHPMyAdmin saya perhatikan itu sangat lambat. Tidak masuk akal bahwa pada localhost itu harus memakan waktu hampir 5 detik untuk setiap halaman untuk dibuka. Saya membuat test case kecil untuk mengalihkan kesalahan dari PHPMyAdmin:

$con = new PDO("mysql:host=localhost;dbname=mysql", "root", "");
$statement = $con->query('SELECT host,user,password FROM user;');
$users = $statement->fetchAll(PDO::FETCH_ASSOC);

Script di atas hanya membutuhkan waktu sekitar 3 detik untuk dijalankan (walaupun butuh lebih dekat hingga 8 detik untuk memuat pertama kali saya menjalankannya.)

Kemudian untuk memeriksa apakah itu kesalahan PDO, saya mencoba menggunakan mysql_connect:

$con = mysql_connect("localhost", "root", "");
mysql_select_db("mysql", $con);
$result = mysql_query('SELECT host,user,password FROM user;');

Membutuhkan waktu yang sangat lama untuk menyelesaikannya.

Saya pikir itu kesalahan PHP pada awalnya, tetapi kode PHP dan file statis dilayani lebih cepat daripada yang saya bisa klik refresh. Saya menguji PHP dengan menjalankan skrip kecil ini:

header("Content-Type: text/plain");

for($i = 0; $i < 5000; $i++)
{
    echo sha1(rand()) . "\n";
}

5000 sha1perhitungan dan halaman ini masih ditampilkan snappier daripada saya dapat menyegarkan jendela saya.

Lalu saya pikir itu salah MySQL. Tetapi sekali lagi, tidak perlu banyak pengujian untuk mengetahui bahwa MySQL bekerja lebih cepat daripada yang saya butuhkan. Menggunakan klien MySQL CLI permintaan pilih pengguna bahkan tidak mengambil waktu yang dapat diukur - itu dilakukan sebelum saya bahkan membiarkan kunci kembali.

Masalahnya harus koneksi PHP ke MySQL - itu sejauh yang saya bisa alasan. Saya dapat menemukan banyak hal tentang PHP yang lambat atau MySQL yang lambat, tetapi tidak ada tentang PHP + MySQL yang sangat lambat.

Terima kasih kepada siapa saja yang dapat membantu saya menyelesaikan masalah ini!


Saya menggunakan XAMPP 1.8.0 untuk win32 ( tautan Unduh )
Versi PHP: 5.4.4
Versi MySQL: 14.14


EDIT: Setelah penghitungan waktu, ternyata itu adalah fungsi koneksi yang memakan waktu begitu lama:

$time = microtime(true);

$con = mysql_connect("localhost", "root", "");
mysql_select_db("mysql", $con);

$con_time = microtime(true);

$result = mysql_query('SELECT host,user,password FROM user;');

$sel_time = microtime(true);

printf("Connect time: %f\nQuery time: %f\n",
       $con_time-$time,
       $sel_time-$con_time);

Keluaran:

Waktu koneksi: 1,006148
Waktu permintaan: 0,000247

Apa yang bisa menyebabkan PHP menghabiskan banyak waktu melakukan koneksi ke database? Klien CLI, HeidiSQL dan meja kerja MySQL terhubung secara instan

Hubro
sumber
php -m output tolong
ketipis
@thinice: pastebin.com/4dXe7ZDa
Hubro

Jawaban:

17

Mungkinkah mysql Anda mencoba menjalankan kueri rev-dns setiap kali Anda terhubung? coba tambahkan ke my.cnf, bagian mysqld: skip-name-resol .

pQd
sumber
Anehnya baik PHPMyAdmin dan klien MySQL CLI sekarang memberi saya "Host '127.0.0.1' tidak diizinkan untuk terhubung ke server MySQL ini". Untuk beberapa alasan skrip PHP masih berfungsi, tetapi sama lambatnya seperti sebelumnya
Hubro
Apakah 'aplikasi gemuk' untuk mengelola mysql - seperti workbench mysql - lambat?
pQd
Menjalankan kueri yang sama dari MySQL Workbench sama cepatnya dengan klien CLI, dan begitu pula HeidiSQL
Hubro
tambahkan beberapa waktu di php dan periksa apakah itu menghubungkan atau menjalankan permintaan yang membutuhkan banyak waktu.
pQd
Terima kasih atas komentar itu, saya memperbarui pertanyaan saya. Baik koneksi dan kueri super cepat
Hubro
30

Ini diambil hampir kata demi kata dari jawaban saya di sini , tapi saya tahu kami mengerutkan kening pada jawaban tautan saja pada SO jadi saya bayangkan kalian juga melakukannya :-)

Jika Anda mengalami masalah ini dan menggunakan versi Windows sebelum Windows 7, ini mungkin bukan jawaban untuk masalah Anda.

Mengapa ini terjadi?

Penyebab masalah ini adalah IPv4 vs IPv6.

Saat Anda menggunakan nama host alih-alih alamat IP, klien MySQL pertama kali menjalankan AAAApencarian host (IPv6) untuk nama tersebut, dan mencoba alamat ini terlebih dahulu jika berhasil menyelesaikan nama menjadi alamat IPv6. Jika salah satu langkah gagal (resolusi nama atau koneksi) itu akan mundur ke IPv4, menjalankan Apencarian dan mencoba host ini sebagai gantinya.

Apa artinya ini dalam praktiknya adalah bahwa jika localhostpencarian IPv6 berhasil tetapi MySQL tidak terikat pada loopback IPv6, Anda perlu menunggu satu siklus waktu habis koneksi sebelum penurunan IPv4 terjadi dan koneksi berhasil.

Ini bukan masalah sebelum Windows 7, karena localhostresolusi dilakukan melalui file hosts, dan itu datang dengan prakonfigurasi saja 127.0.0.1- itu tidak datang dengan rekan IPv6 itu ::1.

Karena Windows 7, bagaimanapun, localhostresolusi dibangun ke resolver DNS, untuk alasan yang diuraikan di sini . Ini berarti bahwa pencarian IPv6 sekarang akan berhasil - tetapi MySQL tidak terikat ke alamat IPv6 itu, sehingga koneksi akan gagal, dan Anda akan melihat penundaan yang diuraikan dalam pertanyaan ini.

Itu bagus. Katakan padaku bagaimana cara memperbaikinya!

Anda punya beberapa pilihan. Melihat-lihat internet, "solusi" umum tampaknya adalah menggunakan alamat IP secara eksplisit alih-alih nama, tetapi ada beberapa alasan untuk tidak melakukan ini, baik terkait portabilitas, keduanya bisa dibilang tidak penting:

  • Jika Anda memindahkan skrip Anda ke komputer lain yang hanya mendukung IPv6, skrip Anda tidak akan berfungsi lagi.

  • Jika Anda memindahkan skrip Anda ke lingkungan hosting berbasis * nix, string ajaib localhostberarti klien MySQL akan lebih suka menggunakan soket Unix jika dikonfigurasi, ini lebih efisien daripada konektivitas berbasis loopback IP

Kedengarannya sangat penting?

Mereka bukan. Anda harus mendesain aplikasi Anda sehingga hal-hal semacam ini didefinisikan dalam file konfigurasi. Jika Anda memindahkan skrip ke lingkungan lain, kemungkinan hal-hal lain juga perlu dikonfigurasikan.

Singkatnya, menggunakan alamat IP bukanlah solusi terbaik , tetapi kemungkinan besar merupakan solusi yang dapat diterima.

Jadi apa solusi terbaik?

Cara terbaik adalah mengubah alamat bind yang digunakan oleh server MySQL. Namun, ini tidak sesederhana yang diinginkan. Tidak seperti Apache, Nginx dan hampir setiap aplikasi layanan jaringan waras lainnya yang pernah dibuat, MySQL hanya mendukung satu alamat pengikatan, jadi ini bukan hanya kasus menambahkan yang lain. Untungnya, sistem operasi mendukung sedikit keajaiban di sini, sehingga kita dapat mengaktifkan MySQL untuk menggunakan IPv4 dan IPv6 secara bersamaan.

Anda harus menjalankan MySQL 5.5.3 atau yang lebih baru, dan Anda harus memulai MySQL dengan --bind-address=argumen baris perintah. Anda memiliki 4 opsi dokumen , tergantung pada apa yang ingin Anda lakukan:

  • Yang mungkin Anda kenal, dan yang paling mungkin Anda gunakan (secara efektif) 0.0.0.0,. Ini mengikat semua alamat IPv4 yang tersedia pada mesin. Ini sebenarnya mungkin bukan hal terbaik untuk dilakukan bahkan jika Anda tidak peduli dengan IPv6, karena memiliki risiko keamanan yang sama ::.

  • Alamat IPv4 atau IPv6 eksplisit (misalnya 127.0.0.1atau ::1untuk loopback). Ini mengikat server ke alamat itu dan hanya alamat itu.

  • Senar ajaib ::. Ini akan mengikat MySQL ke setiap alamat pada mesin, baik loopback dan alamat antarmuka fisik, dalam mode IPv4 dan IPv6. Ini berpotensi risiko keamanan, hanya lakukan ini jika Anda memerlukan MySQL untuk menerima koneksi dari host jarak jauh.

  • Gunakan alamat IPv6 yang dipetakan IPv4 . Ini adalah mekanisme khusus yang dibangun ke dalam IPv6 untuk kompatibilitas mundur selama transisi 4 -> 6, dan ini memungkinkan Anda mengikat ke alamat IPv4 tertentu dan setara dengan IPv6. Ini sangat tidak mungkin berguna bagi Anda untuk apa pun selain alamat "loopback ganda" ::ffff:127.0.0.1. Ini kemungkinan besar merupakan solusi terbaik bagi kebanyakan orang, hanya mengikat ke loopback tetapi memungkinkan koneksi IPv4 dan IPv6.

Apakah saya perlu memodifikasi file host?

TIDAK . Jangan memodifikasi file host. Penyelesai DNS tahu apa yang harus dilakukan localhost, mendefinisikan ulang paling tidak akan berpengaruh, dan paling buruk membingungkan para penyelesai.

Bagaimana dengan --skip-name-resolve?

Ini juga dapat memperbaiki masalah, karena alasan terkait tetapi sedikit berbeda.

Tanpa opsi konfigurasi ini, MySQL akan berusaha untuk menyelesaikan semua alamat IP koneksi klien ke nama host melalui PTRpermintaan DNS. Jika server MySQL Anda sudah diaktifkan untuk menggunakan IPv6 tetapi koneksi masih membutuhkan waktu lama, itu mungkin karena catatan reverse DNS ( PTR) tidak dikonfigurasi dengan benar.

Menonaktifkan resolusi nama akan memperbaiki masalah ini, tetapi memiliki konsekuensi lain, terutama bahwa izin akses yang dikonfigurasi untuk menggunakan nama DNS dalam Hostkondisi sekarang akan gagal.

Jika Anda akan melakukan ini, Anda harus mengonfigurasi semua hibah Anda untuk menggunakan alamat IP dan bukan nama.

DaveRandom
sumber
2
Akhirnya! Terima kasih terima kasih! Akhirnya saya menemukan penjelasan yang tepat, lengkap dan jelas tentang semua penyebab, solusi yang mungkin dan kontra indikasi mereka. Anda harus menulis buku tentang itu!
tobia.zanarella
4
Saya mengalami keterlambatan 1 detik untuk menghubungkan ke MySQL di localhost sampai saya mengubah bind-address menjadi ::1. Sayangnya ::ffff:127.0.0.1terus memberi saya penundaan 1 detik (terlepas dari menggunakan skip-name-resolveatau tidak), ada ide mengapa? (pada Windows 8.1)
Simon East
1
@Simon Bukan petunjuk tanpa melihat, tetapi langkah pertama untuk debug adalah mencoba untuk terhubung ke loopback IPv6 dan loopback IPv4 secara langsung menggunakan alamat eksplisit untuk memverifikasi bahwa MySQL benar-benar mendengarkan dan terhubung di kedua tumpukan, dan debugging dari sana .
DaveRandom
1
ya, :: ffff: 127.0.0.1 tidak berfungsi ...
Raheel Hasan
Jika saya mengikatnya dengan :: 1, Sqlyog tidak berfungsi ... apa yang harus dilakukan ??
Raheel Hasan
13

Biasanya ketika IPv6 diaktifkan di koneksi server ke MySQL menggunakan localhostsangat lambat.

Mengubah alamat server mysql di skrip untuk 127.0.0.1menyelesaikan masalah.

Doug
sumber
+1 ini adalah jawaban yang tepat untuk saya. Saya kira di Windows 8 mereka memindahkan resolusi localhostke resolver DNS dengan alasan @DaveRandom ditautkan ke: serverfault.com/questions/4689/…
wwarren
Ini bekerja untuk saya: D
FosAvance
Ini dapat terjadi untuk server lain selain localhost. Saya memiliki masalah keterlambatan yang sama untuk alamat server pada formulir xx.xxxx.xxxxx.xxxxx.com. Setelah saya mengubah nama server ke alamat IP-nya, masalahnya hilang.
Gruber
1
mysql_connect("localhost", "root", "");

Yah, cukup jelas apa alasannya. PHP benar-benar bagus dalam beberapa hal, tetapi tidak secara langsung menerjemahkan 'localhost' ke '127.0.0.1'. Anda harus mencobanya, itu akan benar-benar menurunkan waktu pemuatan keseluruhan halaman situs web Anda, karena itu menahan PHP dari memeriksa file HOSTS Anda dan apa yang tidak dilakukan untuk mendapatkan alamat IP asli di belakang 'localhost'

Xesau
sumber
Saya tidak tahu apa yang ingin Anda sarankan masalahnya.
kasperd
@kasperd Penyelesaian DNS dari 'localhost'
Xesau
catatan dari '17 - fungsi mysql_ * sudah tidak digunakan lagi sekarang dan dihapus di php7
treyBake
0

Anda juga dapat menghilangkan perlambatan kueri dengan membuat sedikit penyesuaian pada variabel koneksi db Anda (yang diharapkan dalam file terpisah dari skrip Anda untuk portabilitas). Ubah nilai host menjadi "127.0.0.1" alih-alih "localhost". Ini memintas pencarian DNS yang panjang untuk localhost.

Semoga ini membantu!

Billman64
sumber