場景:javascript
- 應用剛上線排除大批量請求的問題
- 線上屢次出現的Deadlock found when trying to get lock錯誤
代碼:html
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; };
分析:java
- 報錯位置都是在this.app.model.Sku.destroy的時候報錯
- Deadlock found when trying to get lock的緣由是多個事物同事更新插入同一表的某一段數據
- 在數據量不大的狀況下,按道理說發生這種死鎖的狀況應該很是少可是事實上出現的機率很高
結論:app
- 應該是destroy使用notIn會涉及到不少行的鎖定,因此形成了死鎖。可是業務上destroy刪除的數據通常爲0條。因此能夠只在必要的時候進行destroy操做。
- 更新的時候少用或不用notIn操做
優化後代碼:async
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; };