Jos*_*osh 13 mysql performance
我是许多不同客户使用的软件即服务应用程序的高级开发人员。我们的软件在由 MySQL 后端提供支持的 Apache / PHP 应用程序服务器集群上运行。在该软件的一个特定实例上,当客户的类别超过 29 个时,查询类别名称列表的 PHP 代码会超时。我知道这毫无意义;数字 30 没有什么特别之处会打破这一点,其他客户的类别超过 30 个,但是,当这个安装有 30 个或更多类别时,问题是 100% 可重现的,而当类别少于 30 个时,问题就会消失。
有问题的表是:
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 ;
Run Code Online (Sandbox Code Playgroud)
有问题的代码递归查询表以获取所有类别。它发出一个
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
Run Code Online (Sandbox Code Playgroud)
然后对返回的每一行重复此查询,但WHERE parent=$category_id
每次都使用。(我确信这个程序可以改进,但这可能是另一个问题)
据我所知,以下查询永远挂起:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Run Code Online (Sandbox Code Playgroud)
我可以在服务器上的 mysql 客户端中完美地执行此查询,并且我也可以在 PHPMyAdmin 中执行它而没有问题。
请注意,问题不是那个特定的查询。如果我DELETE FROM categories WHERE id=22
然后一个与上述类似的不同查询将挂起。此外,上面的查询在我手动运行时返回零行。
我怀疑该表可能已损坏,我尝试过REPAIR TABLE
,OPTIMIZE TABLE
但这些报告的问题均未解决,也没有解决该问题。我放下桌子并重新创建,但问题又回来了。这与其他客户使用的表结构和 PHP 代码完全相同,对其他人没有问题,包括拥有 30 多个类别的客户。
PHP 代码不会永远递归。(这不是无限循环)
MySQL 服务器运行 CentOS linux 和 mysqld Ver 5.0.92-community for pc-linux-gnu on i686 (MySQL Community Edition (GPL))
MySQL服务器负载低:平均负载:0.58, 0.75, 0.73, Cpu(s): 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)
如何解决此问题?关于可能发生什么的任何建议?
更新:我TRUNCE
编辑了表格并插入了 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);
Run Code Online (Sandbox Code Playgroud)
根本没有父母,所有类别都在顶级。问题仍然存在。由 PHP 执行的以下查询失败:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Run Code Online (Sandbox Code Playgroud)
这是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)
Run Code Online (Sandbox Code Playgroud)
更新#2:我现在已经尝试了以下所有内容:
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 |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
Run Code Online (Sandbox Code Playgroud)
更新 #4:我将范围缩小到两个表的组合,categories
上面详述的表和一个media_images
有 556 行的表。如果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");
Run Code Online (Sandbox Code Playgroud)
此代码正在生产中,在所有其他安装上都可以正常工作。只需安装一次,它就会挂在$res = @mysql_query($q,$this->_link);
. 我知道是因为我mysql_query
在调试日志中看到了,而不是 res =
,当我strace
处理 PHP 进程时,它挂在read(
更新 #whatever-it-is-I-hate-this-& (#^& -issue!这已经开始发生在我的两个客户身上。我刚刚发火了tcpdump
,看起来MySQL 的响应从未完全发送。 TCP 流似乎在发送完整的 MySQL 响应之前挂起。(不过我仍在调查)
更新#I-have-gone-completly-crazy-but-it-works-now-kinda:好的,这没有意义,但我找到了解决方案。如果我为 MySQL 服务器的eth2
接口分配第二个 IP 地址,并为 NFS 流量使用一个 IP,为 MySQL 使用第二个 IP,那么问题就会消失。就像我不知何故......如果两个 NFS+MySQL 流量都转到该 IP,则 Ip 地址超载。但这毫无意义,因为您不能“超载”IP 地址。饱和接口肯定,但它是相同的接口。
知道这里到底发生了什么吗?在这一点上,这可能是一个 unix.SE 或 ServerFault 问题......(至少它现在有效......)
更新#why-oh-why:这个问题仍然存在。即使使用两个不同的 IP,它也开始发生。我可以继续创建新的私有 IP,但显然出了点问题。
想法(不确定是否适用于 MyISAM,但我使用 InnoDB)
更改索引“parent”,使其位于 3 列上:parent、order、name。这与 WHERE .. ORDER BY 匹配
消除SELECT *
。只取您需要的列。将任何其他列添加到索引“parent”
这将允许优化器仅 使用索引,因为它现在正在覆盖。就目前而言,您必须读取整个表,因为索引对该查询没有用处
归档时间: |
|
查看次数: |
10073 次 |
最近记录: |