【问题标题】:MySQL JOINing a TABLE to itself without a primary keyMySQL 在没有主键的情况下将 TABLE 加入自身
【发布时间】:2022-01-02 14:23:44
【问题描述】:

我在 mysql 中创建了两个表。每个都包含一个整数 idx 和一个字符串名称。其中,idx 是主键。

CREATE TABLE table_indexed (
    idx     INTEGER,
    name    VARCHAR(24), 
    PRIMARY KEY(idx)
);
CREATE TABLE table_not_indexed (
    idx     INTEGER,
    name    VARCHAR(24)
);

然后我将相同的数据添加到两个表中。 300 万行不同的值到 idx(1-3_000_00,随机排列)和 300 万行 8 个小写字符的随机排列到名称。

然后我运行了一个查询,将每个表连接到自身。没有主键的表运行速度几乎快了 3 倍。

mysql> SELECT COUNT(*)
    -> FROM table_indexed t1 JOIN table_indexed t2
    -> ON t1.idx = t2.idx;
+----------+
| COUNT(*) |
+----------+
|  3000000 |
+----------+
1 row in set (11.80 sec)

mysql> SELECT COUNT(*)
    -> FROM table_not_indexed t1 JOIN table_not_indexed t2
    -> ON t1.idx = t2.idx;
+----------+
| COUNT(*) |
+----------+
|  3000000 |
+----------+
1 row in set (4.12 sec)

编辑:要求 mySQL 解释查询。

mysql> EXPLAIN SELECT COUNT(*)
    -> FROM table_indexed t1 JOIN table_indexed t2
    -> ON t1.idx = t2.idx;
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+---------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref                      | rows    | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+---------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | index  | PRIMARY       | PRIMARY | 4       | NULL                     | 3171970 |   100.00 | Using index |
|  1 | SIMPLE      | t2    | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | index_test3000000.t1.idx |       1 |   100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+--------------------------+---------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT COUNT(*)
    -> FROM table_not_indexed t1 JOIN table_not_indexed t2
    -> ON t1.idx = t2.idx;
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+--------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                                      |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+--------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 2993208 |   100.00 | NULL                                       |
|  1 | SIMPLE      | t2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 2993208 |    10.00 | Using where; Using join buffer (hash join) |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+--------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

mysql>

【问题讨论】:

  • 调查两个查询的 EXPLAIN。我认为第一个查询使用的索引在这种情况下是错误的(100% 选择性)。
  • 这个问题不是专门关于自加入的;它适用于两个不同的表。

标签: mysql join primary-key


【解决方案1】:
  • 在这两种情况下,它都会对 t1 进行表扫描,然后在 t2 中查找匹配的行。
  • 在这种情况下USING INDEX相当于在涉及PK时使用PK。 (EXPLAIN 在这方面有点草率和不一致。)
  • 有时您可以通过EXPLAIN FORMAT=JSON SELECT ... 获取更多详细信息。 (在 这种 的情况下可能没有任何用处。)
  • “rows”只是一个估计值。
  • 非索引案例将 t2 完全读入内存并在其上构建哈希索引。如果join_buffer_size 的值太小,您可以体验另一种选择——重复对 t2 进行全表扫描。
  • 您的实验很好地说明了“连接缓冲区”何时有效,但不如适当的索引。
  • 您的实验结果可能与两个单独的表相同,而不是“自联接”。
  • “3 倍的速度”——对于不同的测试用例,我预计“3”会有很多变化。
  • 有关join_buffer_size、BNL 和 BKA(阻止嵌套循环或批量密钥访问)的更多信息,请参阅https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_join_buffer_size
  • join_buffer_size 设置为大于 RAM 的 1% 可能是不安全的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 2013-08-06
    • 2017-10-21
    • 2020-05-12
    • 2016-12-12
    • 2011-11-03
    • 1970-01-01
    相关资源
    最近更新 更多