如何使用TypeScript实现MySQL Aurora数据库查询分页?
实现MySQL Aurora分页的两种方案(TypeScript + Node18 + MySQL2)
一、偏移量分页(LIMIT/OFFSET)
这是最直观的分页方式,适合数据量不大的场景,通过页码和每页条数计算偏移量来获取数据。
实现步骤
- 从API请求的查询参数中获取
page(当前页码,默认1)和pageSize(每页条数,默认10) - 计算偏移量:
offset = (page - 1) * pageSize - 执行两条SQL:一条查询当前页数据,一条查询总记录数,用于返回分页元数据
代码示例
import { APIGatewayProxyHandlerV2 } from 'aws-lambda'; import mysql from 'mysql2/promise'; // 数据库连接配置(建议用环境变量存储敏感信息) const dbConfig = { host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, ssl: { rejectUnauthorized: false } // Aurora需SSL连接 }; export const handler: APIGatewayProxyHandlerV2 = async (event) => { // 解析并校验分页参数 const page = parseInt(event.queryStringParameters?.page || '1', 10); const pageSize = parseInt(event.queryStringParameters?.pageSize || '10', 10); if (page < 1 || pageSize < 1 || pageSize > 100) { // 限制最大每页条数避免性能问题 return { statusCode: 400, body: JSON.stringify({ error: '无效的分页参数' }) }; } const offset = (page - 1) * pageSize; let connection; try { connection = await mysql.createConnection(dbConfig); // 查询当前页数据(以users表为例,根据你的业务表结构调整) const [rows] = await connection.execute( 'SELECT id, name, email FROM users ORDER BY id ASC LIMIT ? OFFSET ?', [pageSize, offset] ); // 查询总记录数,用于计算总页数 const [countResult] = await connection.execute('SELECT COUNT(*) as total FROM users'); const total = (countResult as any)[0].total; const totalPages = Math.ceil(total / pageSize); return { statusCode: 200, body: JSON.stringify({ data: rows, pagination: { currentPage: page, pageSize: pageSize, totalItems: total, totalPages: totalPages } }) }; } catch (error) { console.error('数据库查询失败:', error); return { statusCode: 500, body: JSON.stringify({ error: '服务器内部错误' }) }; } finally { if (connection) { await connection.end(); } } };
二、键集分页(Cursor-Based Pagination)
当数据量很大时,OFFSET会导致数据库扫描大量无关数据,性能下降。键集分页通过上一页最后一条数据的唯一标识(比如id、时间戳)作为游标,避免偏移量的性能问题,适合滚动加载场景。
实现步骤
- 从API请求的查询参数中获取
cursor(上一页最后一条数据的id,首次请求为空)和pageSize - SQL中用
WHERE id > ?(或根据排序字段调整条件)配合LIMIT获取下一页数据 - 返回当前页数据和下一页的游标(当前页最后一条数据的id)
代码示例
import { APIGatewayProxyHandlerV2 } from 'aws-lambda'; import mysql from 'mysql2/promise'; const dbConfig = { host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, ssl: { rejectUnauthorized: false } }; export const handler: APIGatewayProxyHandlerV2 = async (event) => { const cursor = event.queryStringParameters?.cursor; const pageSize = parseInt(event.queryStringParameters?.pageSize || '10', 10); if (pageSize < 1 || pageSize > 100) { return { statusCode: 400, body: JSON.stringify({ error: '无效的分页参数' }) }; } let connection; try { connection = await mysql.createConnection(dbConfig); let query = 'SELECT id, name, email FROM users ORDER BY id ASC LIMIT ?'; let params: any[] = [pageSize]; // 存在游标时,添加WHERE条件过滤数据 if (cursor) { query = 'SELECT id, name, email FROM users WHERE id > ? ORDER BY id ASC LIMIT ?'; params = [parseInt(cursor, 10), pageSize]; } const [rows] = await connection.execute(query, params); const data = rows as any[]; // 生成下一页游标:当前页数据条数等于pageSize时,说明还有后续数据 const nextCursor = data.length === pageSize ? data[data.length - 1].id : null; return { statusCode: 200, body: JSON.stringify({ data: data, pagination: { pageSize: pageSize, nextCursor: nextCursor } }) }; } catch (error) { console.error('数据库查询失败:', error); return { statusCode: 500, body: JSON.stringify({ error: '服务器内部错误' }) }; } finally { if (connection) { await connection.end(); } } };
关键注意事项
- 两种方案的排序字段必须添加索引,否则大数据量下查询性能会急剧下降
- 偏移量分页可能出现数据不一致(分页过程中数据被删除/新增),键集分页不存在这个问题
- 普通后台管理系统用偏移量分页足够,移动端滚动加载或大数据量场景建议用键集分页
内容的提问来源于stack exchange,提问作者Karim Fayed
相关产品推荐
相关产品推荐

