Mysql与大表:如何优化此查询?

时间:2010-12-01 21:26:33

标签: mysql query-optimization

我有一个使用InnoDB的表,它存储了我系统发送的所有消息。目前该表有4000万行,每月增长3/4百万。

我的查询基本上是选择从用户发送的消息和数据范围内的消息。这是一个简单的创建表:

CREATE TABLE `log` (
  `id` int(10) NOT NULL DEFAULT '0',
  `type` varchar(10) NOT NULL DEFAULT '',
  `timeLogged` int(11) NOT NULL DEFAULT '0',
  `orig` varchar(128) NOT NULL DEFAULT '',
  `rcpt` varchar(128) NOT NULL DEFAULT '',
  `user` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `timeLogged` (`timeLogged`),
  KEY `user` (`user`),
  KEY `user_timeLogged` (`user`,`timeLogged`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

注意:由于其他查询,我也有单独的索引。

查询如下所示:

SELECT COUNT(*) FROM log WHERE timeLogged BETWEEN 1282878000 AND 1382878000 AND user = 20

问题是此查询需要2分钟到10分钟,具体取决于用户和服务器负载,这是等待页面加载的时间太长。我在应用程序中启用了mysql缓存并缓存,但问题是当用户搜索新范围时,它不会命中缓存。

我的问题是:

  • 更改user_timeLogged索引会有什么不同吗?
  • 这是MySQL和大型数据库的问题吗?我的意思是,Oracle或其他数据库是否也遇到这个问题?

AFAIK,我的索引已正确创建,此查询不会花这么长时间。

感谢所有帮助过的人!

2 个答案:

答案 0 :(得分:1)

你正在使用innodb但没有充分利用你的innodb聚集索引(主键),因为它看起来像你的典型查询形式:

select <fields> from <table> where user_id = x and <datefield> between y and z

select <fields> from <table> where id = x 

以下文章可以帮助您优化查询的表格设计。

http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/

如果你理解正确的文章,你应该找到类似以下的东西:

drop table if exists user_log;
create table user_log
(
user_id int unsigned not null,
created_date datetime not null, 
log_type_id tinyint unsigned not null default 0, -- 1 byte vs varchar(10)
...
...
primary key (user_id, created_date, log_type_id)
)
engine=innodb;

以上是来自上述设计的一些查询性能统计数据:

<强>计数

select count(*) as counter from user_log

counter
=======
37770394

select count(*) as counter from user_log where 
 created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00'

counter
=======
35547897

基于用户和日期的查询(所有查询都使用冷缓存运行)

select count(*) as counter from user_log where user_id = 4755

counter
=======
7624

runtime = 0.215 secs


select count(*) as counter from user_log where 
 user_id = 4755 and created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00'

counter
=======
7404

runtime = 0.015 secs

select 
 user_id,
 created_date,
 count(*) as counter
from 
 user_log 
where 
 user_id = 4755 and created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00'
group by
 user_id, created_date
order by
 counter desc
limit 10;

runtime = 0.031 secs

希望这会有所帮助:)

答案 1 :(得分:0)

COUNT(*)未从表缓存加载,因为您有一个WHERE子句,使用EXPLAIN作为@jason提到,尝试将其更改为COUNT(id)并查看是否有帮助。

我可能错了,但我也认为您的索引必须与WHERE子句的顺序相同。由于您的WHERE子句在timeLogged之前使用user,因此您的索引应为KEY user_timeLogged ( timeLogged , user)`

同样,EXPLAIN会告诉你这个索引的变化是否有所作为。