【问题标题】:How to avoid FileSort when using LEFT JOIN + ORDER BY?使用 LEFT JOIN + ORDER BY 时如何避免 FileSort?
【发布时间】:2016-03-10 03:25:13
【问题描述】:

mysql-5.6.24-win32.1432006610

我有两个简单的表,TUser(id, name)TMessage(id, uid, message)TUser 保存用户,TMessage 保存用户消息。

SQL如下,另见:http://sqlfiddle.com/#!9/7f099

CREATE TABLE TUser(
    id     INT UNSIGNED     PRIMARY KEY    NOT NULL    AUTO_INCREMENT,
    name   VARCHAR(128) NOT NULL
);


CREATE TABLE TMessage(
    id      INT UNSIGNED    PRIMARY KEY    NOT NULL    AUTO_INCREMENT,
    uid     INT UNSIGNED    NOT NULL,
    message VARCHAR(256)    NOT NULL
);


CREATE INDEX TMessageIndexUid ON TMessage(uid);

插入一些数据:

INSERT INTO TUser (name) VALUES 
     ('jack')
    ,('rose')
    ,('peter');


INSERT INTO TMessage(uid, message) VALUES
     (1, 'Hello jack')
    , (1, 'Jack, how are you')
    , (1, 'Good morning jack')
    , (2, 'I love you, rose')
    , (3, 'Peter, please call back')
    , (3, 'What are you doing, Peter');

当我运行以下 LEFT JOIN + ORDER BY 查询时,FileSort 显示在 EXPLAIN 结果中:

EXPLAIN
SELECT *
  FROM        TUser
  LEFT JOIN   TMessage 
  ON          TUser.id=TMessage.uid
  WHERE       TUser.id=3
  ORDER BY    TMessage.id DESC;



id  select_type table    type  possible_keys    key              key_len ref   rows Extra
1   SIMPLE      TUser    const PRIMARY;         PRIMARY          4       const 1    Using temporary; Using filesort
1   SIMPLE      TMessage ref   TMessageIndexUid TMessageIndexUid 4       const 2 \N

有什么问题吗?

【问题讨论】:

    标签: mysql filesort


    【解决方案1】:

    文件排序由 ORDER BY 引入:

    mysql> EXPLAIN
        -> SELECT *
        ->   FROM        TUser
        ->   LEFT JOIN   TMessage
        ->   ON          TUser.id=TMessage.uid
        ->   WHERE       TUser.id=3
        ->   ORDER BY    TMessage.id DESC;
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+---------------------------------+
    | id | select_type | table    | partitions | type  | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                           |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+---------------------------------+
    |  1 | SIMPLE      | TUser    | NULL       | const | PRIMARY          | PRIMARY          | 4       | const |    1 |   100.00 | Using temporary; Using filesort |
    |  1 | SIMPLE      | TMessage | NULL       | ref   | TMessageIndexUid | TMessageIndexUid | 4       | const |    4 |   100.00 | NULL                            |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+---------------------------------+
    2 rows in set, 1 warning (0.00 sec)
    
    mysql> EXPLAIN
        -> SELECT *
        ->   FROM        TUser
        ->   LEFT JOIN   TMessage
        ->   ON          TUser.id=TMessage.uid
        ->   WHERE       TUser.id=3;
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------+
    | id | select_type | table    | partitions | type  | possible_keys    | key              | key_len | ref   | rows | filtered | Extra |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | TUser    | NULL       | const | PRIMARY          | PRIMARY          | 4       | const |    1 |   100.00 | NULL  |
    |  1 | SIMPLE      | TMessage | NULL       | ref   | TMessageIndexUid | TMessageIndexUid | 4       | const |    4 |   100.00 | NULL  |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------+
    2 rows in set, 1 warning (0.00 sec)
    

    由于 order by 对 LEFT JOIN 结果进行操作,我不知道如何避免这种文件排序。

    如果将 LEFT JOIN 更改为 INNER JOIN 是可以接受的,这可能是一种绕过方式,同时会丢失一些与 TMessages 不匹配的用户信息。

    mysql> EXPLAIN
        -> SELECT *
        ->   FROM        TUser
        ->   INNER JOIN   TMessage
        ->   ON          TUser.id=TMessage.uid
        ->   WHERE       TUser.id=3
        ->   ORDER BY    TMessage.id DESC;
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------------+
    | id | select_type | table    | partitions | type  | possible_keys    | key              | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------------+
    |  1 | SIMPLE      | TUser    | NULL       | const | PRIMARY          | PRIMARY          | 4       | const |    1 |   100.00 | NULL        |
    |  1 | SIMPLE      | TMessage | NULL       | ref   | TMessageIndexUid | TMessageIndexUid | 4       | const |    2 |   100.00 | Using where |
    +----+-------------+----------+------------+-------+------------------+------------------+---------+-------+------+----------+-------------+
    2 rows in set, 1 warning (0.00 sec)
    

    【讨论】:

    • 文件排序可以避免吗?
    • ORDER BY 有必要吗?
    • 帖子已更新。更改为 INNER JOIN 是否可以接受? @扎克
    • 谢谢。是不是因为TMessage.id列可能使用LEFT JOIN得到NULL值,所以TMessage.id上的索引不会被使用?
    • 我不太确定原因。正如我猜测的那样,使用 LEFT JOIN 后,行可能与原始左表不同,因此 id 上的原始索引将无法使用。在这种情况下,行号与左表相同是巧合。这取决于连接条件,例如,如果 'ON TUser. id != TMessage.uid' 用作连接条件,行数会有所不同。
    【解决方案2】:

    我也不知道“文件排序”。然后我发现了类似的东西,

    "Anytime a sort can’t be performed from an index, it’s a filesort."
    

    以下是参考链接:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-08
      • 2011-01-24
      • 2015-08-05
      相关资源
      最近更新 更多