行级锁 表级锁 乐观锁 悲观锁

乐观锁与悲观锁

乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

案例:

某商品,用户购买后库存数应-1,而某两个或多个用户同时购买,此时三个执行程序均同时读得库存为n,之后进行了一些操作,最后将均执行update table set库存数=n-1,那么,很显然这是错误的。

解决:

1.使用悲观锁(其实说白了也就是排他锁)

程序A在查询库存数时使用排他锁(select * from table where id=10 for update

然后进行后续的操作,包括更新库存数,最后提交事务。

程序B在查询库存数时,如果A还未释放排他锁,它将等待。

程序C同B……

2.使用乐观锁(靠表设计和代码来实现)

一般是在该商品表添加version版本字段或者timestamp时间戳字段

程序A查询后,执行更新变成了:
update table set num=num-1 where id=10 and version=23

这样,保证了修改的数据是和它查询出来的数据是一致的,而其他执行程序未进行修改。当然,如果更新失败,表示在更新操作之前,有其他执行程序已经更新了该库存数,那么就可以尝试重试来保证更新成功。为了尽可能避免更新失败,可以合理调整重试次数(阿里巴巴开发手册规定重试次数不低于三次)。

总结:对于以上,可以看得出来乐观锁和悲观锁的区别。

1.悲观锁使用了排他锁,当程序独占锁时,其他程序就连查询都是不允许的,导致吞吐较低。如果在查询较多的情况下,可使用乐观锁。

2.乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入较频繁,对吞吐要求不高,可使用悲观锁。

也就是一句话:读用乐观锁,写用悲观锁。

表级锁与行级锁

表级锁

  • table-level locking,锁住整个表。
  • 开销小,加锁快。
  • 不会死锁(一次性加载所需的所有表)。
  • 锁粒度大,发生锁冲突概率大,并发效率低。
  • 适合查询。

行级锁

  • row-level loking,锁住一行记录。
  • 开销大,加锁慢。
  • 会死锁。
  • 锁粒度小,发生所冲突概率小,并发效率高。
  • 适合并发写,事务控制。
  • 并不是直接丢记录行加锁,而是对行对应的索引加锁:
    • 如果sql 语句操作了主键索引,Mysql 就会锁定这条主键索引。
    • 如果sql语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
    • 在InnoDB中,如果SQL语句不涉及索引,则会通过隐藏的聚簇索引来对记录加锁。
    • 对聚簇索引加锁,实际效果跟表锁一样,因为找到某一条记录就得扫描全表,要扫描全表,就得锁定表。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值