Как ускорить подсчет (*), который уже использует индексы? (MyISAM) - PullRequest
2 голосов
/ 17 сентября 2009

У меня есть 3 большие таблицы (строки 10k, 10k и 100M), и я пытаюсь сделать простой подсчет их объединения, где все объединенные столбцы проиндексированы. Почему COUNT (*) занимает так много времени, и как я могу ускорить его (без триггеров и сводной информации)?

mysql> describe SELECT COUNT(*) FROM `metaward_alias` INNER JOIN `metaward_achiever` ON (`metaward_alias`.`id` = `metaward_achiever`.`alias_id`) INNER JOIN `metaward_award` ON (`metaward_achiever`.`award_id` = `metaward_award`.`id`) WHERE `metaward_award`.`owner_id` = 8;
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows | Extra       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
|  1 | SIMPLE      | metaward_award    | ref    | PRIMARY,metaward_award_owner_id                       | metaward_award_owner_id    | 4       | const                           | 1552 |             | 
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | paul.metaward_award.id          | 2498 |             | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |    1 | Using index | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+------+-------------+
3 rows in set (0.00 sec)

Но на самом деле выполнение запроса занимает около 10 минут, и я нахожусь на MyISAM, поэтому таблицы на этот период полностью заблокированы

Ответы [ 3 ]

3 голосов
/ 17 сентября 2009

Полагаю, причина в том, что вы выполняете огромное объединение трех таблиц (без применения условия where сначала получается 10k * 10k * 100M = 10 16 строк). Попробуйте переупорядочить объединения (например, начните с metaward_award, затем присоединитесь только к metaward_achiever, посмотрите, сколько времени это займет, затем попробуйте подключить metaward_alias, возможно, используя подзапрос для форсирования предпочтительного порядка оценки).

Если это не поможет, возможно, вам придется денормализовать ваши данные, например, путем сохранения количества псевдонимов для конкретного metaward_achiever. Тогда ты избавишься от одного соединения в целом. Возможно, вы даже можете кэшировать суммы для metaward_award, в зависимости от того, как и как часто обновляются ваши данные.

Другая вещь, которая может помочь, - это загрузить все содержимое вашей базы данных в ОЗУ: -)

1 голос
/ 17 сентября 2009

10 минут слишком много для этого запроса. Я думаю, что вы должны иметь очень маленький кеш ключа. Вы можете получить его размер в байтах с помощью:

SELECT @@key_buffer_size

Прежде всего, вы должны запустить ANALYZE TABLE или OPTIMIZE TABLE . Они отсортируют ваш индекс и могут немного улучшить производительность.

Вы также должны посмотреть, можете ли вы использовать более компактные типы для ваших столбцов. Например, если у вас не будет более 16 миллионов владельцев, наград или псевдонимов, вы можете изменить столбцы INT на MEDIUMINT (UNSIGNED, конечно). Возможно, даже SMALLINT в некоторых случаях? Это уменьшит ваш индексный след, и вы поместите больше его в кеш.

1 голос
/ 17 сентября 2009

Убедитесь, что у вас есть индексы:

metaward_alias      id
metaward_achiever   alias_id
metaward_achiever   award_id
metaward_award      id
metaward_award      owner_id

Я уверен, что многие люди также предложат рассчитывать на определенный столбец, но в MySql это не имеет никакого значения для вашего запроса.

UPDATE

Вы также можете попытаться установить условие для основной таблицы вместо одной из соединяемых таблиц. Это даст вам тот же результат, но это может быть быстрее (я не знаю, насколько умный MySql):

SELECT COUNT(*) FROM `metaward_award` 
   INNER JOIN `metaward_achiever` 
      ON (`metaward_achiever`.`award_id` = `metaward_award`.`id`) 
   INNER JOIN `metaward_alias` 
      ON (`metaward_alias`.`id` = `metaward_achiever`.`alias_id`) 
WHERE `metaward_award`.`owner_id` = 8
...