Mysql с большими таблицами: как оптимизировать этот запрос? - PullRequest
2 голосов
/ 02 декабря 2010

У меня есть таблица с использованием InnoDB, в которой хранятся все сообщения, отправленные моей системой. В настоящее время таблица имеет 40 миллионов строк и растет на 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 ]

1 голос
/ 02 декабря 2010

вы используете 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

Надеюсь, это поможет:)

0 голосов
/ 02 декабря 2010

COUNT(*) не загружается из кэша таблицы, поскольку у вас есть предложение WHERE, используя EXPLAIN в качестве @jason, попробуйте изменить его на COUNT (id) и посмотрите, поможет ли это.

Я могу ошибаться, но я также думаю, что ваши индексы должны быть в том же порядке, что и предложение WHERE. Так как ваше предложение WHERE использует timeLogged перед user, тогда ваш индекс должен быть KEY user_timeLogged ( timeLogged , user) `

Опять же, EXPLAIN скажет вам, имеет ли это изменение индекса значение.

...