必须分批插入并显式事务控制,每1000–5000行COMMIT一次,避免锁表、日志爆炸和超时;禁用自动提交,插入前禁用二级索引,慎用MEMORY表和递归CTE。
直接用 WHILE 循环插入 100 万次,每条 INSERT 都走一次磁盘刷写,实际耗时可能超 30 分钟甚至失败。关键不是“能不能”,而是“怎么避免锁表、日志爆炸和超时”。
START TRANSACTION 包裹整个循环,而不是每次 INSERT 都开事务COMMIT 一次(SET i = i + 1 后加判断),避免 undo log 膨胀导致 ERROR 1205 (HY000): Deadlock found
SET autocommit = 0,否则 COMMIT 无效ALTER TABLE t DISABLE KEYS,插完再 ENABLE KEYS
RAND() 本身没问题,但拼接字符串、生成身份证号或手机号时,5.7 缺少 GENERATED COLUMN 和更严格的类型推断,容易在 CONCAT 中隐式转成科学计数法(比如 13800000000+i 到 100 万时变成 1.38e10)。
LPAD(FLOOR(RAND()*10000000000), 11, '1') 或固定前缀 + FLOOR(RAND()*1000000) 拼接,别直接加整数UUID_SHORT() 替代自增主键做分布式 ID,但注意它不连续,不适合性能压测场景CONCAT('110101', LPAD(FLOOR(RAND()*999999), 6, '0'), '0000', FLOOR(RAND()*10)),跳过校验逻辑但保证长度合法把数据先写进 ENGINE=MEMORY 表再 INSERT INTO ... SELECT 到目标表,确实能提速 3–5 倍,但这个“快”是有代价的。
max_heap_table_size 必须大于预估内存表总大小(行数 × 平均行长),否则插入中途报 ERROR 1114 (HY000): The table is full
SET FOREIGN_KEY_CHECKS = 0
虽然 WITH RECURSIVE 看起来简洁,但它的执行模型是深度优先递归展开,内存占用随层级指数增长。生成 100 万行时,MySQL 常因 ERROR 1643 (HY000): Recursive query aborted after 1000 iterations 中断。
SET cte_max_recursion_depth = 1000000 临时调高,但极易触发 OOM真正卡点不在语法对不对,而在你有没有提前算过:单行平均占多少字节、内存表够不够、binlog 日志会不会撑爆磁盘、以及那条没加 DELIMITER $$ 就执行的存储过程,已经悄悄把你的会话搞崩了。