需要建议优化SQL查询(在MySQL上更新)

时间:2012-04-24 16:37:45

标签: mysql sql optimization indexing

我使用慢查询日志对我的数据库进行了性能分析。原来这是头号烦恼:

UPDATE
    t1
SET
  v1t1 =
  (
    SELECT
        t2.v3t2
    FROM
        t2
    WHERE
        t2.v2t2 = t1.v2t1
    AND t2.v1t2 <= '2012-04-24'
    ORDER BY
        t2.v1t2 DESC,
        t2.v3t2 DESC
    LIMIT 1
);

子查询本身已经很慢了。我尝试使用DISTINCT,GROUP BY和更多子查询进行变换,但是在4秒内没有执行任何操作。例如以下查询

SELECT v2t2, v3t2
FROM t2
WHERE t2.v1t2 <= '2012-04-24'
GROUP BY v2t2
ORDER BY v1t2 DESC    

需要:

mysql> SELECT ...
...    
69054 rows in set (5.61 sec)    

mysql> EXPLAIN SELECT ...
+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+
| id | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra                                        |
+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | t2          | ALL  | v1t2          | NULL | NULL    | NULL | 5203965 | Using where; Using temporary; Using filesort |
+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+

mysql> SHOW CREATE TABLE t2;
...
  PRIMARY KEY (`v3t2`),
  KEY `v1t2_v3t2` (`v1t2`,`v3t2`),
  KEY `v1t2` (`v1t2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8  

SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
|    77070 |
+----------+

SELECT COUNT(*) FROM t2;
+----------+
| COUNT(*) |
+----------+
|  5203965 |
+----------+

我正在尝试获取最新的条目(v3t2)及其父级(v2t2)。不应该是那么大的交易,不是吗?有没有人有任何建议,我应该转向哪个旋钮?非常感谢任何帮助或提示!

这应该是一个更合适的SELECT语句:

SELECT
    t1.v2t1,
  (
    SELECT
        t2.v3t2
    FROM
        t2
    WHERE
        t2.v2t2 = t1.v2t1
    AND t2.v1t2 <= '2012-04-24'
    ORDER BY
        t2.v1t2 DESC,
        t2.v3t2 DESC
    LIMIT 1
) AS latest   
FROM
    t1

3 个答案:

答案 0 :(得分:1)

您的ORDER BY ... LIMIT 1强制数据库执行表的完整扫描,只返回1行。它看起来非常像索引的候选者。

在构建索引之前,请运行以下命令检查fileds选择性:

SELECT count(*), count(v1t2), count(DISTINCT v1t2) FROM t2;

如果您的列中包含大量非NULL值且不同值的数量超过非NULL的40%,那么构建索引是件好事去吧。

如果index没有提供任何帮助,您应该分析列中的数据。您正在使用t2.v1t2 <= '2012-04-24'条件,如果您的表中有一组历史记录,则不会给规划器任何内容,因为预计所有行都是过去的,因此完全扫描是无论如何最好的选择。因此,indexe是无用的。

你应该做的是思考如何以某种方式重写你的查询,只检查有限的记录子集。您的构造ORDER BY ... DESC LIMIT 1表明您可能希望最近的条目达到'2012-04-24'(包括)。为什么不尝试将查询重写为:

SELECT v2t2, v3t2
FROM t2
WHERE t2.v1t2 => date_add('2012-04-24' interval '-10' DAY)
GROUP BY v2t2
ORDER BY v1t2 DESC;

这只是一个例子,了解数据库的设计和数据的性质可以构建更精确的查询。

答案 1 :(得分:0)

我将看一下为子选择t2构建的索引。你应该有v2t2的索引,可能有v1t2和v3t2的索引,因为它的排序。索引应该减少子选择在更新查询中使用它们之前必须查找结果的时间。

答案 2 :(得分:0)

这有效吗?通过使用的密钥删除其中一个排序和组。

UPDATE
    t1
SET
  v1t1 =
  (
    SELECT
        MAX(t2.v3t2)
    FROM
        t2
    WHERE
        t2.v2t2 = t1.v2t1
    AND t2.v1t2 <= '2012-04-24'
    GROUP BY t2.v1t2
    ORDER BY t2.v1t2 DESC
    LIMIT 1
);

替代版本

UPDATE `t1`
SET `v1t1` = (
  SELECT MAX(`t2`.`v3t2`)
  FROM `t2`
  WHERE `t2`.`v2t2` = `t1`.`v2t1`
  AND `t2`.`v1t2` = (
    SELECT MAX(`t2`.`v1t2`)
    FROM `t2`
    WHERE `t2`.`v2t2` = `t1`.`v2t1
    AND `t2`.`v1t2` <= '2012-04-24'
    LIMIT 1
  )
  LIMIT 1
);

并将此索引添加到t2

KEY `v2t2_v1t2` (`v2t2`, `v1t2`)