我正在试图弄清楚如何在MySQL中优化一个非常慢的查询(我没有设计这个):
SELECT COUNT(*) FROM change_event me WHERE change_event_id > '1212281603783391';
+----------+
| COUNT(*) |
+----------+
| 3224022 |
+----------+
1 row in set (1 min 0.16 sec)
将其与完整计数进行比较:
select count(*) from change_event;
+----------+
| count(*) |
+----------+
| 6069102 |
+----------+
1 row in set (4.21 sec)
解释说明对我没有帮助:
explain SELECT COUNT(*) FROM change_event me WHERE change_event_id > '1212281603783391'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: me
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: NULL
rows: 4120213
Extra: Using where; Using index
1 row in set (0.00 sec)
好吧,它仍然认为它需要大约400万个条目来计算,但我可以更快地计算文件中的行数!我不明白为什么MySQL需要这么长时间。
以下是表格定义:
CREATE TABLE `change_event` (
`change_event_id` bigint(20) NOT NULL default '0',
`timestamp` datetime NOT NULL,
`change_type` enum('create','update','delete','noop') default NULL,
`changed_object_type` enum('Brand','Broadcast','Episode','OnDemand') NOT NULL,
`changed_object_id` varchar(255) default NULL,
`changed_object_modified` datetime NOT NULL default '1000-01-01 00:00:00',
`modified` datetime NOT NULL default '1000-01-01 00:00:00',
`created` datetime NOT NULL default '1000-01-01 00:00:00',
`pid` char(15) default NULL,
`episode_pid` char(15) default NULL,
`import_id` int(11) NOT NULL,
`status` enum('success','failure') NOT NULL,
`xml_diff` text,
`node_digest` char(32) default NULL,
PRIMARY KEY (`change_event_id`),
KEY `idx_change_events_changed_object_id` (`changed_object_id`),
KEY `idx_change_events_episode_pid` (`episode_pid`),
KEY `fk_import_id` (`import_id`),
KEY `idx_change_event_timestamp_ce_id` (`timestamp`,`change_event_id`),
KEY `idx_change_event_status` (`status`),
CONSTRAINT `fk_change_event_import` FOREIGN KEY (`import_id`) REFERENCES `import` (`import_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
版本:
$ mysql --version
mysql Ver 14.12 Distrib 5.0.37, for pc-solaris2.8 (i386) using readline 5.0
有什么明显的东西我不见了吗? (是的,我已经尝试过“SELECT COUNT(change_event_id)”,但没有性能差异。)
答案 0 :(得分:46)
InnoDB使用群集主键,因此主键与数据页中的行一起存储,而不是存储在单独的索引页中。为了进行范围扫描,您仍然必须扫描数据页中的所有可能宽的行;请注意,此表包含TEXT列。
我会尝试两件事:
optimize table
。这将确保数据页面按排序顺序进行物理存储。这可能会加速群集主键上的范围扫描。(如果它从零开始递增,你也可能想让change_event_id列bigint 无符号)
答案 1 :(得分:14)
以下是我建议的一些事项:
将列从“bigint”更改为“int unsigned”。您真的期望在此表中拥有超过42亿条记录吗?如果没有,那么你就浪费了超宽领域的空间(和时间)。 MySQL索引在较小的数据类型上更有效。
运行“OPTIMIZE TABLE”命令,然后查看您的查询是否更快。
您可能还会根据ID字段考虑partitioning your table,尤其是旧记录(ID值较低)随时间变得不太相关时。分区表通常可以比一个巨大的未分区表更快地执行聚合查询。
编辑:
仔细查看此表,它看起来像一个日志式表,其中插入行但从未修改过。
如果这是真的,那么您可能不需要InnoDB存储引擎提供的所有事务安全性,并且您可能能够使用switching to MyISAM,这在聚合查询上效率更高。
答案 2 :(得分:5)
我之前使用IP地理定位数据库遇到了这样的行为。经过一些记录,MySQL从基于范围的查询的索引中获得任何优势的能力显然已经消失。使用地理位置数据库,我们通过将数据分段为足够合理的块来处理它,以允许使用索引。
答案 3 :(得分:3)
检查索引的碎片程度。在我的公司,我们有一个夜间导入过程,可以破坏我们的索引,随着时间的推移,它会对数据访问速度产生深远的影响。例如,我们有一个SQL过程,在解析索引花了3分钟后,花了2个小时才运行一天。我们使用SQL Server 2005查找可以在MySQL上检查此脚本的脚本。
更新:点击此链接:http://dev.mysql.com/doc/refman/5.0/en/innodb-file-defragmenting.html
答案 4 :(得分:1)
在该表上运行“analyze table_name
” - 索引可能不再是最优的。
您经常可以通过运行“show index from table_name
”来说明这一点。如果基数值为NULL
,则需要强制重新分析。
答案 5 :(得分:1)
MySQL确实首先说“使用where”,因为它确实需要从索引数据中读取所有记录/值以实际计算它们。借助InnoDb,它还试图“抓住”4密耳的记录范围来计算它。
您可能需要尝试不同的事务隔离级别:http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html#isolevel_read-uncommitted
并查看哪一个更好。
使用MyISAM它会很快,但使用密集写入模型会导致锁定问题。
答案 6 :(得分:0)
我会创建一个“计数器”表,并将“创建行”/“删除行”触发器添加到您正在计数的表中。触发器应该在每次插入/删除时增加/减少“计数器”表上的计数值,因此您不需要在每次需要时计算它们。
您也可以通过缓存计数器在应用程序端完成此操作,但这将涉及在每次插入/删除时清除“计数器缓存”。
如需参考,请查看此http://pure.rednoize.com/2007/04/03/mysql-performance-use-counter-tables/
答案 7 :(得分:0)
尽管我建议添加索引,但是为了使搜索更有效。我将命令留给您重试指标
CREATE INDEX ixid_1 ON change_event (change_event_id);
并重复查询
SELECT COUNT(*) FROM change_event me WHERE change_event_id > '1212281603783391';
-JACR