理解 悲观锁就是什么事情都是须要当心翼翼,生怕弄错了出大问题,git
通常状况下 "增删改" 都是有事务在进行操做的,可是 "查" 是不须要事务操做的,sql
可是凡事没有例外,好比双十一购物,不少人抢购一个物品,可是商品总数(假设5个)数据库
同一时间进行操做,每一个人都能看到物品多少个,都早同一时间下单,可是只有5个商品,只有5个用户能过购买到(每人一个购买),其余人也会显示出订单的操做,可是库存已是为0没法购买的行为(显示仍是5ge库存),会给用户形成很差体验安全
既让如此 为何不让一个事务锁定物品的个数修改,而其余用户只能查看,并不能下单,当用户进行updata操做以后释放锁,其余用户才能根据状况下单,再次把物品行锁住(不会出现订单表交钱了还提示没库存)spa
可能我说的不是很准确,可是悲观锁就是一步一步走,若是轮到它则执行则锁行操做,避免一些问题,通常用在金钱查询,库存查询等... 版本控制
执行sql语句格式code
select money from usercred where id=1 for update;
统一格式在select查询条件以后增长 for updata (记住查询条件是索引列才是锁行不让则是锁表) 查询操做中除了敏感数据查询,其余不须要使用锁行操做orm
整体理解就是 悲观锁是一个在有须要求的状况下查询语句事务中隔离性最高的ser... 串行,当悲观锁锁住行时,其余事务只能等待,而且不能读取该行悲观数据blog
而且悲观锁只能在begin和commit之间执行索引
乐观锁 查询出须要修改的数据,可是增长一个字段做为一个版本控制(像git同样),若是进行修改一块儿将版本控制的字段一块儿where做为条件,若是符合则修改,不符合也没事,由于数据库中的数据给其余事务修改以后,where查询的条件不符合,因此不会对持久化的数据形成影响
例子以下
create tablecard(
idint;money float;
versionint; //乐观锁控制,字段名无所谓primary key(id); //主键
auto increment(id);//自增加index(money,version); //索引
);
建立上面一个表,我使用go的gorm来表达一下操做
type card struct{
Idint `gorm:"id"`
Moneyfloat `gorm:"money"` //默认这个帐户100元
Version int `gorm:"varsion"`
}
func main(){
var c cardif err := db.first(&c).Error():err!=nil{ //查出一条数据
panic(err)
}
err= db.Model(&c).Where("id = ? and varsion = ?",c.Id,c.Version).updata(c.Money + 200 ,c.Version+1).error()if err !=nil{//说明其余事务在我执行这个操做时进行了updata commit,我这边where在数据查询表没有出现符合条件的,因此执行失败
}
fmt.Println("success")
}
乐观锁就不是一个事务的操做,是手动建立的一种逻辑来保证数据一致性
根据多个条件来进行控制数据是否和查询时一致保持安全
比方 a 操做查询获得一个数据,其中返回的数据集中含有 money(100)和一个varsion(1),,你对这个数据进行修改,其中money+100,varsion+1 ,可是在你修改尚未提交时,其余b,c,d..的链接也同时和你同样的操做,而且比你提交commit的速度快,当你使用updata修改语句去根据先前查询获得结果寻找数据时,对比version和money,可是已经有其余提交比你快提交了,找不到a查询过的数据行(此时时b链接提交的数据是money = 300,version=2),因此不会提交,丢弃操做,
乐观使用一个额外字段保证数据和查询出时的数据一致才会更新(更新查询时查询的条件要有惟一属性,不让更新查询时出现多个操做不可逆)
悲观锁更加适合转帐交易业务,乐观锁倾向并非对数据要求绝对安全的环境,可是绝对不适用于转帐交易等敏感地位
悲观锁和事务区别在于
悲观锁会将查询时就将行锁住,其余链接操做不能查询更不能修改,可是也是事务的一种
事务 通常用于增删改而不用于查的事务管理(虽然也能够管理查的事务,可是和悲观锁同样差很少,除非时特别需求,否则不会要求对查的操做加锁)