MySQL 8.0中不推荐用WHILE循环单行插入,因其默认autocommit=1导致每行触发完整事务开销;高效方案是用存储过程封装递归CTE配合批量INSERT…SELECT,需设cte_max_recursion_depth并避免CTE内多次调用NOW()/RAND()。
直接上结论:MySQL 8.0 中不推荐用传统单行循环式存储过程批量造数据,它慢、卡、易超时;真正高效的做法是用存储过程封装递归 CTE + 批量 INSERT … SELECT,或退而求其次——用带分批提交的拼接式 INSERT(如你知识库中那个 batch_insert 过程)。
不是语法错,是执行模型硬伤:
autocommit=1,每条 INSERT 都触发完整事务流程(binlog 写入 + redo log 刷盘 + 索引更新)WHILE 是解释执行,无向量化优化,10 万行常卡在几分钟甚至报 ERROR 1205 (40001): Deadlock found 或超时RAND() 在循环内多次调用时,可能被优化器复用(尤其在 INSERT ... SELECT 场景),导致字段值重复这是 MySQL 8.0+ 唯一既纯 SQL、又接近 LOAD DATA INFILE 性能的方案。关键不在“能不能写”,而在“怎么写不翻车”:
SET SESSION cte_max_recursion_depth = 1000000;(插 100 万行至少要这个值)WITH RECURSIVE 必须紧跟 INSERT,不能拆成两步(否则 CTE 结果集丢失)SELECT 里多次调用 NOW() 或 RAND() —— 它们会被反复求值,但时间戳几乎一样,随机性也难控id 衍生其他字段更稳定,比如:CONCAT('user_', n) 比 CONCAT('user_', FLOOR(RAND()*10000)) 更不易重复示例(插入 50 万行):
DELIMITER //CREATE PROCEDURE insert_bulk_cte(IN cnt BIGINT)BEGINSET SESSION cte_max_recursion_depth = cnt + 10;SET autocommit = 0;INSERT INTO test(id, name) WITH RECURSIVE seq AS (SELECT 1 AS nUNION ALLSELECT n + 1 FROM seq WHERE n
你知识库里的 batch_insert 过程就是典型代表。它不依赖 CTE,靠字符串拼接 + PREPARE 实现批量写入,优势在于:
p_batch_size ≤ 0 时设为 1000,p_total_count ≤ 0 直接报错max_allowed_packet 限制@sql 拼接时若含单引号(如 'test'),必须用两个单引号转义,否则语法错误很多人跑通了就以为万事大吉,但线上压测一跑就崩:
autocommit 就跑循环 → 插 1 万行可能耗时 3 分钟以上RAND() 生成主键或唯一字段 → 极大概率撞 Duplicate entry 导致整个事务回滚NOW() 或 UUID() 作为字段值 → 时间戳全一样 / UUID 生成逻辑受 session 变量影响,不可复现真正稳定的测试数据,核心是「可预测的随机」:用自增序号派生字段,或固定 seed 的 RAND(12345),而不是放任 MySQL 自己“发挥”。