場景:應用剛上線排除大批量請求的問題線上多次出現的Deadlock found when trying to get lock錯誤代碼:async batchUpdate(skus, { transaction }) { const result = await Promise.all(skus.m... ...
場景:
- 應用剛上線排除大批量請求的問題
- 線上多次出現的Deadlock found when trying to get lock錯誤
代碼:
async batchUpdate(skus, { transaction }) {
const result = await Promise.all(skus.map(async sku => {
const record = await this.app.model.Sku.upsert(sku, { transaction });
return record;
}));
// SaaS 中刪掉的 sku,插件也要同步刪除
const ids = _.map(skus, 'sku_id');
const productIds = _.map(skus, 'product_id');
const { Op } = this.app.Sequelize;
await this.app.model.Sku.destroy({
where: {
sku_id: { [Op.notIn]: ids },
product_id: productIds,
}, transaction,
});
return result;
};
分析:
- 報錯位置都是在this.app.model.Sku.destroy的時候報錯
- Deadlock found when trying to get lock的原因是多個事物同事更新插入同一表的某一段數據
- 在數據量不大的情況下,按道理說發生這種死鎖的情況應該非常少但是事實上出現的概率很高
結論:
- 應該是destroy使用notIn會涉及到很多行的鎖定,所以造成了死鎖。但是業務上destroy刪除的數據一般為0條。所以可以只在必要的時候進行destroy操作。
- 更新的時候少用或不用notIn操作
優化後代碼:
async batchUpdate(skus, { transaction }) {
const result = await Promise.all(skus.map(async sku => {
const record = await this.app.model.Sku.upsert(sku, { transaction });
return record;
}));
// SaaS 中刪掉的 sku,插件也要同步刪除
const ids = _.map(skus, 'sku_id');
const productIds = _.map(skus, 'product_id');
const { Op } = this.app.Sequelize;
const delSkus = await this.app.model.Sku.findAll({
where: {
sku_id: { [Op.notIn]: ids },
product_id: productIds,
}, transaction,
});
if (delSkus && delSkus.length) {
await this.app.model.Sku.destroy({
where: {
sku_id: delSkus.map(sku => sku.sku_id),
}, transaction,
});
}
return result;
};