异步MySQL查询并发执行致数据插入不完整问题排查
问题诊断与修复方案
核心问题:全局循环变量导致的异步执行混乱
你的代码里for循环的i没有用let声明,导致它成为全局变量。当连续调用5个initTimeTables时,这5个异步函数会共享同一个i变量——比如第一个函数刚循环到i=50,第二个函数的循环会直接覆盖i的值,最终所有函数的循环逻辑被打乱,自然无法插入完整的168条数据。
修复步骤
1. 修复循环变量作用域
给for循环里的i加上let声明,让每个循环迭代拥有独立的变量:
for(let i=0; i<=167; i++) { // 添加let限制变量作用域 let inputTimeTableName = timeTableName; let inputFieldId = initValuesVar[i].ttfield; let inputFieldValue = initValuesVar[i].ttfieldvalue; await ttupdate(inputTimeTableName, inputFieldId, inputFieldValue); }
2. 替换SQL字符串拼接为参数化查询
当前直接拼接SQL字符串存在严重注入风险,还可能因特殊字符导致执行异常,改用参数化查询:
async function ttupdate(timeTableName, ttFieldId, ttFieldValue) { // 用?作为占位符,避免注入问题 let [result] = await pool.query( 'REPLACE INTO ?? (ttfield , ttfieldvalue , ttname) VALUES (?, ?, ?)', [timeTableName, ttFieldId, ttFieldValue, timeTableName] ); return result } async function initTimeTables(timeTableName) { // 创建表也使用参数化 await pool.query( 'CREATE TABLE IF NOT EXISTS ?? (ttfield VARCHAR(45), ttfieldvalue VARCHAR(45), ttname VARCHAR(45), UNIQUE KEY unique_ttfield (ttfield))', [timeTableName] ); let [tableLength] = await pool.query( 'SELECT ttname FROM ??', [timeTableName] ); if(tableLength.length < 168) { let initValuesVar = await initValues(); for(let i=0; i<=167; i++) { let inputTimeTableName = timeTableName; let inputFieldId = initValuesVar[i].ttfield; let inputFieldValue = initValuesVar[i].ttfieldvalue; await ttupdate(inputTimeTableName, inputFieldId, inputFieldValue); } } else {return;} }
3. 可选:序列化执行多个初始化任务
如果希望逐个执行初始化(降低数据库并发压力),可以用async/await依次调用:
async function initAllTables() { await initTimeTables('always'); await initTimeTables('never'); await initTimeTables('custom1'); await initTimeTables('custom2'); await initTimeTables('custom3'); } initAllTables();
额外优化建议
- 批量插入:当前循环单条插入效率极低,可将168条数据拼成批量REPLACE语句,减少数据库请求次数。
- 错误捕获:添加
try/catch包裹异步逻辑,避免静默失败,方便排查问题。
内容的提问来源于stack exchange,提问作者IoT-Practitioner
相关产品推荐
相关产品推荐

