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 ;
这段代码递归地查询表格以获取所有的分类。它发出一个
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
然后对返回的每一行重复执行此查询,但每次使用WHERE parent=$category_id
。(我相信这个过程可以改进,但那可能是另一个问题)
据我所知,以下查询似乎永远挂起:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
我可以在服务器上的mysql客户端中完美地执行这个查询,也可以在PHPMyAdmin中执行它而没有问题。
请注意,问题不是那个具体的查询。如果我执行“DELETE FROM categories WHERE id=22”,那么一个类似上面的不同查询将会挂起。此外,上面的查询在我手动运行时返回零行。
我怀疑表可能损坏了,所以我尝试了“REPAIR TABLE”和“OPTIMIZE TABLE”,但是这两个都没有报告问题或解决问题。我删除了表并重新创建,但问题又出现了。这个表结构和PHP代码与其他客户使用的完全相同,其他客户包括有超过30个类别的客户都没有任何问题。
PHP代码并不是无限递归。(这不是无限循环)
MySQL服务器正在运行CentOS Linux,mysqld版本为5.0.92-community for pc-linux-gnu on i686(MySQL Community Edition (GPL))。
MySQL服务器的负载很低:平均负载为0.58、0.75、0.73,CPU使用率为4.6%us、2.9%sy、0.0%ni、92.2%id、0.0%wa、0.0%hi、0.3%si、0.0%st。几乎没有使用交换空间(448k)。
我应该如何解决这个问题?有什么可能导致这种情况的建议吗?
更新:我使用TRUNCATE命令清空了表,并插入了30行虚拟数据。
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);
没有任何父级,所有类别都在顶层。问题仍然存在。以下查询由PHP执行,但失败了:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
这是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)
更新 #2:我现在已经尝试了以下所有方法:
- 我将这个表格和数据复制到了另一个使用相同软件的网站。问题并没有随着表格一起出现,似乎只限于这个数据库。
- 我按照gbn的建议更改了索引,但问题仍然存在。
- 我删除了该表格,并重新创建了一个InnoDB表格,并插入了相同的30个测试行。问题依然存在。
我怀疑问题可能与这个数据库有关...
更新 #3:我完全删除了数据库,并以新名称重新创建,并导入了她的数据。问题仍然存在。
我发现导致程序卡住的实际PHP语句是对mysql_query()的调用。此后的语句都不会被执行。
当这个调用卡住时,MySQL将该线程标记为睡眠状态!
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 |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
更新 #4:我已经缩小范围,确定问题出在两个表的组合上,即上面详细介绍的categories
表和一个包含556行的media_images
表。如果media_images
表少于556行,或者categories
表少于30行,问题就会消失。就像是我在这里遇到了某种MySQL限制...
更新 #5:我刚刚尝试将数据库完全迁移到另一个MySQL服务器上,问题就解决了...所以这与我的生产数据库服务器有关...
更新 #6:以下是每次卡住的相关PHP代码:
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");
这段代码在生产环境中运行良好,并且在所有其他安装上都正常工作。只有在一个安装中,在$res = @mysql_query($q,$this->_link);
处卡住了。我知道是因为在调试日志中看到了mysql_query
,而没有看到res =
,当我使用strace
跟踪PHP进程时,它在read(
处卡住了。
更新 #不管怎么说-我讨厌这个问题!现在又发生在我的两个客户身上。我刚刚启动了tcpdump
,似乎MySQL的响应没有完整发送。TCP流似乎在完全发送MySQL响应之前就停滞了。(不过我还在继续调查)
更新 #我已经彻底疯了但是现在好像可以了:好吧,这没有任何意义,但是我找到了一个解决办法。如果我给MySQL服务器的eth2
接口分配第二个IP地址,并且使用一个IP用于NFS流量,另一个IP用于MySQL,那么问题就消失了。就好像我以某种方式...过载了IP地址,如果NFS+MySQL流量都发送到同一个IP地址。但这毫无道理,因为你不能“过载”一个IP地址。饱和一个接口倒是可以,但它是同一个接口。
你有任何想法这里到底发生了什么?这可能是一个unix.SE或ServerFault的问题...(至少现在可以工作了...)
更新 #为什么啊为什么:这个问题仍然存在。即使使用两个不同的IP也开始出现这个问题。我可以继续创建新的私有IP,但显然有些问题。
tcpdump
来调试这个问题。 - Josh