【问题标题】:How to make well indexed MySQL tables join effectively如何使索引良好的 MySQL 表有效连接
【发布时间】:2012-06-15 19:22:08
【问题描述】:

这是第一个表'tbl1':

+---------+---------------------+------+-----+---------+----------------+
| Field   | Type                | Null | Key | Default | Extra          |
+---------+---------------------+------+-----+---------+----------------+
| val     | varchar(45)         | YES  | MUL | NULL    |                |
| id      | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
+---------+---------------------+------+-----+---------+----------------+

及其索引:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| tbl1  |          0 | PRIMARY  |            1 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
| tbl1  |          1 | val      |            1 | val         | A         |     2147085 |     NULL | NULL   | YES  | BTREE      |         |
| tbl1  |          1 | id_val   |            1 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
| tbl1  |          1 | id_val   |            2 | val         | A         |   201826018 |     NULL | NULL   | YES  | BTREE      |         |
| tbl1  |          1 | val_id   |            1 | val         | A         |     2147085 |     NULL | NULL   | YES  | BTREE      |         |
| tbl1  |          1 | val_id   |            2 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

(一些额外索引的原因是:http://bit.ly/KWx1Xz。)

第二张桌子也差不多。以下是它的索引基数:

+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| tbl2   |          0 | PRIMARY  |            1 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
| tbl2   |          1 | val      |            1 | val         | A         |      881336 |     NULL | NULL   | YES  | BTREE      |         |
| tbl2   |          1 | id_val   |            1 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
| tbl2   |          1 | id_val   |            2 | val         | A         |   201826018 |     NULL | NULL   | YES  | BTREE      |         |
| tbl2   |          1 | val_id   |            1 | val         | A         |      881336 |     NULL | NULL   | YES  | BTREE      |         |
| tbl2   |          1 | val_id   |            2 | id          | A         |   201826018 |     NULL | NULL   |      | BTREE      |         |
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

任务是将它们内连接到 val 列并获取 id 的列表(并在 1 秒内完成)。

这是“加入”方法:

SELECT tbl1.id FROM tbl1 JOIN tbl2 ON tbl1.val = 'iii' AND tbl2.val = 'iii' AND tbl1.id = tbl2.id;

结果:集合中有 10831 行(55.15 秒

查询说明:

+----+-------------+--------+--------+----------------------------------+---------+---------+---------------------------+------+--------------------------+
| id | select_type | table  | type   | possible_keys                    | key     | key_len | ref                       | rows | Extra                    |
+----+-------------+--------+--------+----------------------------------+---------+---------+---------------------------+------+--------------------------+
|  1 | SIMPLE      | tbl1   | ref    | PRIMARY,val,id_val,val_id        | val_id  | 138     | const                     | 5160 | Using where; Using index |
|  1 | SIMPLE      | tbl2   | eq_ref | PRIMARY,val,id_val,val_id        | PRIMARY | 8       | search_test.tbl1.id       | 1    | Using where              |
+----+-------------+--------+--------+----------------------------------+---------+---------+---------------------------+------+--------------------------+

这里是'in'方法:

SELECT id FROM tbl1 WHERE val = 'iii' and id IN (SELECT id FROM tbl2 WHERE val = 'iii');

结果:集合中有 10831 行(1 分 10.15 秒

解释:

+----+--------------------+--------+-----------------+---------------------------------+---------+---------+-------+------+--------------------------+
| id | select_type        | table  | type            | possible_keys                   | key     | key_len | ref   | rows | Extra                    |
+----+--------------------+--------+-----------------+---------------------------------+---------+---------+-------+------+--------------------------+
|  1 | PRIMARY            | tbl1   | ref             | val,val_id                      | val_id  | 138     | const | 8553 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | tbl2   | unique_subquery | PRIMARY,val,id_val,val_id       | PRIMARY | 8       | func  |    1 | Using where              |
+----+--------------------+--------+-----------------+---------------------------------+---------+---------+-------+------+--------------------------+

那么,问题来了:如何调整这个查询让 MySQL 在一秒钟内完成它?

【问题讨论】:

  • 多么好的一件艺术品!

标签: mysql performance join indexing


【解决方案1】:
SELECT tbl1.id FROM tbl1 JOIN tbl2 ON tbl1.id = tbl2.id and tbl1.val = tbl2.val
where tbl1.val = 'iii';

【讨论】:

  • 谢谢,很快就会测试!但是你能解释一下我的加入有什么不同吗?
  • shl,与“iii”相比,您浪费了两次时间,这意味着您一遍又一遍地扫描表格。 (在 tbl1 和 tbl2 中找到一行 tbl1.id = tbl2.id,现在检查 tbl1.val='iii',现在检查 tbl2.val = 'iii')。 Srini 的解决方案有效(和我的有效),因为 MySQL 进程是从外向内连接的。所以首先它会在 tbl1 上找到“iii”的所有匹配项(这大大减少了要排序的记录数量),然后比较 PK 并寻找'val' 上的匹配。 IE。对 tbl1 进行表扫描,对 tbl1 进行扫描(减少一次)和 tbl2 以匹配 id 和 val,即总共 3 次扫描。
【解决方案2】:

好的,我已经在每个表的 30,000 多条记录上进行了测试,它运行得非常快。

就目前的情况而言,您目前正在对两个大型表执行联接,但如果您首先在每个表上扫描“val”上的匹配项,这将大大减少联接集的大小。

我最初将这个答案发布为一组子查询,但我没有意识到 MySQL 在嵌套子查询中的速度非常慢,因为它是从外向内执行的。但是,如果您将子查询定义为视图,它会从内向外运行它们.

所以,首先创建视图。

CREATE VIEW tbl1_iii AS (
SELECT * FROM tbl1 WHERE val='iii'
);
CREATE VIEW tbl2_iii AS (
SELECT * FROM tbl2 WHERE val='iii'
);

然后运行查询。

SELECT tbl1_iii.id from tbl1_iii,tbl2_iii
WHERE tbl1_iii.id = tbl2_iii.id;

闪电。

【讨论】:

  • 嵌套查询可能是一个不好的方法。
  • 嗯。我刚才正在阅读这个,似乎有两种方法可以强制 MySQL 首先执行内部查询:1. 将子查询存储为视图或 2. 给整个子查询一个别名(使用 AS)
  • 只有一种方法可以找出答案。测试一下。
猜你喜欢
  • 1970-01-01
  • 2011-07-06
  • 2012-10-03
  • 2018-05-07
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
  • 2012-10-23
相关资源
最近更新 更多