MySQL 分页是 Web 开发中非常核心的技术,用于在大量数据中高效地显示指定页的内容。其核心实现方式是使用 LIMIT 和 OFFSET 子句。
一、核心语法:LIMIT 和 OFFSET
MySQL 中实现分页主要依靠 LIMIT 关键字,它有两种用法: 1.LIMIT count: 只返回前 count 条记录。 2.LIMIT offset, count: 从第 offset 条记录开始,返回 count 条记录。 等价于 LIMIT count OFFSET offset (这是更符合标准SQL的写法,推荐使用)。
基本公式
要获取第 page_num 页(页码从 1 开始),每页大小为 page_size 的数据,其计算公式为:
SELECT * FROM table_name [WHERE ...] [ORDER BY ...] -- 分页必须有确定的排序,否则顺序混乱! LIMIT (page_num - 1) * page_size, page_size; -- 或使用标准写法: SELECT * FROM table_name [WHERE ...] [ORDER BY ...] LIMIT page_size OFFSET (page_num - 1) * page_size;
二、具体示例
假设我们有一个 articles 表,要按发布时间倒序进行分页。
示例 1:获取第一页数据,每页 10 条
SELECT id, title, created_time FROM articles ORDER BY created_time DESC -- 必须排序! LIMIT 0, 10; -- 或 SELECT id, title, created_time FROM articles ORDER BY created_time DESC LIMIT 10 OFFSET 0;
示例 2:获取第三页数据,每页 10 条
-- 偏移量 offset = (3 - 1) * 10 = 20 SELECT id, title, created_time FROM articles ORDER BY created_time DESC LIMIT 20, 10; -- 或(标准写法) SELECT id, title, created_time FROM articles ORDER BY created_time DESC LIMIT 10 OFFSET 20;
三、在应用程序中的实现(以 Python 为例)
在 Web 后端(如 Flask, Django)中,你通常会从前端接收 page(当前页)和 size(每页大小)参数。
import pymysql def get_articles(page=1, size=10): """ 分页获取文章列表 :param page: 页码,从1开始 :param size: 每页大小 :return: 文章列表 """ offset = (page - 1) * size计算偏移量 connection = pymysql.connect(host='localhost', user='user', password='pass', database='db') try: with connection.cursor(pymysql.cursors.DictCursor) as cursor: 1. 查询分页数据 sql = """ SELECT id, title, created_time FROM articles ORDER BY created_time DESC LIMIT %s OFFSET %s """ cursor.execute(sql, (size, offset)) data = cursor.fetchall() 2. (重要)查询总记录数,用于计算总页数 count_sql = "SELECT COUNT(*) AS total FROM articles" cursor.execute(count_sql) total_count = cursor.fetchone()['total'] total_pages = (total_count + size - 1) // size向上取整计算总页数 return { 'data': data, 'pagination': { 'current_page': page, 'page_size': size, 'total_count': total_count, 'total_pages': total_pages, 'has_next': page < total_pages, 'has_prev': page > 1 } } finally: connection.close() 调用示例:获取第二页,每页5条 result = get_articles(page=2, size=5) print(result['data']) print(f"总共有 {result['pagination']['total_pages']} 页")
四、性能优化:大数据量下的分页瓶颈及解决方案
问题: 当偏移量 OFFSET 非常大时(如 LIMIT 1000000, 20),MySQL 需要先扫描并跳过前 1000000 条记录,这会导致性能急剧下降。
解决方案:
前提: 排序字段必须具有唯一性(通常是一个自增ID或时间戳)。
-- 传统分页(慢): SELECT * FROM table ORDER BY id DESC LIMIT 1000000, 20; -- 键集分页(快): -- 假设我们已知上一页最后一条记录的 id 是 1020 SELECT * FROM table WHERE id < 1020 -- 基于已知的最后一个ID进行查询 ORDER BY id DESC LIMIT 20;
优点: 速度极快,性能不受页码影响。 缺点: 无法直接跳到任意页码,只能“上一页”或“下一页”。
-- 假设在 (created_time, id, title) 上有一个复合索引 -- 不好的查询:SELECT * FROM articles ... (需要回表获取所有字段) -- 好的查询:SELECT id, title, created_time FROM articles ... (所需字段全在索引里)
五、总结与最佳实践
分页方法 |
适用场景 |
优点 |
缺点 |
LIMIT OFFSET |
传统Web分页,需要跳转到任意页码 |
实现简单,支持随机跳页 |
大数据量时OFFSET过大性能差 |
键集分页 |
移动端无限加载、瀑布流、“加载更多” |
性能极高,不受数据量影响 |
不支持随机跳页,实现稍复杂 |
1.一定要排序:使用 ORDER BY 确保分页顺序是确定的。 2.性能优先:对于深度分页(如第1000页以后),强烈推荐使用键集分页。 3.避免 SELECT *:只查询需要的字段,结合覆盖索引可以极大提升性能。 4.参数化查询:在应用程序中,一定要使用参数化查询来拼接 LIMIT 和 OFFSET,防止SQL 注入。
最终建议: 对于后台管理系统等需要跳页的场景,如果数据量不是特别大,使用传统的 LIMIT OFFSET。 对于C端产品(如App、新闻站)、数据量巨大的表,优先采用键集分页来实现“无限滚动”。
另外搭配便捷的MYSQL备份工具,可定时备份、异地备份,MYSQL导出导入。可本地连接LINUX里的MYSQL,简单便捷。可以大大地提高工作效率喔。