我在NodeJs中有一种使用Express框架的方法,其中迭代数组并在Mysql DB中进行更新,
该代码接收一个Connection对象和一个Post Body,
发布请求正文是对象的数组,包含要保存在数据库中的数据,
我试图一个接一个地循环对象,并使用更新查询将其保存在数据库中。
现在奇怪的是,该代码仅在立即被调用两次时才有效,
就是在测试中,我发现我必须两次请求API才能使代码保存数据。
我在第一次API调用时遇到以下错误-
Error Code: 1205. Lock wait timeout exceeded; try restarting transaction
这是一个简单的Update调用,我检查了MySql进程,没有死锁,
SHOW FULL PROCESSLIST;
但是在第二个API即时调用中,相同的代码起作用。
let updateByDonationId = async (conn, requestBody, callback) => {
let donations = [];
donations = requestBody.donations;
//for(let i in donations){
async.each(donations, function(singleDonation, callback) {
//let params = donations[i];
let params = singleDonation;
let sqlData = []
let columns = "";
if(params.current_location != null){
sqlData.push(params.current_location);
columns += "`current_location` = ?,";
}
if(params.destination_location != null){
sqlData.push(params.destination_location);
columns += "`destination_location` = ?,";
}
if(columns != ''){
columns = columns.substring(0,columns.length-1);
let sqlQuery = 'UPDATE donation_data SET '+columns
+' WHERE donation_id = "' + params.donation_id + '"';
conn.query(sqlQuery, sqlData, function (err, result) {
logger.info(METHOD_TAG, this.sql);
if (err) {
logger.error(METHOD_TAG, err);
return callback(err, false);
}
})
}
else{
return callback(null, false);
}
columns = "";
sqlData = [];
},
function(err, results) {
if (err) {
logger.error(METHOD_TAG, err);
return callback(err, false);
}
else{
return callback(null, true);
}
});
//return callback(null, true);
} // END
也请参考以下内容,我想他也出于奇怪的原因而收到了ER_LOCK_WAIT_TIMEOUT-
NodeJS + mysql: using connection pool leads to deadlock tables
问题似乎与正确指出的Node的非阻塞异步性质有关
任何人都可以提供正确的代码吗?
答案 0 :(得分:1)
我想说Node.js的异步特性将在这里引起您的问题。您可以尝试重写循环。您可以使用Promise或async.eachSeries方法。
尝试更改循环以使用以下内容:
async.eachSeries(donations, function(singleDonation, callback) {
答案 1 :(得分:0)
.query()
该方法是异步的,因此,您的代码将尝试先执行一个查询,而不必等待前一个查询完成。在数据库方面,如果它们碰巧影响数据库的同一部分,它们只会排队,即,一个查询在数据库的该部分具有“锁定”。现在,其中一项交易必须等待另一项交易完成,如果等待时间长于阈值,则会导致您得到错误。
但是您说您在第二次立即调用中没有收到错误,我的猜测是在第一次调用期间数据已被缓存,因此第二次调用速度更快,并且足够快以使等待时间保持在阈值,因此该错误不是在第二次调用时引起的。
要避免所有这些并仍保持代码的异步特性,可以将Promise与async-await一起使用。
第一步是为我们的.query()
函数创建一个基于Promise的包装函数,如下所示:
let qPromise = async (conn, q, qData) => new Promise((resolve, reject) => {
conn.query(q, qData, function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
现在这是您修改后的函数,它使用了基于Promise的函数和async-await:
let updateByDonationId = async (conn, requestBody, callback) => {
let donations = [];
donations = requestBody.donations;
try {
for (let i in donations) {
let params = donations[i];
let sqlData = [];
let columns = "";
if (params.current_location != null) {
sqlData.push(params.current_location);
columns += "`current_location` = ?,";
}
if (params.destination_location != null) {
sqlData.push(params.destination_location);
columns += "`destination_location` = ?,";
}
if (columns != '') {
columns = columns.substring(0, columns.length - 1);
let sqlQuery = 'UPDATE donation_data SET ' + columns
+ ' WHERE donation_id = "' + params.donation_id + '"';
let result = await qPromise(conn, sqlQuery, sqlData);
logger.info(METHOD_TAG, result); // logging result, you might wanna modify this
}
else {
return callback(null, false);
}
columns = "";
sqlData = [];
}
} catch (e) {
logger.error(METHOD_TAG, e);
return callback(err, false);
}
return callback(null, true);
}
我是动态编写代码的,因此可能存在一些语法错误。