MySQL:连接小到大表的性能不佳

Posted

技术标签:

【中文标题】MySQL:连接小到大表的性能不佳【英文标题】:MySQL: poor performance joining small to large table 【发布时间】:2017-02-27 16:36:58 【问题描述】:

我有以下表格:

CREATE TABLE smalltable (
    smalltable_id VARCHAR(64) NOT NULL,
    bigtable_id VARCHAR(64),
    ...
    PRIMARY KEY (smalltable_id)
) ENGINE=InnoDB;

CREATE TABLE bigtable (
    bigtable_id VARCHAR(64) NOT NULL,
    count BIGINT,
    PRIMARY KEY (bigtable_id)
) ENGINE=InnoDB;

smalltable 大约有 8000 行,bigtable 大约有 4000 万行。我想从smalltable 中检索这些行,其中smalltable.bigtable_id 存在于bigtable 中。以下查询花费了将近 10 个小时来完成:

SELECT * FROM smalltable
INNER JOIN bigtable
ON smalltable.bigtable_id = bigtable.bigtable_id;

这是EXPLAIN的输出:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: smalltable
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 8610
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: bigtable
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 38818260
     filtered: 100.00
        Extra: Using where; Using join buffer (Block Nested Loop)

我不是解释这一点的专家,但看起来 mysql 正在对两个表进行顺序扫描。如果我编写一个小 Python 脚本,它遍历 smalltable 中的所有行并在 bigtable 上为 smalltable 中的每一行执行 SELECT 查询,整个事情在 25 秒内完成。我希望通过单个 SQL 查询获得同样的性能。

【问题讨论】:

在 smalltable (bigtable_id) 上添加索引 谢谢。这确实大大加快了速度。现在查询需要 1 分 30 秒。不过,仍然比手动迭代 smalltable 并触发对 bigtable 的选择查询要慢。此外,最好有一个不需要更改小表的解决方案。在实际用例中,smalltable 中的数据是另一个大表的子集,我没有那个表的写权限。 索引是它所在的位置。另一种方法是做你已经在做的事情。 检查两个表中bigtable_idCOLLATION -- 必须相同。 join_buffer_size的设置是什么?什么版本的 MySQL? 【参考方案1】:

查看优化器路径,哪个表是主源,好像很慢,用bigtable作为主源。试试这个:

SELECT STRAIGHT_JOIN * FROM smalltable
INNER JOIN bigtable
ON smalltable.bigtable_id = bigtable.bigtable_id;

使用 STRAIGHT_JOIN 将告诉 mysql 遵循查询中表的顺序。

【讨论】:

【参考方案2】:

如果您只需要来自smalltable 的信息(这是您的描述所暗示的),

SELECT *
    FROM smalltable AS s
    WHERE EXISTS (
        SELECT *
            FROM bigtable
            WHERE bigtable_id = s.bigtable_id );

【讨论】:

以上是关于MySQL:连接小到大表的性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 大表性能不佳

大表的mysql性能问题

MySQL大表性能优化

Mysql数据库性能优化大总结

mysql中两个大表之间的连接查询

MySQL 性能:在大表中排序很慢,即使过滤的子集很小