Mysql提高涉及多个表的查询速度

时间:2014-01-03 12:04:33

标签: mysql performance query-optimization

我有以下查询

SELECT a.id, b.id from table1 AS a, table2 AS b
WHERE a.table2_id IS NULL
AND a.plane = SUBSTRING(b.imb, 1, 20)
AND (a.stat LIKE "f%" OR a.stat LIKE "F%")

以下是EXPLAIN的输出

+----+-------------+-------+------+-------------------------------------------------------------------------------------------+------------------------------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys                                                                             | key                          | key_len | ref  | rows     | Extra       |
+----+-------------+-------+------+-------------------------------------------------------------------------------------------+------------------------------+---------+------+----------+-------------+
|  1 | SIMPLE      | b     | ALL  | NULL                                                                                      | NULL                         | NULL    | NULL | 28578039 |             |
|  1 | SIMPLE      | a     | ref  | index_on_plane,index_on_table2_id_id,mysql_confirmstat_on_stat                            | index_on_plane               | 258     |  func|        2 | Using where |
+----+-------------+-------+------+-------------------------------------------------------------------------------------------+------------------------------+---------+------+----------+-------------+

查询需要 80分钟才能执行。

table1上的索引如下

+--------------+------------+--------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table        | Non_unique | Key_name                       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+--------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| table1 |          0 | PRIMARY             |            1 | id         | A         |    50319117 |     NULL | NULL   |      | BTREE      |         |               |
| table1 |          1 | index_on_post       |            1 | post       | A         |     7188445 |     NULL | NULL   | YES  | BTREE      |         |               |
| table1 |          1 | index_on_plane      |            1 | plane      | A         |    25159558 |     NULL | NULL   | YES  | BTREE      |         |               |
| table1 |          1 | index_on_table2_id  |            1 | table2_id  | A         |    25159558 |     NULL | NULL   | YES  | BTREE      |         |               |
| table1 |          1 | index_on_stat       |            1 | stat       | A         |         187 |     NULL | NULL   | YES  | BTREE      |         |               |
+--------------+------------+--------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

和table2索引是。

+-------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name                  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| table2 |          0 | PRIMARY                 |            1 | id             | A         |    28578039 |     NULL | NULL   |      | BTREE      |         |               |
| table2 |          1 | index_on_post           |            1 | post           | A         |    28578039 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2 |          1 | index_on_ver            |            1 | ver            | A         |        1371 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2 |          1 | index_on_imb            |            1 | imb            | A         |    28578039 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

如何改进此查询的执行时间?

这是更新后的解释

EXPLAIN SELECT STRAIGHT_JOIN a.id, b.id from table1 AS a JOIN b AS b  
ON a.plane=substring(b.imb,1,20) 
WHERE a.table2_id IS NULL  
AND (a.stat LIKE "f%" OR a.stat LIKE "F%");
+----+-------------+-------+------+-------------------------------------------------------------------------------------------+-------------------------------+---------+-------+----------+--------------------------------+
| id | select_type | table | type | possible_keys                                       | key                | key_len | ref   | rows     | Extra                          |
+----+-------------+-------+------+-------------------------------------------------------------------------------------------+-------------------------------+---------+-------+----------+--------------------------------+
|  1 | SIMPLE      | a     | ref  | index_on_plane,index_on_table2_id,index_on_stat     | index_on_table2_id | 5       | const |   500543 | Using where                    |
|  1 | SIMPLE      | b     | ALL  | NULL                                                | NULL               | NULL    | NULL  | 28578039 | Using where; Using join buffer |
+----+-------------+-------+------+-------------------------------------------------------------------------------------------+-------------------------------+---------+-------+----------+--------------------------------+

SQL小提琴链接http://www.sqlfiddle.com/#!2/362a6/4

4 个答案:

答案 0 :(得分:1)

您的架构至少会以三种方式使您的查询失败。您将需要修改您的架构,以获得像这样的体面表现。我可以看到三种修复架构的方法。

第一种方式(可能非常容易修复):

  a.stat LIKE "f%" OR a.stat LIKE "F%"

OR操作可能会使查询的运行时间翻倍。但是,如果您将stat列的排序规则设置为不区分大小写,则可以将其更改为

  a.stat LIKE "f%"

此列已有索引。

第二种方式(也许不是很难修复)。该条款明确地否定了索引的使用;当涉及NULL值时,它们是无用的。

WHERE a.table2_id IS NULL

您可以将table2_id的定义更改为NOT NULL并提供默认值(可能为零)以指示缺少数据吗?如果是这样,您将处于良好状态,因为您将能够将其更改为使用索引的搜索谓词。

WHERE a.table2_id = 0

第三种方式(可能很难)。本条款中的函数的存在使得在加入时使用索引失败。

WHERE ... a.plane = SUBSTRING(b.imb, 1, 20)

你需要制作一个额外的专栏(是的,是的,在甲骨文中,它可能是一个功能索引,但谁拥有那种钱?)调用b.plane或存储在其中的子字符串。

如果你做了所有这些事情并稍微重构一下你的查询,这就是它的样子:

SELECT a.id AS aid, 
       b.id AS bid
  FROM table1 AS a
  JOIN table2 AS b ON a.plane = b.plane /* the new column */
 WHERE a.stat LIKE 'f%'
   AND a.table2_id = 0

最后,您可以通过创建以下复合索引来覆盖查询的索引,从而略微调整此性能。如果您不确定这意味着什么,请查找覆盖索引

 table1  (table2_id, stat, plane, id)
 table2  (plane, id)  /* plane is your new column */

覆盖索引需要权衡:它们会降低插入和更新操作的速度,但会加快查询速度。只有你有足够的信息才能做出明智的权衡。

答案 1 :(得分:0)

必须对连接操作正在执行的列进行索引,并且MySQL优化器应该使用它来获得更好的性能。它将最小化检查的行数(连接大小)

试试这个

SELECT STRAIGHT_JOIN a.id, b.id from table1 AS a JOIN table2 AS b ON a.plane=substring(b.imb,1,20)
WHERE a.table2__id IS NULL and (a.stat LIKE "f%" OR a.stat LIKE "F%")

首先检查执行计划。如果它甚至没有使用index_on_imb索引,请创建一个组合table2.imbtable2.id的复合索引,其中table2.imb将按顺序排列。

答案 2 :(得分:0)

除了我对ID colmns的评论之外,您似乎正在尝试在“平面”而不是ID列上回填连接。如果我是正确的,你想要table2中table1中没有匹配的所有记录

select
      a.id,
      b.id
   from
      table2 b
         left join table1 a
            on b.id = a.table2_id
           AND substr( b.imb, 1, 20 ) = a.plane
           AND (   a.stat LIKE "f%" 
                OR a.stat LIKE "F%")
   where
      a.table2_id is null

另外,为了帮助索引加入,我会覆盖索引,这样引擎就不必回到原始数据来获取合格的记录。

table1 -- index ( plane, stat, table2_id, id )
table2 -- index ( imb, id )

但是,请再次澄清表连接的基础或者没有它基于一个键...根据table1的示例列有一个列table2_id,我认为这与table2.id有关。

执行左连接的目的基本上是说...对于左侧表中的每个记录(在我的示例table2中),无论条件/条件如何都加入到右侧表(table1) - 现在使用KEY ID列作为主要基础,然后使用平面和状态设置。

所以,即使我在table2_id上的两个表之间进行连接,如果它找到匹配项,它也将被排除......只有当它找不到匹配项时才会被包括在内。

最后,既然你隐藏了表格的真正基础,那么你就是在猜测那些帮助的工作。即使它是“个人”类型的数据,您也没有显示任何数据,我只是如何获得它。对于你想要获得的更好的心理形象比具有有限背景的虚假表/列名更好。

答案 3 :(得分:0)

派生表可能会提高性能,在这种情况下取​​决于索引index_on_table2_id,index_on_stat ..

SELECT a.id, b.id from table1 AS a, table2 AS b
WHERE a.table2_id IS NULL
AND a.plane = SUBSTRING(b.imb, 1, 20)
AND (a.stat LIKE "f%" OR a.stat LIKE "F%")

可以改写为.. 派生表将强制MySQL检查500543行,就像最后一个解释说的那样

SELECT a.id, b.id
FROM (SELECT plane FROM table1 WHERE (a.table2_id IS NULL) AND (a.stat LIKE "f%" OR a.stat LIKE "F%")) a
INNER JOIN table2 b
ON a.plane = SUBSTRING(b.imb, 1, 20)