【问题标题】:How do I avoid a full table scan with this basic inner join?如何避免使用此基本内部联接进行全表扫描?
【发布时间】:2009-12-23 12:32:32
【问题描述】:

我有一个表,它的外键指向存储一些 blob 数据的表。当我对具有主表条件的表进行内部联接时,联接类型从“索引”变为“全部”。我想避免这种情况,因为我的 blob 表大约为数十 GB。我怎样才能避免它?

这是基本的内连接:

EXPLAIN SELECT m.id, b.id, b.data 
        FROM metadata m, blobstore b 
        WHERE m.fkBlob = b.id;

1, 'SIMPLE', 'm', 'index', 'fk_blob', 'fk_blob', '4', '', 1, 'Using index'
1, 'SIMPLE', 'b', 'eq_ref', 'PRIMARY', 'PRIMARY', '4', 'blob_index.m.fkBlob', 1, ''

这里我在主表上添加一个条件:

EXPLAIN SELECT m.id, b.id, b.data 
        FROM metadata m, blobstore b 
        WHERE m.fkBlob = b.id AND m.start < '2009-01-01';
1, 'SIMPLE', 'b', 'ALL', 'PRIMARY', '', '', '', 1, ''
1, 'SIMPLE', 'm', 'ref', 'fk_blob,index_start', 'fk_blob', '4', 'blob_index.b.id', 1, 'Using where'

请注意,列出表格的顺序已更改。由于我添加了关于主表的条件,它现在正在对 blob 表进行全表扫描。

这是架构:

 DROP TABLE IF EXISTS `blob_index`.`metadata`;
    CREATE TABLE  `blob_index`.`metadata` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `fkBlob` int(10) unsigned NOT NULL,
      `start` datetime NOT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_blob` (`fkBlob`),
      KEY `index_start` (`start`),
      CONSTRAINT `fk_blob` FOREIGN KEY (`fkBlob`) REFERENCES `blobstore` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;


    DROP TABLE IF EXISTS `blob_index`.`blobstore`;
    CREATE TABLE  `blob_index`.`blobstore` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `data` mediumblob NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

【问题讨论】:

    标签: mysql query-optimization blob inner-join


    【解决方案1】:

    我猜你是在空表上尝试这个(因为 MySQL 认为它需要通过一行来进行全表扫描),这可能会影响调度程序的结果。当您在真实桌子上执行此操作时,EXPLAIN 结果可能会有所不同(实际上在我的测试中确实有所不同)。

    【讨论】:

    • 你说得对,我往里面扔了一些数据,对两个表都运行了分析表,现在第一个联接是元表上的范围类型。
    【解决方案2】:

    优化器认为您的查询将受益于交换表顺序(这很可能意味着统计信息不是最新的)。

    您可以尝试在metadata (start, fkBlob)上添加索引:

    CREATE INDEX ix_metadata_start_blob ON metadata (start, fkBlob)
    

    并在两个表上运行ANALYZE TABLE

    这样,start 上的索引将用于过滤 metadata,这将成为前导。

    您也可以显式强制连接的顺序:

    SELECT  *
    FROM    metadata m
    STRAIGHT_JOIN
            blobstore b
    ON      b.id = m.fkBlob
    WHERE   m.start <= '2009-01-01'
    

    ,尽管通常不建议这样做。

    【讨论】:

      【解决方案3】:

      如果我正确阅读了您发布的内容,它会从 indexrefeq_refall

      CREATE INDEX idx_metadata USING BTREE ON `metadata` (fkBlob,start);
      

      应该马上拿回来。

      【讨论】:

        【解决方案4】:
        if the index doesnot take it right use HINTS
        
        select /* INDEX <index_name> */
        blah blah blah
        from ........
        

        【讨论】:

          【解决方案5】:

          在第一个示例中,MySQL 使用元数据 fk_blob 索引,因为它是一个覆盖索引——您在查询中使用的每一列都存在于索引中。 (这就是“使用索引”的意思。)该查询仍然进行了全面扫描,但它通过辅助索引而不是主索引扫描每一行。一旦你使用 start,你就丢失了覆盖索引,MySQL 计算出使用 blobstore 作为驱动索引会更快。 (InnoDB 的主索引与行存储集成在一起。)

          如果您希望 MySQL 继续使用元数据索引作为驱动索引,请确保其上有一个对查询有用的索引。 (start, fkBlob) 上的索引最适合第二个查询,但这对其他查询可能没有用。下一个最佳索引是将 (fkBlob) 替换为 (fkBlob, start)。您必须在索引过多(维护成本高)与高效查询计划之间取得平衡。测试、测试、测试——永远不要盲目相信你的开发数据库上的解释。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-05-15
            • 2015-01-12
            • 1970-01-01
            • 2021-12-08
            • 2021-11-04
            • 1970-01-01
            相关资源
            最近更新 更多