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

*

一、并发控制
当程序中可能出现并发的情况时,就需要通过一定的手段来保证在并发情况下数据的准确性,通过这种手段保证了当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这种手段就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用户的工作产生不合理的影响。
没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题。

MySQL中,只要有多个请求需要在同一时刻修改数据,都会产生并发控制的问题。而锁的作用可以保证同一资源能被某个请求唯一使用。加锁是会消耗系统资源的,包括获得锁、检查锁是否已解除、释放锁等等操作,都会增加系统的开销.为了平衡锁的开销和数据的安全性,MySQL对锁的粒度提供了多种方式,主要分为表锁和行级锁。

1.row-level locking(行级锁)

行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

只有线程当前使用的行被锁定,其他行对于其他线程都是可用的InnoDB默认行级锁。是基于索引数据结构来实现的,而不是像ORACLE的锁,是基于block的。InnoDB也会升级为表级锁,全表/全索引更新,请求autoinc锁等。

这行级锁是mysql中粒度最细的一种锁机制,表示只对当前所操作的行进行加锁,行级锁发生冲突的概率很低,其粒度最小,但是加锁的代价最大。行级锁分为共享锁和排他锁
特点 :开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发效率高,适合并发写,事务控制。
并不是直接丢记录行加锁,而是对行对应的索引加锁:
—如果sql 语句操作了主键索引,Mysql 就会锁定这条主键索引。
—如果sql语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
–在InnoDB中,如果SQL语句不涉及索引,则会通过隐藏的聚簇索引来对记录加锁。
–对聚簇索引加锁,实际效果跟表锁一样,因为找到某一条记录就得扫描全表,要扫描全表,就得锁定表。

2.table-level locking(表级锁)

整个表被客户锁定。根据锁定的类型,其他客户不能向表中插入记录,甚至从中读数据也受到限制MyISAM、MEMORY默认锁级别,个别时候,InnoDB也会升级为表级锁。

行级锁是mysql中粒度最细的一种锁机制,表示只对当前所操作的行进行加锁,行级锁发生冲突的概率很低,其粒度最小,但是加锁的代价最大。行级锁分为共享锁和排他锁。
 开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。适合查询。

3.Optimistic Locking(乐观锁)

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

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。。

实现数据版本有两种方式,第一种是使用版本号大多数基于数据版本(Version)记录机制实现,第二种是使用时间戳。
1.使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
2.时间戳(timestamps):采取这种策略后,当每次要提交更新的时候就会将系统当前时间和实体加载时的时间进行比较,如果不一致,那么就报告乐观锁失败,从而回滚事务或者重新尝试提交。采用时间戳有一些不足,比如在集群环境下,每个节点的时间同步也许会成问题,并且如果并发事务间隔时间小于当前平台最小的时钟单位,那么就会发生覆盖前一个事务结果的问题。因此一般采用版本字段比较好。

乐观锁的缺点:

  1. ABA 问题2. 循环时间长开销大3. 只能保证一个共享变量的原子操作

使用场景

乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
乐观锁适用于读比较少的情况下(多写场景),如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

可参考:https://www.iteye.com/blog/chenzhou123520-1863407

4.Pessimistic Locking(悲观锁)

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

它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
2、实现:大多数情况下依靠数据库的锁机制实现

可参照:https://www.iteye.com/blog/chenzhou123520-1860954

一般悲观锁、乐观锁都需要都通过sql语句的设定、数据的设计结合代码来实现,例如乐观锁中的版本号字段,单纯面向数据库操作,是需要自己来实现乐观锁的,简言之,也就是版本号或时间戳字段的维护是程序自己维护的,自增、判断大小确定是否更新都通过代码判断实现。数据库进提供了乐观、悲观两个思路进行并发控制。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值