MySQL 8.0.32无过滤条件全量排序SELECT查询优化咨询
问题描述
我们的产品已将MySQL服务器从5.7版本升级至8.0.32,现需优化一条无过滤条件的SELECT查询。当前执行的语句为:
SELECT * FROM ds_order ORDER BY entrytime ASC;
表ds_order的结构如下:
CREATE TABLE `ds_order` ( `ordertime` datetime(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000', `itemid` mediumint NOT NULL DEFAULT '0', `vendorid` mediumint NOT NULL DEFAULT '0', `grpid` mediumint NOT NULL DEFAULT '0', `price` double DEFAULT '0', `entrytime` datetime(6) DEFAULT NULL, UNIQUE KEY `uniquestatkey` (`itemid`,`vendorid`,`grpid`), KEY `itemid` (`itemid`), KEY `vendorid` (`vendorid`), KEY `idx_entrytime` (`entrytime`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1
表内共有9546204行数据。受业务限制,无法添加WHERE过滤条件,也不能省略查询字段*。想请教:修改表结构(如分区)或更换存储引擎是否有助于优化?如何高效按entrytime升序获取全量数据?
优化方案
一、优先更换存储引擎为InnoDB
MyISAM在MySQL 8.0中属于被逐步淘汰的引擎,换成InnoDB对这个场景的提升非常明显:
- 利用聚簇索引特性:InnoDB的主键是聚簇索引,叶子节点直接存储整行数据。建议先把
entrytime改为非空(业务上订单肯定有录入时间,可设为NOT NULL DEFAULT CURRENT_TIMESTAMP(6)),然后新增自增id字段,将主键调整为PRIMARY KEY(entrytime, id)(解决entrytime重复的问题)。这样按entrytime升序查询全量数据时,直接扫描聚簇索引就能顺序获取所有行,无需回表,比MyISAM的二级索引查询效率高几倍。 - 更高效的缓存机制:InnoDB的Buffer Pool可以同时缓存索引和数据,全表扫描时缓存命中率更高,减少磁盘IO次数。
- 适配8.0新特性:MySQL 8.0对InnoDB的优化远多于MyISAM,并行查询、自适应哈希索引等特性都能有效提升大表查询性能。
二、分区表对当前场景帮助有限
分区的核心优势是通过WHERE条件过滤掉不需要的分区,但你无法添加过滤条件,全量查询需要扫描所有分区,反而会增加分区管理的额外开销,因此不建议用分区来优化这个场景。
三、临时优化措施(不换引擎的情况下)
如果暂时无法更换存储引擎,可以试试这些临时方案:
- 更新表统计信息:执行
ANALYZE TABLE ds_order;让MySQL获取最新的表数据分布,确保优化器能正确选择idx_entrytime索引。 - 调整排序缓冲区:适当调大
sort_buffer_size参数(注意不要过大避免占用过多内存),这个参数能减少磁盘临时表的使用,但对于900多万行的全量排序,效果有限。
四、分批读取数据(业务允许的话)
如果业务不需要一次性获取全量数据,建议改成分批查询,避免一次排序全表数据:
- 避免使用OFFSET分页(OFFSET越大效率越低),改用基于
entrytime的连续查询:
-- 第一次查询 SELECT * FROM ds_order ORDER BY entrytime ASC LIMIT 1000; -- 后续查询用上一次结果的最后一条entrytime SELECT * FROM ds_order WHERE entrytime > '2024-01-01 00:00:00.000000' ORDER BY entrytime ASC LIMIT 1000;
这种方式能直接利用idx_entrytime索引快速定位,无需全表扫描和大内存排序。
五、小细节优化
- 将
entrytime设为非空:当前entrytime是DEFAULT NULL,索引中会存储NULL值,排序时会有额外开销,改成非空更符合业务逻辑也能提升索引效率。 - 字符集调整:如果业务允许,将
latin1改为utf8mb4,适配更多字符场景,不过对查询速度影响不大。
内容的提问来源于stack exchange,提问作者Dams
相关产品推荐
相关产品推荐

