BCP 能达每秒10万+行,关键在绕过日志、禁用约束、匹配格式、规避网络和磁盘瓶颈;漏任一环节速度即降一个数量级。

直接说结论:BCP 能做到每秒 10 万+ 行,但“极速”不靠参数堆砌,而靠绕过日志、禁用约束、匹配格式、规避网络和磁盘瓶颈这四点。漏掉任意一个,速度就掉一个数量级。
为什么 bcp in 导入上亿行还卡在几万行/秒?
常见现象是命令跑着跑着变慢,或一开始快、后面越来越慢。根本原因不是数据量大,而是默认行为在“偷偷做额外事”:
-
bcp默认走完整事务日志记录(Full Recovery 模式下),每行都写 log,I/O 瓶颈立刻暴露 - 目标表有索引、外键、CHECK 约束时,
bcp会逐行校验——哪怕加了-h "CHECK_CONSTRAINTS",它也只跳过约束检查,不跳过触发器和级联操作 - 用
-c(字符模式)导入含中文或特殊字符的字段,若没配-C 65001(UTF-8)或-w(Unicode),会出现乱码或截断,后续重试成本极高 - 源文件放在远程共享路径或 NAS 上,
bcp读取受 SMB 协议延迟拖累,实测比本地 SSD 慢 3–5 倍
bcp in 必加的 4 个关键参数组合
这不是“建议”,是上亿行场景下的硬性配置组合。少一个,性能就断崖下跌:
-
-n:用本机格式(native format),跳过字符串解析和类型转换。比-c快 30%–50%,且不丢精度(比如datetime2微秒级不会被截断) -
-b 10000:批次大小设为 1 万行。太小(如 1000)导致提交太频繁;太大(如 10 万)可能触发内存溢出或锁升级 -
-h "TABLOCK,CHECK_CONSTRAINTS":TABLOCK让整个导入过程只申请表级锁,避免页锁争用;CHECK_CONSTRAINTS显式启用约束检查(注意:不是禁用!RDS SQL Server 等托管服务强制要求开启此项,否则报错) -
-E:保留源数据中的标识列值。如果导出时用了queryout+SELECT IDENTITY_INSERT ON,导入时必须带这个,否则主键冲突
完整命令示例:
bcp MyDB.dbo.Orders in "D:\data\orders_native.bcp" -n -b 10000 -h "TABLOCK,CHECK_CONSTRAINTS" -E -S myserver -U sa -P pass123
导入前必须做的三件事(90% 的人跳过)
BCP 不管表结构,只认数据格式。上亿行一旦出错,重跑代价极大:
- 提前在目标库执行
ALTER DATABASE MyDB SET RECOVERY BULK_LOGGED:让批量操作最小化日志(bulk-logged),大幅降低 log 文件增长和 I/O 压力。导入完记得切回FULL(如需备份) - 导入前删掉非聚集索引,导入完成后再重建。索引维护占总耗时 40%+,且
bcp无法并行建索引 - 确认源文件是用
bcp ... out同一版本、同一参数(尤其是-n)导出的。不同 SQL Server 版本的本机格式不完全兼容(例如 SQL Server 2019 导出的-n文件,SQL Server 2022 可能报Unexpected EOF encountered in BCP data-file)
遇到 Invalid character value for cast specification 怎么办?
这是字符/二进制格式错配最典型的错误,不是数据脏,而是参数没对齐:
- 导出时用了
-c -C 65001(UTF-8 字符文件),导入时却用-n:直接报错,因为-n期待的是二进制流,不是文本 - 导出时用了
-w(Unicode 字符),导入时却用-c:中文字段首字节为0xFF,被当控制字符截断 - 解决办法只有一条:导出和导入严格用同一套格式参数。推荐统一用
-n(本机格式),它不依赖代码页,也不怕换行符歧义,唯一要求是两端 SQL Server 版本相近(差一个主版本号通常可接受)
真正难处理的,是混合了 xml、geography 或稀疏列的表——这些类型在本机格式中需要额外字段对齐,此时必须用 -f format.fmt 配合格式文件,不能靠猜测。

















