同时,由于 WHERE 子句的选择性极低,即使使用全表扫描后进行排序(Filesort),排序操作的开销也可能比大量的随机回表 IO 低得多.
我们做一个简单的练习
DROP TABLE IF EXISTS users; CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), gender CHAR(1), -- 'M' 或 'F' age INT, INDEX idx_gender (gender) );
DELIMITER // CREATE PROCEDURE load_users(IN total INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i <= total DO INSERT INTO users(name, gender, age) VALUES ( CONCAT('user_', i), IF(RAND() > 0.5, 'M', 'F'), FLOOR(20 + (RAND() * 30)) ); SET i = i + 1; END WHILE; END // DELIMITER ;
-- 开启 prefer_ordering_index SET optimizer_switch = 'prefer_ordering_index=on'; EXPLAIN SELECT * FROM users ORDER BY gender LIMIT 10; -- 关闭 prefer_ordering_index SET optimizer_switch = 'prefer_ordering_index=off'; EXPLAIN SELECT * FROM users ORDER BY gender LIMIT 10;
mysql> SET optimizer_switch = 'prefer_ordering_index=on'; Query OK, 0 rows affected (0.00 sec) mysql> EXPLAIN SELECT * FROM users ORDER BY gender LIMIT 10; +----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------+ | 1 | SIMPLE | users | NULL | index | NULL | idx_gender | 5 | NULL | 10 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------+ 1 row inset, 1 warning (0.00 sec) mysql> SET optimizer_switch = 'prefer_ordering_index=off'; Query OK, 0 rows affected (0.00 sec) mysql> EXPLAIN SELECT * FROM users ORDER BY gender LIMIT 10; +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------+ | 1 | SIMPLE | users | NULL | ALL | NULL | NULL | NULL | NULL | 997227 | 100.00 | Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+----------------+ 1 row inset, 1 warning (0.00 sec)
-- 开启 prefer_ordering_index SET optimizer_switch = 'prefer_ordering_index=on'; EXPLAIN SELECT gender, COUNT(*) FROM users GROUP BY gender;
-- 关闭 prefer_ordering_index SET optimizer_switch = 'prefer_ordering_index=off'; EXPLAIN SELECT gender, COUNT(*) FROM users GROUP BY gender;
mysql> SET optimizer_switch = 'prefer_ordering_index=on'; Query OK, 0 rows affected (0.00 sec) mysql> EXPLAIN SELECT gender, COUNT(*) FROM users GROUP BY gender; +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | users | NULL | index | idx_gender | idx_gender | 5 | NULL | 997227 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ 1 row inset, 1 warning (0.04 sec) mysql> mysql> -- 关闭 prefer_ordering_index mysql> SET optimizer_switch = 'prefer_ordering_index=off'; Query OK, 0 rows affected (0.00 sec) mysql> EXPLAIN SELECT gender, COUNT(*) FROM users GROUP BY gender; +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | users | NULL | index | idx_gender | idx_gender | 5 | NULL | 997227 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+------------+---------+------+--------+----------+-------------+ 1 row inset, 1 warning (0.00 sec)
写道这里,MySQL推出这个参数本来是好意,但是截至到目前,尚未得到在任何版本,智能化此问题的方案,对于业务和DBA来说,大部分情况选择的是关闭此选项,因为我们不能完全杜绝无可选择性下的数据和索引建立后,在遇到GROUP BY ORDER BY 情况下的错误索引的在这类语句上得使用。