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

老哥 试过可行吗