在开发业务系统时,分页功能是一个常见需求。然而,如果实现不当,分页查询会严重影响性能,特别是在处理大表时。本文将介绍几种分页查询优化技巧,帮助提升数据库查询效率。
CREATE TABLE `employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
PRIMARY KEY (`id`),
KEY `idx_name_age_position` (`name`, `age`, `position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
假设我们有一个大表 employees
,并需要实现分页功能。常见实现方式如下:
SELECT * FROM employees LIMIT 10000,10;
这条 SQL 的含义是从表 employees
中取出从第 10001 行开始的 10 行记录。然而,MySQL 实际执行时会读取前 10010 条记录,然后丢弃前 10000 条,仅返回后 10 条记录。这种方式在大表中查询效率极低。
如果表的主键是自增且连续的,可以利用主键优化分页查询。例如:
SELECT * FROM employees LIMIT 90000,5;
SELECT * FROM employees WHERE id > 90000 LIMIT 5;
优化后的 SQL 使用主键索引,大大减少了扫描的行数。
这种优化适用于以下情况:
如果主键不连续(如有记录被删除)或查询按照非主键字段排序,则结果可能不一致。
如果需要根据非主键字段排序(例如 name
字段),可以通过分步查询优化:
SELECT * FROM employees ORDER BY name LIMIT 90000,5;
SELECT * FROM employees e
INNER JOIN (
SELECT id FROM employees ORDER BY name LIMIT 90000,5
) ed ON e.id = ed.id;
优化后的 SQL 先通过子查询按 name
字段排序获取主键,再利用主键查询具体记录,避免了直接对全表进行排序。
优化后 SQL 执行时间显著减少,因为子查询利用索引进行排序,而原始 SQL 使用了低效的文件排序(filesort
)。
MySQL 表关联常用两种算法:
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `t2` LIKE t1;
STRAIGHT_JOIN
强制指定连接顺序,例如: SELECT * FROM t2 STRAIGHT_JOIN t1 ON t2.a = t1.a;
COUNT(*)
查询优化统计行数时,常见 SQL 如下:
SELECT COUNT(*) FROM employees;
COUNT(*) ≈ COUNT(1) > COUNT(字段) > COUNT(主键)
COUNT(*) ≈ COUNT(1) > COUNT(主键) > COUNT(字段)
COUNT(*)
是 MySQL 专门优化的高效方式,不会取出字段值,而是按行累加,推荐使用。
在设计表结构时,选择合适的数据类型对性能优化至关重要:
TINYINT
替代 INT
)。NOT NULL
,减少存储和比较开销。DATE
、DATETIME
或 TIMESTAMP
。通过合理优化分页查询和表设计,可以显著提升查询效率,为系统性能提供有力保障。