概念
悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。
实现机制
版本号控制:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数。当数据被修改时,version值会+1。当线程A要更新数据时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的 version值与当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
比如:数据库中用户表中有一个version字段,当前值为0;而当前帐户余额字段(money)为100 。
线程A此时将其读出(version=0),并从其帐户余额中扣除50(100-50)。
在线程A操作的过程中,线程B也读入此用户信息(version=0),并从其帐户余额中扣除30(100-30)。
线程A完成了修改工作,将数据版本号加一(version=1),连同帐户扣除后余额(money=50),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录version更新为1。
线程B完成了操作,也将版本号加一(version=1)试图向数据库提交数据(money=70),但此时比对数据库记录版本时发现,线程B提交的数据版本号为1 ,数据库记录当前版本也为1,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,线程B的提交被驳回。这样,就避免了操作员B用基于version=0的旧数据修改的结果覆盖线程A的操作结果的可能。
Gorm使用乐观锁
在开发业务是使用Gorm对数据库数据进行操作,目前Gorm已实现乐观锁插件,使用起来也很方便。
Gorm乐观锁插件GitHub地址:/go-gorm/optimisticlock
首先,导入插件包/go-gorm/optimisticlock
对应数据库版本控制的字段定义为类型
import "/plugin/optimisticlock"
type User struct {
ID int
Money string
Version optimisticlock.Version
}
先查询数据库用户数据
var user User
DB.First(&user,1)
// select * from user where id = 1
然后使用user
对象来进行后续的更新操作就可以自动更新语句中加入版本控制条件了,非常方便,就不用自己手动拼装version
条件了
DB.Model(&user).Update("money", 200)
// UPDATE `users` SET `money`=200,`version`=`version`+1 WHERE `users`.`version` = 1 AND `id` = 1