这是一个 MySQL 5.0.26 服务器,运行在 SuSE Enterprise 10 上。这可能是一个 Serverfault 问题。
使用这些特定查询(如下)的网络用户界面有时会显示 30+,最坏情况下甚至会显示 120+ 秒,以生成所涉及的页面.
在开发中,当查询单独运行时,它们在第一次运行时最多需要 20 秒(没有启用查询缓存),但之后的任何地方都需要 2 到 7 秒 - 我假设因为所涉及的表和索引已经放入 ram。
据我所知,最长的加载时间是由读取/更新锁定引起的。这些是 MyISAM 表。所以它看起来像是一个很长的更新进来了,然后是几个 7 秒的查询,它们只是累加起来。我同意这个解释。
我不满意的是 MySQL 似乎没有利用它所在的硬件,虽然瓶颈似乎是数据库,我无法理解为什么。
我会说“投入更多硬件”,但我们做到了,而且情况似乎并没有改变。在最慢的时间查看“顶部”永远不会显示 mysqld
的 cpu 或内存使用率,就好像服务器根本没有问题一样 - 但是,为什么查询需要这么长时间?
我怎样才能让 MySQL 使用这个硬件的废话,或者找出我做错了什么?
额外的细节:
在 MySQL Administrator(适用于 Windows)的“Memory Health”选项卡上,Key Buffer 的使用率不到 1/8 - 因此所有索引都应该在 RAM 中。我可以提供任何可能有帮助的图表的屏幕截图。
非常想解决这个问题。可以这么说,有遗留代码“生成”这些查询,并且它们几乎保持原样。我已经在所涉及的表上尝试了所有索引组合,但欢迎提出任何建议。
这是开发中的当前 Create Table 语句(我添加的“实验”键似乎对示例查询有帮助一点):
CREATE TABLE `registration_task` (
`id` varchar(36) NOT NULL default '',
`date_entered` datetime NOT NULL default '0000-00-00 00:00:00',
`date_modified` datetime NOT NULL default '0000-00-00 00:00:00',
`assigned_user_id` varchar(36) default NULL,
`modified_user_id` varchar(36) default NULL,
`created_by` varchar(36) default NULL,
`name` varchar(80) NOT NULL default '',
`status` varchar(255) default NULL,
`date_due` date default NULL,
`time_due` time default NULL,
`date_start` date default NULL,
`time_start` time default NULL,
`parent_id` varchar(36) NOT NULL default '',
`priority` varchar(255) NOT NULL default '9',
`description` text,
`order_number` int(11) default '1',
`task_number` int(11) default NULL,
`depends_on_id` varchar(36) default NULL,
`milestone_flag` varchar(255) default NULL,
`estimated_effort` int(11) default NULL,
`actual_effort` int(11) default NULL,
`utilization` int(11) default '100',
`percent_complete` int(11) default '0',
`deleted` tinyint(1) NOT NULL default '0',
`wf_task_id` varchar(36) default '0',
`reg_field` varchar(8) default '',
`date_offset` int(11) default '0',
`date_source` varchar(10) default '',
`date_completed` date default '0000-00-00',
`completed_id` varchar(36) default NULL,
`original_name` varchar(80) default NULL,
PRIMARY KEY (`id`),
KEY `idx_reg_task_p` (`deleted`,`parent_id`),
KEY `By_Assignee` (`assigned_user_id`,`deleted`),
KEY `status_assignee` (`status`,`deleted`),
KEY `experimental` (`deleted`,`status`,`assigned_user_id`,`parent_id`,`date_due`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
还有一个荒谬的问题:
SELECT
users.user_name
assigned_user_name,
registration.FIELD001 parent_name,
registration_task.status status,
registration_task.date_modified date_modified,
registration_task.date_due date_due,
registration.FIELD240 assigned_wf,
if(LENGTH(registration_task.description)>0,1,0) has_description,
registration_task.*
FROM
registration_task LEFT JOIN users ON registration_task.assigned_user_id=users.id
LEFT JOIN registration ON registration_task.parent_id=registration.id
where
(registration_task.status != 'Completed' AND registration.FIELD001 LIKE '%'
AND registration_task.name LIKE '%' AND registration.FIELD060 LIKE 'GN001472%')
AND registration_task.deleted=0
ORDER BY date_due asc LIMIT 0,20;
my.cnf - '[mysqld]' 部分。
[mysqld]
port = 3306
socket = /var/lib/mysql/mysql.sock
skip-locking
key_buffer = 384M
max_allowed_packet = 100M
table_cache = 2048
sort_buffer_size = 2M
net_buffer_length = 100M
read_buffer_size = 2M
read_rnd_buffer_size = 160M
myisam_sort_buffer_size = 128M
query_cache_size = 16M
query_cache_limit = 1M
EXPLAIN 上面的查询,没有额外的索引
:+----+-------------+-------------------+--------+--------------------------------+----------------+---------+------------------------------------------------+---------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------------------+--------+--------------------------------+----------------+---------+------------------------------------------------+---------+-----------------------------+ | 1 | SIMPLE | registration_task | ref | idx_reg_task_p,status_assignee | idx_reg_task_p | 1 | const | 1067354 | Using where; Using filesort | | 1 | SIMPLE | registration | eq_ref | PRIMARY,gbl | PRIMARY | 8 | sugarcrm401.registration_task.parent_id | 1 | Using where | | 1 | SIMPLE | users | ref | PRIMARY | PRIMARY | 38 | sugarcrm401.registration_task.assigned_user_id | 1 | | +----+-------------+-------------------+--------+--------------------------------+----------------+---------+------------------------------------------------+---------+-----------------------------+
EXPLAIN above query, with 'experimental' index:
+----+-------------+-------------------+--------+-----------------------------------------------------------+------------------+---------+------------------------------------------------+--------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------------------+--------+-----------------------------------------------------------+------------------+---------+------------------------------------------------+--------+-----------------------------+ | 1 | SIMPLE | registration_task | range | idx_reg_task_p,status_assignee,NewIndex1,tcg_experimental | tcg_experimental | 259 | NULL | 103345 | Using where; Using filesort | | 1 | SIMPLE | registration | eq_ref | PRIMARY,gbl | PRIMARY | 8 | sugarcrm401.registration_task.parent_id | 1 | Using where | | 1 | SIMPLE | users | ref | PRIMARY | PRIMARY | 38 | sugarcrm401.registration_task.assigned_user_id | 1 | | +----+-------------+-------------------+--------+-----------------------------------------------------------+------------------+---------+------------------------------------------------+--------+-----------------------------+
SHOW INDEXES FROM registration_task;
mysql> SHOW INDEXES FROM registration_task; +-------------------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | +-------------------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+ | registration_task | 0 | PRIMARY | 1 | id | A | 1445612 | NULL | NULL | | BTREE | | | registration_task | 1 | idx_reg_task_p | 1 | deleted | A | 2 | NULL | NULL | | BTREE | | | registration_task | 1 | idx_reg_task_p | 2 | parent_id | A | 57824 | NULL | NULL | | BTREE | | | registration_task | 1 | By_Assignee | 1 | assigned_user_id | A | 5295 | NULL | NULL | YES | BTREE | | | registration_task | 1 | By_Assignee | 2 | deleted | A | 5334 | NULL | NULL | | BTREE | | | registration_task | 1 | status_assignee | 1 | status | A | 18 | NULL | NULL | YES | BTREE | | | registration_task | 1 | status_assignee | 2 | deleted | A | 23 | NULL | NULL | | BTREE | | | registration_task | 1 | NewIndex1 | 1 | deleted | A | 2 | NULL | NULL | | BTREE | | | registration_task | 1 | NewIndex1 | 2 | assigned_user_id | A | 5334 | NULL | NULL | YES | BTREE | | | registration_task | 1 | NewIndex1 | 3 | parent_id | A | 180701 | NULL | NULL | | BTREE | | | registration_task | 1 | tcg_experimental | 1 | date_due | A | 1919 | NULL | NULL | YES | BTREE | | | registration_task | 1 | tcg_experimental | 2 | deleted | A | 3191 | NULL | NULL | | BTREE | | | registration_task | 1 | tcg_experimental | 3 | status | A | 8503 | NULL | NULL | YES | BTREE | | | registration_task | 1 | tcg_experimental | 4 | assigned_user_id | A | 53541 | NULL | NULL | YES | BTREE | | | registration_task | 1 | tcg_experimental | 5 | parent_id | A | 722806 | NULL | NULL | | BTREE | | +-------------------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+ 15 rows in set (0.00 sec)
Solution?
I think I may have solved the problem, that to some will seem so embarrassingly obvious, but was somehow overlooked until now: The definition of registration.id
, is:
`id` bigint(20) unsigned NOT NULL auto_increment
虽然 registration_task.parent_id
(FK 到 registration.id
)是:
`parent_id` varchar(36) NOT NULL
通过以下方式更改:
alter table `sugarcrm401`.`registration_task` change `parent_id` `parent_id` bigint(20) UNSIGNED NOT NULL;
... 导致 EXPLAIN 仅显示检查过的 25 行,较早的是 651,903,而在强制执行时最好是 103,345疯狂的索引。
如果我发布了 registration
表的表定义,我相信有人可能已经发现了它。我将验证这一点并在周末后发布跟进。
最佳答案
当您有一个查询说 != 时,它总是会很慢。请注意,即使状态字段在两个不同的索引上,索引也没有被用于查询的那部分!
您不希望 varchar(255) 字段完全索引并且将其作为两个不同键的一部分会使您的更新非常非常慢。总共有五个索引只会增加困惑。如果您在更新发生的同时进行任何选择,那么正如您已经看到的那样,这真的会花费很长时间。
您可能想要做的是仅索引 255 个字符字段的一小部分。更好的是,您可能希望在此处使用整数 (statusCode) 而不是状态。这将大大加快速度。
拥有更多内存或更多 CPU 在这里无济于事。拥有一个额外的硬盘驱动器可为您提供 20 - 30% 的速度提升。但是,只需重新组织索引,您就可以在不到一秒的时间内完成相同的查询。
关于mysql - 如何让MySQL利用可用的系统资源,或者找到 "the real problem"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3023400/