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.