文件排序而不是索引扫描

Posted

技术标签:

【中文标题】文件排序而不是索引扫描【英文标题】:Filesort instead of index scan 【发布时间】:2020-11-23 15:18:35 【问题描述】:

我将我的 php/mysql 数据库从 Unix 迁移到 Windows。我转储了数据库并将其直接导入到 Windows 上,没有任何区别。我使用相同的脚本/PHP 版本。

我的桌子是:

CREATE TABLE `pximg` (
  `ppoc` tinyint unsigned NOT NULL,
  `file` int unsigned NOT NULL,
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `img` mediumblob,
  PRIMARY KEY (`ppoc`,`file`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

我要重现的 PHP 脚本:

// SQL queries
$queryNoBindings = "EXPLAIN SELECT img, ts FROM pximg WHERE ppoc = 0 ORDER BY `file` DESC LIMIT 1";
$queryBindings   = "EXPLAIN SELECT img, ts FROM pximg WHERE ppoc = ? ORDER BY `file` DESC LIMIT 1";
$ppoc = 0;

// connect
$m = new mysqli('127.0.0.1', $dbData['px'][1], $dbData['px'][2], $dbData['px'][3], $dbData['px'][4]);

// query with no bindings
$q = $m->query($queryNoBindings);
$r1 = $q->fetch_array(MYSQLI_ASSOC);
print_r($r1); 

// query with bindings
$stmt = $m->prepare($queryBindings);
$stmt->bind_param('i', $ppoc);                
$stmt->execute();
$r = $stmt->get_result();
$r2 = $r->fetch_array(MYSQLI_ASSOC);
print_r($r2); 

Unix 上的结果是

Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => pximg
    [partitions] => 
    [type] => ref
    [possible_keys] => PRIMARY
    [key] => PRIMARY
    [key_len] => 1
    [ref] => const
    [rows] => 385758
    [filtered] => 100.00
    [Extra] => Backward index scan
)
Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => pximg
    [partitions] => 
    [type] => ref
    [possible_keys] => PRIMARY
    [key] => PRIMARY
    [key_len] => 1
    [ref] => const
    [rows] => 385758
    [filtered] => 100
    [Extra] => Backward index scan
)

Windows 上的结果是

Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => pximg
    [partitions] => 
    [type] => ref
    [possible_keys] => PRIMARY
    [key] => PRIMARY
    [key_len] => 1
    [ref] => const
    [rows] => 370682
    [filtered] => 100.00
    [Extra] => Backward index scan
)
Array
(
    [id] => 1
    [select_type] => SIMPLE
    [table] => pximg
    [partitions] => 
    [type] => ref
    [possible_keys] => PRIMARY
    [key] => PRIMARY
    [key_len] => 1
    [ref] => const
    [rows] => 370682
    [filtered] => 100
    [Extra] => Using filesort
)

所以区别在于:当我在 Windows 上使用带有绑定的查询时,它现在使用的是文件排序而不是反向索引扫描,并且需要大约 20 秒而不是 0.02 秒。

有人知道为什么吗? 或者,如果文件排序没问题,为什么现在查询速度如此之慢?

【问题讨论】:

相同版本的 MySQL?案例请提供EXPLAIN FORMAT=JSON SELECT。 “成本”可能略有不同。另请提供SHOW INDEXES FROM ..——基数估计可能存在差异。 【参考方案1】:

您新加载的服务器似乎没有使用您的主键。

这里有一些事情可以尝试:

    new 服务器上的

    SHOW TABLE piximg; 向您显示批量加载表的定义。主键定义是否进入您的新表?如果没有,请执行此操作。

     ALTER TABLE piximg ADD PRIMARY KEY(`ppoc`, `file`);
    

    ALTER TABLE pximg ENABLE KEYS; 表示您的批量加载操作禁用了密钥处理。

    OPTIMIZE TABLE pximg; 因为有时新批量加载的表的索引统计信息在优化之前没有意义。

    针对此查询的具体情况,稍微更改索引定义,在file 列中包含DESC 限定符。

     PRIMARY KEY (`ppoc`, `file` DESC)
    

    模式SELECT blah, blah, blob ... ORDER BY blah DESC LIMIT 1 是一个臭名昭著的性能反模式。你可能想试试这个。

SELECT a.img, a.ts 
  FROM pximg a
  JOIN (
              SELECT ppoc, MAX(file) file
                FROM piximg
               GROUP BY ppoc
       ) b ON a.ppoc = b.ppoc AND a.file = b.file
 WHERE ppoc = <<<whatever>>>

这将避免让 MySQL 服务器对包含大块 Blob 的大量行进行排序,只丢弃除一个之外的所有行。

【讨论】:

非常感谢您的帮助! 1.) SHOW TABLES 显示 [Index_length] => 0 ... 2) 和 3) 到目前为止没有帮助。 5)查询确实如此,结果是“Using where;Using index”,它的速度和预期的一样快——非常感谢!但我仍在调查为什么原始查询(带有绑定)没有使用主键...

以上是关于文件排序而不是索引扫描的主要内容,如果未能解决你的问题,请参考以下文章

为啥这是索引扫描而不是索引查找

为啥当 WHERE 子句包含参数化值时 SQL Server 使用索引扫描而不是索引查找

Postgres:强制分析器使用位图扫描而不是索引扫描

索引扫描而不是搜索

Spring JPA 查询始终使用序列扫描而不是索引扫描

SQL索引