MySQL:有没有办法优化具有以下数据库结构的多表ORDER BY?

问题描述 投票:0回答:1

我有两个表,其结构和索引如下(出于目的,实名已更改):

mysql> describe table1;
+---------+---------------------+------+-----+-------------------+----------------+
| Field   | Type                | Null | Key | Default           | Extra          |
+---------+---------------------+------+-----+-------------------+----------------+
| id      | bigint(20) unsigned | NO   | PRI | NULL              | auto_increment |
| field1  | varchar(255)        | NO   | UNI | NULL              |                |
| date    | timestamp           | NO   | MUL | CURRENT_TIMESTAMP |                |
| text    | varchar(10000)      | NO   |     | NULL              |                |
| flag    | tinyint(1)          | YES  |     | 0                 |                |
+---------+---------------------+------+-----+-------------------+----------------+

mysql> show indexes from 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         |     1420047 |     NULL | NULL   |      | BTREE      |         |               |
| table1 |          0 | table1_field1_unique |            1 | field1      | A         |     1420047 |     NULL | NULL   |      | BTREE      |         |               |
| table1 |          1 | table1_date_idx      |            1 | date        | A         |     1420047 |     NULL | NULL   |      | BTREE      |         |               |
+--------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

mysql> describe table2;
+------------------------+---------------------+------+-----+---------+----------------+
| Field                   | Type                | Null | Key | Default | Extra          |
+------------------------+---------------------+------+-----+---------+----------------+
| id                      | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| table1_id               | bigint(20) unsigned | NO   | MUL | NULL    |                |
| some1_id                | bigint(20) unsigned | YES  | MUL | NULL    |                |
| some1_name              | varchar(255)        | YES  | MUL | NULL    |                |
| some2_id                | bigint(20) unsigned | NO   | MUL | NULL    |                |
| some2_name              | varchar(255)        | NO   | MUL | NULL    |                |
| some3_name              | varchar(255)        | YES  | MUL | NULL    |                |
| some4_email             | varchar(255)        | YES  |     | NULL    |                |
| some4_name              | varchar(255)        | YES  | MUL | NULL    |                |
| some4_place1_gift       | varchar(255)        | YES  |     | NULL    |                |
| some4_place2_gift       | varchar(255)        | YES  |     | NULL    |                |
| some4_place3_gift       | varchar(255)        | YES  |     | NULL    |                |
+------------------------+---------------------+------+-----+---------+----------------+

mysql> show indexes from 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         |      462911 |     NULL | NULL   |      | BTREE      |         |               |
| table2|          1 | table2_table1_table1_id_foreign |            1 | table1_id  | A         |      462911 |     NULL | NULL   |      | BTREE      |         |               |
| table2|          1 | some4_name_idx                  |            1 | some4_name | A         |        5645 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2|          1 | some3_name_idx                  |            1 | some3_name | A         |        3560 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2|          1 | some2_id_idx                    |            1 | some2_id   | A         |         116 |     NULL | NULL   |      | BTREE      |         |               |
| table2|          1 | some1_id_idx                    |            1 | some1_id   | A         |         390 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2|          1 | some1_name_idx                  |            1 | some1_name | A         |        1727 |     NULL | NULL   | YES  | BTREE      |         |               |
| table2|          1 | some2_name_idx                  |            1 | some2_name | A         |         221 |     NULL | NULL   |      | BTREE      |         |               |
+--------------+------------+--------------------------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我有以下疑问:

SELECT
    table1.id AS table1_id,
    table1.field1,
    table1.date,
    table1.text,
    table2.id AS table2_id,
    table2.some1_id,
    table2.some1_name,
    table2.some2_id,
    table2.some2_name,
    table2.some3_name,
    table2.some4_email,
    table2.some4_name,
    table2.some4_place1_gift,
    table2.some4_place2_gift,
    table2.some4_place3_gift
FROM
    table2
INNER JOIN
    table1 ON table2.table1_id = table1.id
WHERE
    table2.some1_name = 'Some1_Name_Example'
    AND table2.some2_name = 'Some2_Name_Example'
    AND table2.some3_name = 'Some3_Name_Example'
    AND (
        table2.some2_id IN (1, 22, 975, 5981, 6127, 10861)
        OR table2.some1_id IN (2564, 4886, 12514, 12724, 13905, 15491, 16295, 18125, 22162, 20702, 20704, 21503)
        OR (table2.some2_id = 1277 AND table1.date < '2022-03-27 00:00:00')
        OR (table2.some2_id = 1678 AND table1.date < '2023-06-16 00:00:00')
        OR (table2.some2_id = 9519 AND table1.date < '2021-01-05 00:00:00')
        OR (table2.some1_id = 4648 AND table1.date < '2023-06-16 00:00:00')
    )
ORDER BY
    table1.date DESC,
    table2.id DESC
LIMIT 200;

查询时间:1.04秒。这是此查询的 EXPLAIN 语句:

+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+----------------------------+--------+---------------------------------------------------------------------+
| id | select_type | table        | type   | possible_keys                                                                                       | key             | key_len | ref                  | rows   | Extra                                                               |
+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+----------------------------+--------+---------------------------------------------------------------------+
|  1 | SIMPLE      | table2       | ref    | table2_table1_id_foreign,some3_name_idx,some2_id_idx,some1_id_idx,some1_name_index,some2_name_index | some3_name_idx  | 768     | const                | 231455 | Using index condition; Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | events       | eq_ref | PRIMARY,table1_date_idx                                                                             | PRIMARY         | 8       | db.table2.table1_id  |      1 | Using where                                                         |
+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+----------------------------+--------+---------------------------------------------------------------------+

然后我删除

ORDER BY table1.date DESC
子句(只留下
ORDER BY table2.id DESC
)。

查询时间:0.12秒。解释声明:

+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+--
| id | select_type | table        | type   | possible_keys                                                                                       | key             | key_len | ref                  | rows   | Extra        |
+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+--
|  1 | SIMPLE      | table2       | ref    | table2_table1_id_foreign,some3_name_idx,some2_id_idx,some1_id_idx,some1_name_index,some2_name_index | some3_name_idx  | 768     | const                | 231455 | Using where  |
|  1 | SIMPLE      | events       | eq_ref | PRIMARY,table1_date_idx                                                                             | PRIMARY         | 8       | db.table2.table1_id  |      1 | Using where  |
+----+-------------+--------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+---------+---

看起来

ORDER BY table1.date DESC
在查询执行中产生了大量开销。我不确定在这种情况下是否可以采取任何措施来优化查询。有什么建议吗?

附注我知道

WHERE
子句看起来很奇怪,但我的主要问题是
ORDER BY
优化。

mysql query-optimization sql-order-by database-indexes
1个回答
0
投票

此场景存在两个性能问题。

  1. SELECT whole-mess-of-rows ... ORDER BY something LIMIT small-number
    是众所周知的性能反模式。为了满足查询,数据库必须获取大量数据,按特定顺序对其进行排序,然后丢弃除一小部分之外的所有数据。有时可以使用索引来避免排序任务。

    这就是为什么省略 ORDER BY 子句会使查询更快。它只返回前 200 行,以先生成的行为准。但这些行不会很有用,因为它们的顺序是不可预测的。您不能合理地省略 ORDER BY。

  2. 覆盖索引将帮助 WHERE 过滤此查询。试试这个索引:

    CREATE INDEX names ON table2
       (some1_name, some2_name, some_3_name, some2_id, some1_id)
    

    它应该会让事情变得更快一些。

  3. 如果您仍然在性能方面遇到困难,您可以尝试所谓的延迟加入。首先使用子查询来获取所需行的 id 值。像这样。

     SELECT
         table1.id AS table1_id,
         table2.id AS table2_id
     FROM
         table2
     INNER JOIN
         table1 ON table2.table1_id = table1.id
     WHERE
         table2.some1_name = 'Some1_Name_Example'
         AND table2.some2_name = 'Some2_Name_Example'
         AND table2.some3_name = 'Some3_Name_Example'
         AND (
             table2.some2_id IN (1, 22, 975, 5981, 6127, 10861)
             OR table2.some1_id IN (2564, 4886, 12514, 12724, 13905, 15491, 16295, 18125, 22162, 20702, 20704, 21503)
             OR (table2.some2_id = 1277 AND table1.date < '2022-03-27 00:00:00')
             OR (table2.some2_id = 1678 AND table1.date < '2023-06-16 00:00:00')
             OR (table2.some2_id = 9519 AND table1.date < '2021-01-05 00:00:00')
             OR (table2.some1_id = 4648 AND table1.date < '2023-06-16 00:00:00')
         )
     ORDER BY
         table1.date DESC,
         table2.id DESC
     LIMIT 200;
    

    这与原始查询具有相同的排序然后丢弃问题,但排序后的每一行要小得多——只有几个

    id
    值和一个“日期”。

    然后将其加入到您的表格中以获取详细信息(我没有对此进行调试)。

     SELECT
         table1.id AS table1_id,
         table1.field1,
         table1.date,
         table1.text,
         table2.id AS table2_id,
         table2.some1_id,
         table2.some1_name,
         table2.some2_id,
         table2.some2_name,
         table2.some3_name,
         table2.some4_email,
         table2.some4_name,
         table2.some4_place1_gift,
         table2.some4_place2_gift,
         table2.some4_place3_gift
         FROM (
             SELECT
                 table1.id AS table1_id,
                 table2.id AS table2_id
             FROM
                 table2
             INNER JOIN
                 table1 ON table2.table1_id = table1.id
             WHERE
                 table2.some1_name = 'Some1_Name_Example'
                 AND table2.some2_name = 'Some2_Name_Example'
                 AND table2.some3_name = 'Some3_Name_Example'
                 AND (
                     table2.some2_id IN (1, 22, 975, 5981, 6127, 10861)
                     OR table2.some1_id IN (2564, 4886, 12514, 12724, 13905, 15491, 16295, 18125, 22162, 20702, 20704, 21503)
                     OR (table2.some2_id = 1277 AND table1.date < '2022-03-27 00:00:00')
                     OR (table2.some2_id = 1678 AND table1.date < '2023-06-16 00:00:00')
                     OR (table2.some2_id = 9519 AND table1.date < '2021-01-05 00:00:00')
                     OR (table2.some1_id = 4648 AND table1.date < '2023-06-16 00:00:00')
                 )
             ORDER BY
                 table1.date DESC,
                 table2.id DESC
             LIMIT 200
          ) ids
     INNER JOIN table1 ON ids.table1_id = table1.id
     INNER JOIN table2 ON ids.table2_id = table2.id AND table2.table1_id = table1.id
     ORDER BY     
        table1.date DESC,
        table2.id DESC
    
© www.soinside.com 2019 - 2024. All rights reserved.