官方文档:https://uniapp.dcloud.io/uniCloud/cf-database?id=transaction
事务通常用来在某个数据库操作失败之后进行回滚。
案例1:高并发下简单的防止超卖
高并发时很多用户同时对一条数据读写,很容易造成数据混乱,表现在秒杀抢购等场景就是超卖。以秒杀为例,开发者可以从扣除库存这步入手对超卖进行很大程度的限制,下面是一个简单的示例:
// 云函数
const db = uniCloud.database()
const dbCmd = db.command
exports.main = async function(event){
const transaction = await db.startTransaction()
// 其他业务逻辑...
// 库存减一
// 这里要注意,操作数据库的时候是使用transaction.collection,而不是往常的写法db.collection
const reduceRes = await transaction.collection('goods').doc('goods_id').update({
stock: dbCmd.inc(-1)
})
if(reduceRes.updated === 0) { // 如果没成功更新库存就认为下单失败
await transaction.rollback()
return {
code: 1001,
message: '下单失败'
}
}else {
await transaction.commit()
console.log(`transaction succeeded`)
return {
success: true,
message: '下单成功'
}
}
}
限制
事务操作时为保障效率和并发性,只允许进行单记录操作,不允许进行批量操作,但可以在一个事务进行多次数据库操作。
- 以前阿里云对于修改和删除仅支持使用doc方法,不支持使用where方法(现在不知道如何,有待测试)。
- 新增时使用add方法一次只可以新增单条,不可新增多条,即不支持在add方法内传入数组
- 腾讯云没有限制where的使用,但是使用where修改或删除多条会导致无法回滚
注意事项
- 请注意transaction.doc().get()返回的data不是数组形式
示例代码
两个账户之间进行转账的简易示例(阿里云):
const db = uniCloud.database()
const dbCmd = db.command
exports.main = async (event) => {
const transaction = await db.startTransaction()
try {
const aaaRes = await transaction.collection('account').doc('aaa').get()
const bbbRes = await transaction.collection('account').doc('bbb').get()
if (aaaRes.data && bbbRes.data) {
const updateAAARes = await transaction.collection('account').doc('aaa').update({
amount: dbCmd.inc(-10)
})
const updateBBBRes = await transaction.collection('account').doc('bbb').update({
amount: dbCmd.inc(10)
})
const aaaEndRes = await transaction.collection('account').doc('aaa').get()
if (aaaEndRes.data.amount < 0) { // 请注意transaction.doc().get()返回的data不是数组形式
await transaction.rollback(-100)
return {
success: false,
error: `rollback`,
rollbackCode: -100,
}
} else {
await transaction.commit()
console.log(`transaction succeeded`)
return {
success: true,
aaaAccount: aaaRes.data.amount - 10,
}
}
} else {
return {
success: false,
error: `rollback`,
rollbackCode: -100,
}
}
} catch (e) {
await transaction.rollback()
console.error(`transaction error`, e)
return {
success: false,
error: e
}
}
}