Saya adalah pengembang senior pada aplikasi Software-as-a-Service yang digunakan oleh banyak pelanggan berbeda. Perangkat lunak kami berjalan di sekelompok server aplikasi Apache / PHP, didukung oleh backend MySQL. Pada satu contoh khusus dari perangkat lunak, kode PHP untuk menanyakan daftar nama kategori sedang habis ketika pelanggan memiliki lebih dari 29 kategori . Saya tahu ini tidak masuk akal; tidak ada yang istimewa dari angka 30 yang akan mematahkan ini dan pelanggan lain memiliki lebih dari 30 kategori, namun masalahnya adalah 100% dapat direproduksi ketika instalasi yang satu ini memiliki 30 atau lebih kategori dan hilang ketika ada kurang dari 30 kategori.
Tabel yang dimaksud adalah:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(64) NOT NULL,
`title` varchar(128) NOT NULL,
`parent` int(10) unsigned NOT NULL,
`keywords` varchar(255) NOT NULL,
`description` text NOT NULL,
`status` enum('Active','Inactive','_Deleted','_New') NOT NULL default 'Active',
`style` enum('_Unknown') default NULL COMMENT 'Autoenum;',
`order` smallint(5) unsigned NOT NULL,
`created_at` datetime NOT NULL,
`modified_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `parent` (`parent`),
KEY `created_at` (`created_at`),
KEY `modified_at` (`modified_at`),
KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='R2' AUTO_INCREMENT=33 ;
Kode tersebut secara rekursif menanyakan tabel untuk mengambil semua kategori. Ini mengeluarkan a
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
Dan kemudian mengulangi permintaan ini untuk setiap baris yang dikembalikan, tetapi menggunakan WHERE parent=$category_id
setiap waktu. (Saya yakin prosedur ini bisa diperbaiki, tapi itu mungkin pertanyaan lain)
Sejauh yang saya tahu, permintaan berikut ini menggantung selamanya:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Saya dapat menjalankan query ini di klien mysql di server dengan sangat baik, dan saya dapat menjalankannya di PHPMyAdmin tanpa masalah juga.
Perhatikan bahwa bukan itu permintaan khusus yang menjadi masalah. Jika saya DELETE FROM categories WHERE id=22
maka permintaan yang berbeda mirip dengan yang di atas kemudian akan hang. Selain itu, kueri di atas mengembalikan nol baris ketika saya menjalankannya secara manual .
Saya menduga bahwa meja mungkin rusak, dan aku mencoba REPAIR TABLE
dan OPTIMIZE TABLE
tapi nether ini dilaporkan masalah atau memecahkan masalah. Saya menjatuhkan meja dan menciptakan kembali, tetapi masalahnya kembali. Ini persis struktur tabel yang sama dan kode PHP yang digunakan pelanggan lain tanpa masalah bagi orang lain, termasuk pelanggan yang memiliki lebih dari 30 kategori.
Kode PHP tidak berulang selamanya. (Ini bukan dan loop tak terbatas)
Server MySQL menjalankan CentOS linux dengan komunitas mysqld Ver 5.0.92 untuk pc-linux-gnu di i686 (MySQL Community Edition (GPL))
Muat di server MySQL rendah: rata-rata muat: 0,58, 0,75, 0,73, Cpu (s): 4,6% as, 2,9% sy, 0,0% ni, 92,2% id, 0,0% wa, 0,0% hi, 0,0% hi, 0,3% si, 0,0% st. Diabaikan swap yang digunakan (448k)
Bagaimana saya bisa memecahkan masalah ini? Adakah saran tentang apa yang mungkin terjadi?
UPDATE: Saya TRUNCE
mengedit tabel dan memasukkan 30 baris data dummy:
INSERT INTO `categories` (`id`, `name`, `title`, `parent`, `keywords`, `description`, `status`, `style`, `order`, `created_at`, `modified_at`) VALUES
(1, 'New Category', '', 0, '', '', 'Inactive', NULL, 1, '2011-10-25 12:06:30', '2011-10-25 12:06:34'),
(2, 'New Category', '', 0, '', '', 'Inactive', NULL, 2, '2011-10-25 12:06:39', '2011-10-25 12:06:40'),
(3, 'New Category', '', 0, '', '', 'Inactive', NULL, 3, '2011-10-25 12:06:41', '2011-10-25 12:06:42'),
(4, 'New Category', '', 0, '', '', 'Inactive', NULL, 4, '2011-10-25 12:06:46', '2011-10-25 12:06:47'),
(5, 'New Category', '', 0, '', '', 'Inactive', NULL, 5, '2011-10-25 12:06:49', NULL),
(6, 'New Category', '', 0, '', '', 'Inactive', NULL, 6, '2011-10-25 12:06:51', '2011-10-25 12:06:52'),
(7, 'New Category', '', 0, '', '', 'Inactive', NULL, 7, '2011-10-25 12:06:53', '2011-10-25 12:06:54'),
(8, 'New Category', '', 0, '', '', 'Inactive', NULL, 8, '2011-10-25 12:06:56', '2011-10-25 12:06:57'),
(9, 'New Category', '', 0, '', '', 'Inactive', NULL, 9, '2011-10-25 12:06:59', '2011-10-25 12:06:59'),
(10, 'New Category', '', 0, '', '', 'Inactive', NULL, 10, '2011-10-25 12:07:01', '2011-10-25 12:07:01'),
(11, 'New Category', '', 0, '', '', 'Inactive', NULL, 11, '2011-10-25 12:07:03', '2011-10-25 12:07:03'),
(12, 'New Category', '', 0, '', '', 'Inactive', NULL, 12, '2011-10-25 12:07:05', '2011-10-25 12:07:05'),
(13, 'New Category', '', 0, '', '', 'Inactive', NULL, 13, '2011-10-25 12:07:06', '2011-10-25 12:07:07'),
(14, 'New Category', '', 0, '', '', 'Inactive', NULL, 14, '2011-10-25 12:07:08', '2011-10-25 12:07:09'),
(15, 'New Category', '', 0, '', '', 'Inactive', NULL, 15, '2011-10-25 12:07:11', '2011-10-25 12:07:12'),
(16, 'New Category', '', 0, '', '', 'Inactive', NULL, 16, '2011-10-25 12:07:13', '2011-10-25 12:07:14'),
(17, 'New Category', '', 0, '', '', 'Inactive', NULL, 17, '2011-10-25 12:09:41', '2011-10-25 12:09:42'),
(18, 'New Category', '', 0, '', '', 'Inactive', NULL, 18, '2011-10-25 12:09:47', NULL),
(19, 'New Category', '', 0, '', '', 'Inactive', NULL, 19, '2011-10-25 12:09:48', NULL),
(20, 'New Category', '', 0, '', '', 'Inactive', NULL, 20, '2011-10-25 12:09:48', NULL),
(21, 'New Category', '', 0, '', '', 'Inactive', NULL, 21, '2011-10-25 12:09:49', NULL),
(22, 'New Category', '', 0, '', '', 'Inactive', NULL, 22, '2011-10-25 12:09:50', NULL),
(23, 'New Category', '', 0, '', '', 'Inactive', NULL, 23, '2011-10-25 12:09:51', NULL),
(24, 'New Category', '', 0, '', '', 'Inactive', NULL, 24, '2011-10-25 12:09:51', NULL),
(25, 'New Category', '', 0, '', '', 'Inactive', NULL, 25, '2011-10-25 12:09:52', NULL),
(26, 'New Category', '', 0, '', '', 'Inactive', NULL, 26, '2011-10-25 12:09:53', NULL),
(27, 'New Category', '', 0, '', '', 'Inactive', NULL, 27, '2011-10-25 12:09:54', NULL),
(28, 'New Category', '', 0, '', '', 'Inactive', NULL, 28, '2011-10-25 12:09:55', NULL),
(29, 'New Category', '', 0, '', '', 'Inactive', NULL, 29, '2011-10-25 12:09:56', NULL),
(30, 'New Category', '', 0, '', '', 'Inactive', NULL, 30, '2011-10-25 12:09:57', NULL);
Tidak ada orang tua sama sekali , semua kategori berada di tingkat atas. masalah masih ada di sana. Permintaan berikut, dijalankan oleh PHP, gagal:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Inilah EXPLAIN
:
mysql> EXPLAIN SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`;
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | categories | ref | parent | parent | 4 | const | 1 | Using where; Using filesort |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
UPDATE # 2: Saya sekarang telah mencoba semua hal berikut:
- Saya menyalin tabel dan data ini ke situs lain dengan perangkat lunak yang sama. Masalahnya tidak mengikuti tabel. Tampaknya terbatas pada database yang satu ini.
- Saya mengubah indeks seperti yang disarankan jawaban gbn. Masalahnya tetap ada.
- Saya menjatuhkan meja dan dibuat ulang sebagai
InnoDB
tabel dan memasukkan 30 baris tes yang sama di atas. Masalahnya tetap ada.
Saya curiga pasti ada sesuatu dengan database ini ...
PEMBARUAN # 3: Saya benar-benar menjatuhkan basis data dan membuatnya kembali dengan nama baru, mengimpor datanya. Masalahnya tetap ada.
Saya telah menemukan bahwa pernyataan PHP aktual yang hang adalah panggilan untuk mysql_query()
. Pernyataan setelah ini tidak pernah dieksekusi.
Sementara panggilan itu hang, MySQL daftar utas sebagai tidur!
mysql> show full processlist;
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| 5560 | root | localhost | problem_db | Query | 0 | NULL | show full processlist |
----- many rows which have no relevancy; only rows from this customer's app are shown ------
| 16341 | shared_db | oak01.sitepalette.com:53237 | shared_db | Sleep | 308 | | NULL |
| 16342 | problem_db | oak01.sitepalette.com:60716 | problem_db | Sleep | 307 | | NULL |
| 16344 | shared_db | oak01.sitepalette.com:53241 | shared_db | Sleep | 308 | | NULL |
| 16346 | problem_db | oak01.sitepalette.com:60720 | problem_db | Sleep | 308 | | NULL |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
UPDATE # 4: Saya telah mempersempitnya ke kombinasi dua tabel, categories
tabel terperinci di atas dan sebuah media_images
tabel dengan 556 baris. Jika media_images
tabel berisi kurang dari 556 baris, atau categories
tabel berisi kurang dari 30 baris, masalahnya hilang. Sepertinya ini semacam batasan MySQL yang saya tekan di sini ...
UPDATE # 5: Saya baru saja mencoba memindahkan database ke server MySQL yang berbeda sama sekali dan masalahnya hilang ... Jadi itu terkait dengan server database produksi saya ...
UPDATE # 6: Berikut kode PHP yang relevan yang hang setiap kali:
public function find($type,$conditions='',$order='',$limit='')
{
if($this->_link == self::AUTO_LINK)
$this->_link = DFStdLib::database_connect();
if(is_resource($this->_link))
{
$q = "SELECT ".($type==_COUNT?'COUNT(*)':'*')." FROM `{$this->_table}`";
if($conditions)
{
$q .= " WHERE $conditions";
}
if($order)
{
$q .= " ORDER BY $order";
}
if($limit)
{
$q .= " LIMIT $limit";
}
switch($type)
{
case _ALL:
DFSkel::log(DFSkel::LOG_DEBUG,"mysql_query($q,$this->_link);");
$res = @mysql_query($q,$this->_link);
DFSkel::log(DFSkel::LOG_DEBUG,"res = $res");
Kode ini dalam produksi dan berfungsi dengan baik pada semua pemasangan lainnya. Hanya dengan satu instal, itu hang di $res = @mysql_query($q,$this->_link);
. Saya tahu karena saya melihat mysql_query
log debug, dan bukan res =
, dan ketika saya strace
proses PHP, itu tergantung padaread(
PEMBARUAN # apa pun itu-aku-benci-ini- & (# ^ & -masalah! Ini sekarang mulai terjadi pada dua pelanggan saya. Saya baru saja bersemangat tcpdump
dan sepertinya respons dari MySQL tidak pernah dikirim sepenuhnya. Aliran TCP sepertinya menggantung sebelum respons MySQL penuh dapat dikirim. (Namun saya masih menyelidiki)
UPDATE # Saya-sudah-sudah-benar-benar-gila-tapi-itu-bekerja-sekarang-agak: Ok, ini tidak masuk akal, tapi saya telah menemukan solusi. Jika saya menetapkan alamat IP kedua ke eth2
antarmuka server MySQL , dan menggunakan satu IP untuk lalu lintas NFS dan IP kedua untuk MySQL, maka masalahnya hilang. Sepertinya saya entah bagaimana ... membebani alamat IP jika kedua lalu lintas NFS + MySQL pergi ke IP itu. Tapi itu tidak masuk akal karena Anda tidak bisa "membebani" alamat IP. Menjenuhkan suatu antarmuka tentu saja, tetapi itu adalah antarmuka yang sama.
Ada yang tahu apa yang sedang terjadi di sini? Ini mungkin pertanyaan unix.SE atau ServerFault pada titik ini ... (Setidaknya itu berfungsi sekarang ...)
UPDATE # why-oh-why: Masalah ini masih terjadi. Itu mulai terjadi bahkan menggunakan dua IP berbeda. Saya dapat terus membuat IP pribadi baru, tetapi jelas ada sesuatu yang salah.
sumber
Jawaban:
Untuk profil umum tentang apa yang sebenarnya terjadi dalam rencana kueri, Anda dapat mencoba PROFIL
Ini pada dasarnya akan membantu Anda menentukan di mana hangup berada.
Tentu saja, ini hanya berfungsi jika Anda telah mengompilasi MySQL
enable-profiling
.sumber
Gagasan (tidak yakin apakah berlaku untuk MyISAM, saya bekerja dengan InnoDB)
Ubah indeks "induk" jadi di 3 kolom: induk, urutan, nama. Ini cocok dengan WHERE .. ORDER BY
Hapus
SELECT *
. Ambil saja kolom yang Anda butuhkan. Tambahkan kolom lain ke indeks "induk"Ini akan memungkinkan pengoptimal menggunakan hanya indeks karena sekarang mencakup. Seperti berdiri, Anda harus membaca seluruh tabel karena indeks tidak berguna untuk permintaan itu
sumber
parent
indeks ke(parent, order, name)
Saya akan memeriksa beberapa hal di Server DB Produksi
netstat | grep -i mysql | grep TIME_WAIT
skip-host-cache
danskip-name-resolve
memotong penggunaan DNS mysqld. Dengan demikian saya bisa menghubungkan ke jawaban @ marcioAlmada sebagai pos pemeriksaan untuk memeriksa.Jika Anda merasa tidak ada pemeriksaan ini yang berguna, beri komentar SECEPATNYA dan beri tahu saya agar saya dapat menghapus jawaban saya.
sumber
/var
ada blok buruk (itu pada RAID10) tapi saya bisa dengan mudah salah. Saya akan memeriksa netstat, ide bagus di sana! Saya tidak menggunakanmysql_pconnect
tetapi akan memeriksa jaringan / dns / etc.dmesg
. Kecuali Anda memiliki perangkat keras RAID, dalam hal ini periksa program monitor serangan perangkat keras Anda.TIME_WAIT
koneksi MySQL. Tidak ada jumlah besar dengan cara apa pun ... Tabel ini tidak berat dengan aktivitas.Saya akan mengatakan Anda telah menekan schrödinbug . Anda dapat mencoba
die()
setelah atau sebelum permintaan Anda dan mencoba menelusuri kode Andaif statements
yang jarang terjadi. Sulit mengatakan apa yang hang ketika kita tidak memiliki kode Anda.EDIT: Saat ini saya mengatakan bahwa ini mungkin baris ini
yang (saya asumsikan) membuat fungsi koneksi setiap kali dipanggil. Mungkin itu masalahnya. Apa max_connections Anda di my.cnf?
sumber
mysql_query()
tcpdump
dalam beberapa hari ke depan. Jika ini benar - benar masalah PHP, maka saya harus mengirim pertanyaan baru pada SO.$this->_link
untuk konstan:self::AUTO_LINK
. 2. Bahkan jika saya, kode itu ada di if:,if($this->_link == self::AUTO_LINK)
dan baris berikutnya$this->_link = DFStdLib::database_connect();
mengubah nilai$this->_link
sehinggaif
tidak akan dijalankan lagi. Saya yakin hanya ada satu koneksi ke database per utas. (Lihat daftar proses)Beberapa upaya:
Firewall ?? Apakah ada firewall yang memblokir aplikasi Anda dan mencegahnya membuat permintaan ke server database produksi Anda atau sebaliknya?
Apakah Anda menggunakan nama domain dalam konfigurasi koneksi Anda atau alamat IP? Menggunakan nama domain dapat sedikit memperlambat interaksi basis data dan ini dikombinasikan dengan waktu eksekusi skrip maksimum PHP pendek akan menyebabkan hangout selamanya
Saran terakhir ini tampaknya menjelaskan perilaku variabel aneh ketika berpindah server database. Yang satu mungkin merespons jauh lebih cepat daripada yang lain, dan karena untuk setiap catatan yang ditemukan Anda akan memiliki kueri sekunder, hipotesis itu akan menjelaskan mengapa aplikasi hanya menunda dengan sejumlah hasil yang diminta (> 30).
Setidaknya kita sampai pada kesimpulan utama. Jelas masalahnya bukan pada server MySQL sendiri. Melihat dokumentasi dan sepertinya tidak ada batasan fitur yang sesuai dengan situasi spesifik Anda, juga saya tidak pernah punya masalah dengan tabel rekursif dan jumlah entri tertentu.
Semoga itu bisa membantu.
sumber
Sudahkah Anda mencoba memperbarui perintah mysql_query () menjadi driver PHP5 asli? mysqli :: query ()? Tidak yakin ini akan melakukan apa pun tetapi mungkin layak dicoba.
sumber