Mysql Query Optimization - PullRequest
       11

Mysql Query Optimization

0 голосов
/ 11 декабря 2008

У меня следующий запрос SQL:

select expr1, operator, expr2, count(*) as c 
from log_keyword_fulltext 
group by expr1, operator, expr2 
order by c desc limit 2000;

Проблема: count(*) как часть моего заказа по убивает мое приложение, возможно потому, что оно не использует индекс. Я хотел бы знать, есть ли способ сделать это быстрее, например, select внутри другого select или что-то в этом роде.

Мой SELECT объяснил:

+----+-------------+----------------------+-------+---------------+-------+---------+------+--------+----------------------------------------------+
| id | select_type | table                | type  | possible_keys | key   | key_len | ref  | rows   | Extra                                        |
+----+-------------+----------------------+-------+---------------+-------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | log_keyword_fulltext | index | NULL          | expr1 | 208     | NULL | 110000 | Using index; Using temporary; Using filesort | 
+----+-------------+----------------------+-------+---------------+-------+---------+------+--------+----------------------------------------------+

ОБНОВЛЕНИЕ:

Я пытался сделать такой подзапрос

select * from (select b.expr1,b.operator,b.expr2,count(*) as c 
from log_keyword_fulltext b group by b.expr1,b.operator,b.expr2) x 
order by x.c desc limit 2000;

работает, но не быстрее, вот объяснение:

+----+-------------+------------+-------+---------------+-------+---------+------+--------+----------------+
| id | select_type | table      | type  | possible_keys | key   | key_len | ref  | rows   | Extra          |
+----+-------------+------------+-------+---------------+-------+---------+------+--------+----------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL  | NULL    | NULL |  38398 | Using filesort | 
|  2 | DERIVED     | b          | index | NULL          | expr1 | 208     | NULL | 110000 | Using index    | 
+----+-------------+------------+-------+---------------+-------+---------+------+--------+----------------+

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

Ответы [ 6 ]

2 голосов
/ 11 декабря 2008

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

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

1 голос
/ 11 декабря 2008

Чего мне не хватает? Я не вижу предложения WHERE. Мне кажется, вы запрашиваете сканирование таблицы.

Если вы рассчитываете на свое предложение "LIMIT", вам не повезло - это совокупный расчет COUNT.

1 голос
/ 11 декабря 2008

Всегда старайтесь брать счетчик какого-то одного столбца вместо счетчика (*), поскольку он учитывает перестановку каждого столбца каждой строки. Так что это занимает больше времени

Eg:

select expr1, operator, expr2, count(expr1) as c 
from log_keyword_fulltext 
group by expr1, operator, expr2 
order by c desc limit 2000;
0 голосов
/ 11 декабря 2008

Лучший способ предотвратить сканирование таблицы - добавить индекс покрытия для тех полей, к которым вы регулярно обращаетесь. Существует единовременная стоимость для создания индекса. Кроме того, для операций INSERT и DELETE в таблице предусмотрены некоторые дополнительные расходы, поэтому индекс можно обновлять.

Индексы покрытия не позволяют базе данных считывать всю запись в памяти, чтобы получить доступ к значениям для нескольких полей, которые вас интересуют. Весь запрос можно выполнить вне индекса.

ALTER TABLE `log_keyword_fulltext` ADD INDEX `idx_name`(expr1, operator, expr2)

Если это не фактические поля, а операции над полями, например left (foo, 20), вы можете индексировать часть поля, которую вы будете использовать в будущих предложениях SELECT или WHERE.

См. на этой странице для других идей оптимизации.

0 голосов
/ 11 декабря 2008

Что вы подразумеваете под «убить ваше приложение»? Каков контекст? Как часто вы запускаете этот запрос? Что происходит с базой данных во время выполнения этого запроса? Этот конкретный результат должен быть в режиме реального времени? Каковы условия (вставки / с, выбор / с, размер дБ и т. Д.)

Вот что вы могли бы сделать:

  1. Сохранить счет в отдельной таблице, которую вы будете обновлять с помощью триггеров при вставке / удалении

  2. Если вы не можете заставить MySQL сделать это с помощью простого смахивания таблиц с помощью магии, попробуйте хранимую процедуру, чтобы сделать что-то вроде (псевдокод):

    CREATE TEMP TABLE t (e1 EXP_T, op OP_T, e2 EXP_T, count INTEGER)
    ADD AN INDEX ON count
    FOR EACH LINE OF SELECT exp1,operator,exp2 FROM log_blah DO
           UPDATE t SET count=count+1 WHERE exp1=e1 AND operator=op AND exp2=e2
           IF IT DOES NOT WORK INSERT INTO t VALUES (exp1,operator,exp2,1)
    DONE
    SELECT * FROM t ORDER BY count DESC LIMIT 2000
    

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

0 голосов
/ 11 декабря 2008

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

Не уверен, что это работает в MySQL, но в PostreSQL или Oracle, это будет

create foo as 
   select expr1, operator, expr2, count(*) as c
   from log_keyword_fulltext 
   group by expr1, operator, expr2;
select * from foo order by c desc limit 2000;

Кроме того, вам нужно будет выполнить все подсчеты, чтобы отсортировать их, поэтому предложение limit не помешает ему выполнить все эти вычисления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...