压测或功能测试需要 10 万条数据,第一反应是写存储过程循环插入。但存储过程有权限要求、调试困难、移植性差,而且很多公司 DBA 不给开发开存储过程权限。

这篇文章给出三种不依赖存储过程的批量造数方案,从快到慢、从简单到灵活,按场景选用。

方案 1:INSERT INTO ... SELECT 笛卡尔积(最快)

利用已有表的数据做笛卡尔积,一条 SQL 造出百万级数据。

-- 假设已有一张 1000 行的基础数据表 user_base
-- 需要 10 万条订单数据:1000 用户 × 100 订单 = 10 万

INSERT INTO order_table (user_id, order_no, amount, status, create_time)
SELECT
    u.id,
    CONCAT('ORD', LPAD(u.id * 1000 + t.n, 10, '0')),
    ROUND(RAND() * 1000, 2),
    FLOOR(RAND() * 3),
    DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)
FROM user_base u
CROSS JOIN (
    SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
    UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
    UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
    UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20
    UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24 UNION ALL SELECT 25
    UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29 UNION ALL SELECT 30
    UNION ALL SELECT 31 UNION ALL SELECT 32 UNION ALL SELECT 33 UNION ALL SELECT 34 UNION ALL SELECT 35
    UNION ALL SELECT 36 UNION ALL SELECT 37 UNION ALL SELECT 38 UNION ALL SELECT 39 UNION ALL SELECT 40
    UNION ALL SELECT 41 UNION ALL SELECT 42 UNION ALL SELECT 43 UNION ALL SELECT 44 UNION ALL SELECT 45
    UNION ALL SELECT 46 UNION ALL SELECT 47 UNION ALL SELECT 48 UNION ALL SELECT 49 UNION ALL SELECT 50
    UNION ALL SELECT 51 UNION ALL SELECT 52 UNION ALL SELECT 53 UNION ALL SELECT 54 UNION ALL SELECT 55
    UNION ALL SELECT 56 UNION ALL SELECT 57 UNION ALL SELECT 58 UNION ALL SELECT 59 UNION ALL SELECT 60
    UNION ALL SELECT 61 UNION ALL SELECT 62 UNION ALL SELECT 63 UNION ALL SELECT 64 UNION ALL SELECT 65
    UNION ALL SELECT 66 UNION ALL SELECT 67 UNION ALL SELECT 68 UNION ALL SELECT 69 UNION ALL SELECT 70
    UNION ALL SELECT 71 UNION ALL SELECT 72 UNION ALL SELECT 73 UNION ALL SELECT 74 UNION ALL SELECT 75
    UNION ALL SELECT 76 UNION ALL SELECT 77 UNION ALL SELECT 78 UNION ALL SELECT 79 UNION ALL SELECT 80
    UNION ALL SELECT 81 UNION ALL SELECT 82 UNION ALL SELECT 83 UNION ALL SELECT 84 UNION ALL SELECT 85
    UNION ALL SELECT 86 UNION ALL SELECT 87 UNION ALL SELECT 88 UNION ALL SELECT 89 UNION ALL SELECT 90
    UNION ALL SELECT 91 UNION ALL SELECT 92 UNION ALL SELECT 93 UNION ALL SELECT 94 UNION ALL SELECT 95
    UNION ALL SELECT 96 UNION ALL SELECT 97 UNION ALL SELECT 98 UNION ALL SELECT 99 UNION ALL SELECT 100
) t;

优点:

  • 一条 SQL 完成,速度极快(10 万条几秒)
  • 不依赖存储过程,任何有 INSERT 权限的账号都能跑
  • 数据有真实分布(基于已有 user_base)

缺点:

  • 需要已有基础数据表做笛卡尔积
  • 数据随机性靠 RAND(),字段语义靠 CONCAT/函数拼接,不够灵活

适用场景:压测、性能测试,对数据语义要求不高的场景。

方案 2:MySQL 8 递归 CTE(无需基础表)

MySQL 8.0+ 支持递归公共表表达式(CTE),可以无中生有生成序列:

-- 生成 10 万条用户数据
INSERT INTO user (id, name, email, age, create_time)
WITH RECURSIVE seq AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM seq WHERE n < 100000
)
SELECT
    n,
    CONCAT('用户', n),
    CONCAT('user', n, '@example.com'),
    FLOOR(18 + RAND() * 42),
    DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)
FROM seq;

优点:

  • 不需要已有数据表,无中生有
  • 可以精确控制生成条数
  • 语法相对直观

缺点:

  • MySQL 8.0+ 才支持,老版本(5.7 及以下)用不了
  • 递归深度默认有限制(cte_max_recursion_depth),10 万条需要调大参数
  • 字段语义还是靠函数拼接

调大递归深度:

SET SESSION cte_max_recursion_depth = 100000;

适用场景:MySQL 8.0+,需要精确控制数据量和分布的场景。

方案 3:工具生成 SQL 文件(最灵活)

对于字段语义要求高、需要关联多张表的场景,用 Mock 测试数据生成器 生成 SQL 文件,再导入数据库。

操作步骤:

  1. 粘贴建表语句到工具,工具自动解析字段结构
  2. 设置生成行数(如 10 万条)
  3. 工具按字段类型生成语义化数据:
    • VARCHAR 按字段名生成:name 生成随机姓名,email 生成随机邮箱
    • INT 按范围生成:age 生成 18-60 随机整数
    • DATETIME 按时间范围生成:近一年内随机时间
    • ENUM 按枚举值随机选择
  4. 下载生成的 SQL 文件,用 mysql 命令或客户端导入
mysql -u root -p your_db < mock_data_100k.sql

优点:

  • 字段语义化程度高:name 是真实姓名风格,email 是真实邮箱格式
  • 单表生成快,多张表分别生成后用外键字段手动对齐(如统一用 user_id 范围 1-1000)
  • 不依赖数据库版本,任何支持 SQL 导入的环境都能用
  • 生成的 SQL 文件可以版本控制, repeatable

缺点:

  • 当前版本只支持单表,多表关联需分别生成后自行对齐外键值
  • 需要额外工具步骤
  • 生成 10 万条数据的 SQL 文件较大(几十 MB),导入需要一定时间

适用场景:功能测试、验收测试,对数据语义和关联关系要求高的场景。

三种方案对比

维度 INSERT SELECT 笛卡尔积 递归 CTE 工具生成 SQL
速度 极快(秒级) 快(秒级) 较慢(分钟级)
数据库版本 全版本支持 MySQL 8.0+ 全版本支持
基础数据依赖 需要已有表 不需要 不需要
字段语义 靠函数拼接,一般 靠函数拼接,一般 语义化,高
多表关联 手动 JOIN 手动 JOIN 不支持(需分别生成后手动对齐外键)
数据可重复性 每次随机 每次随机 SQL 文件固定
权限要求 INSERT 权限 INSERT + CTE 权限 INSERT 权限

常见坑

  • 「递归 CTE 报递归深度超限」:ERROR 3636 (HY000): Recursive query aborted after 1001 iterations,调大 cte_max_recursion_depth。
  • 「INSERT SELECT 锁表」:大表 INSERT SELECT 会锁表,在业务低峰跑,或分批插入(LIMIT + 循环)。
  • 「生成的数据有重复主键」:INSERT SELECT 时没加唯一约束,或 CONCAT 生成的 order_no 重复。加唯一索引或确保生成逻辑唯一。
  • 「SQL 文件太大导入失败」:10 万条数据的 SQL 文件可能几十 MB,用 mysql 命令行导入比客户端稳定,或拆成多个小文件分批导入。

相关工具与速查

  • Mock 测试数据生成器:粘贴建表语句生成语义化测试数据,支持 MySQL / PostgreSQL / Oracle / SQL Server / SQLite 五种方言,单表最多 500 行;多表场景分别生成后按外键范围对齐
  • SQL 通用语法速查:INSERT、SELECT、JOIN、子查询等基础语法
  • MySQL 命令速查:批量导入、索引优化、事务控制等