用 Dapper 分批处理大量数据,核心是避免一次性加载全部结果到内存,同时保持数据库连接轻量、SQL 高效。关键在于手动分页查询、流式读取(
QueryAsync<t></t>+
IAsyncEnumerable<t></t>)或配合 SQL 的分页/游标机制,而不是依赖 Dapper 自动“分批”——它本身不提供内置的批量分片能力。
使用 OFFSET-FETCH 或 ROW_NUMBER 实现服务端分页
适合数据有序、可预估总条数的场景(如按 ID 或时间排序)。每次只查一页,减少单次内存占用和网络传输压力。
SQL 示例(SQL Server):SELECT * FROM Orders WHERE Status = @status ORDER BY Id OFFSET @skip ROWS FETCH NEXT @take ROWS ONLY C# 调用时循环执行,每次更新
@skip(如 0, 1000, 2000…),
@take建议 500–5000,视单行大小和网络延迟调整 注意:OFFSET 越大性能越差,超 10 万行建议改用游标(keyset pagination),例如
WHERE Id > @lastId ORDER BY Id LIMIT @take
用 IAsyncEnumerable 流式读取(.NET 5+)
避免把整张表拉进 List
await foreach (var order in connection.QueryAsync
批量写入:用 ExecuteAsync + 参数化 IN 或临时表
Dapper 不直接支持批量 INSERT,需自己拼 SQL 或借助扩展(如 Dapper.FastCRUD);但安全高效的做法仍是分组 + 参数化。
小批量(≤ 1000 条):拼INSERT INTO ... VALUES (@p0), (@p1), ...,参数用
DynamicParameters批量添加 大批量(> 1000):优先走 SQL Server 的
SqlBulkCopy(需 DataTable 或 IDataReader),或 PostgreSQL 的
COPY;Dapper 只负责触发 避免用循环单条
ExecuteAsync,哪怕加了事务,网络往返开销也极大
结合事务与批次控制防中断
长耗时批处理必须考虑失败恢复,不能让整个流程因一条脏数据失败而重来。
每批(如 1000 条)单独开事务,成功则提交,失败则记录错误 ID 并跳过,继续下一批 维护一个轻量 checkpoint 表(如BatchLog),记录已处理的最大 ID 或时间戳,程序重启后从断点续跑 慎用大事务:SQL Server 中长时间事务会阻塞日志截断,可能撑爆日志文件
基本上就这些。Dapper 是轻量 ORM,分批不是它该干的事,而是你用它写出更可控的 SQL 和执行逻辑。重点在设计好分片策略、选对读写模式、管住连接和事务生命周期。
