user := &User{}
if err := Db.Select("diamond").Where("uid = ?", uid).First(user).Error; err != nil {
log.Println(err)
return 5001,"用户不存在",""
}
blance := user.Diamond+money
if blance <0{
log.Println(user)
log.Println(money)
log.Println(blance)
return 5003,"用户余额不足",""
}
if err := Db.Model(User{}).Where("uid = ?",uid).UpdateColumn("diamond",blance).Error;err != nil{
log.Println(err)
return 5002,"用户交易失败",""
}
这样并发的时候会有问题,请问怎么加锁啊?
Comment From: pathbox
建议使用事务
Comment From: jinzhu
用事物+数据库检验,select for update之类的
Comment From: loyalpartner
@jinzhu 在事务里面可以使用 goroutine 执行更新或者删除操作吗?下面的代码这样写有问题吗
package v2
import (
"errors"
"common/databases/mysql"
"gorm.io/gorm"
"reflect"
)
type Op string
const (
Op_CREATE Op = "create"
Op_UPDATE Op = "update"
Op_DELETE Op = "delete"
)
func (o Op) Validate(opInter *OpInter) error {
if o == Op_CREATE {
return nil
}
if len(opInter.Where) == 0 {
return errors.New("update where is nil")
}
return nil
}
type OpInter struct {
Op Op
Object interface{}
Where string
}
func OpTables(tx *gorm.DB, opInter *mysql.OpInter, errChan chan error, i int, stopChan chan bool) {
select {
case stop := <-stopChan:
if stop {
//fmt.Print(fmt.Sprintf("error count:%d", i) + "\n")
return
}
//fmt.Print(fmt.Sprintf("success count:%d", i) + "\n")
err := opInter.Op.Validate(opInter)
if err != nil {
errChan <- err
return
}
object := opInter.Object
switch opInter.Op {
case mysql.Op_CREATE:
err := tx.Create(object).Error
errChan <- err
case mysql.Op_UPDATE:
err := tx.Updates(object).Where(opInter.Where).Error
errChan <- err
case mysql.Op_DELETE:
err := tx.Where(opInter.Where).Delete(object).Error
errChan <- err
default:
errChan <- errors.New("op is invalidate")
}
return
}
}
// OpObjectRelation:op指定模型及其关联数据,支持多个模型op(create,update,delete)
func OpObjectRelation(db *gorm.DB, opInters []*mysql.OpInter) error {
return db.Transaction(func(tx *gorm.DB) error {
var errChan = make(chan error, 5)
// 是否执行关闭
var stopChan = make(chan bool)
var validateOpInter []*mysql.OpInter
for _, opInter := range opInters {
if reflect.ValueOf(opInter.Object).IsNil() {
continue
}
validateOpInter = append(validateOpInter, opInter)
}
// 处于等待状态的协程数量
goWaitNum := len(validateOpInter)
for i, info := range validateOpInter {
go OpTables(tx, info, errChan, i, stopChan)
}
stopChan <- false
goWaitNum -= 1
var count int
for {
select {
case err := <-errChan:
count += 1
if err != nil {
// 关闭等待状态协程,否则会导致产生更多协程阻塞,且无法被消费/回收,导致内存泄漏
for i := 0; i < goWaitNum; i++ {
stopChan <- true
}
close(errChan)
close(stopChan)
return err
} else {
if count == len(validateOpInter) {
close(errChan)
close(stopChan)
return nil
} else {
stopChan <- false
goWaitNum -= 1
}
}
}
}
})
}
Comment From: AzraelJi
老哥 试过可行吗