Mysql查询优化

时间:2008-12-11 13:22:47

标签: mysql optimization

我有以下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 个答案:

答案 0 :(得分:2)

您正在运行需要扫描整个表的查询,这不会扩展。没有WHERE子句,因此绝对需要扫描整个事物。

考虑维护一些摘要表,而不是经常进行此查询。

答案 1 :(得分:1)

总是尝试计算一些单列而不是计数(*),因为它需要计算每行每列的permutaiotion。所以需要更长的时间

Eg:

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

答案 2 :(得分:1)

我错过了什么?我没有看到WHERE子句。在我看来,您请求进行表格扫描。

如果你指望你的“LIMIT”条款,那你就不幸了 - 这就是COUNT汇总计算。

答案 3 :(得分:0)

“杀死你的申请”是什么意思?背景是什么?您多久运行一次此查询?运行此查询时数据库上发生了什么?这个特定的结果是否必须是实时的?有什么条件(insert / s,选择/ s,db大小等)

以下是您可以做的事情:

  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
    
  3. 1可能就是你想要的。并忘记索引,这个查询无论如何都要刷整个表。

答案 4 :(得分:0)

防止表扫描的最佳方法是为您经常访问的字段添加封面索引。创建索引需要一次性成本。对表上的INSERT和DELETE操作还有一些额外的成本,因此可以更新索引。

封面索引可防止数据库必须将整个记录读入内存,以便访问您关心的少数字段的值。整个查询可以在索引上运行。

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

如果这些不是实际字段,而是字段上的操作,例如left(foo,20),您实际上可以索引将在SELECT或WHERE子句中使用的字段部分。

有关其他优化提示,请参阅this page

答案 5 :(得分:-1)

试图计算和排序它将是一个杀手。我建议尝试用计数制作一个临时表,然后选择...按顺序排列。

不确定这是否适用于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;

此外,为了对它们进行排序,您将不得不进行所有计数,因此限制条款不会阻止它进行所有这些计算。