数据库——MySQL中的锁(表锁、行锁、间隙锁、乐观锁、悲观锁)

锁是计算机协调多个进程或纯线程并发访问某一资源的机制。

一、MySQL 中有哪几种锁

不同的存储引擎支持不同的锁机制

MyISAM和MEMORY存储引擎:表级锁

BDB存储引擎:页面锁,但也支持表级锁

InnoDB存储引擎:既支持行级锁,也支持表级锁,默认情况下是采用行级锁。

二、MySQL大致可归纳为以下3种锁:

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率最低,并发度最高。

页面锁:开锁和加锁时间界于表锁和行锁之间,会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

三、锁的分类:

  • 粒度:表锁 / 行锁 / 间隙锁
  • 方式:读锁(共享锁) / 写锁(排他锁)
  • 态度:悲观锁 / 乐观锁

                 悲观锁 (读锁、写锁都是悲观锁)

                 乐观锁 (乐观锁,需要外部程序实现)

四、锁的方式:

读锁:

表加读锁后,该表只能读不能写,同时加锁线程受到限制:不能再访问别的表

如:A 进入厕所的1号隔间,不关隔间门(使用读锁),

       B 可以看到这个隔间发生了什么(其他线程能读),但不能再使用这个隔间。

       A 因隔间门没关,所以也不能执行写操作,还不能访问其他隔间。

写锁:

表加写锁后,其他线程不能读和写。

五、MyISAM,表级锁的两种模式:表共享锁和表独占写锁

对某一个表的读操作,不会阻塞其他用户对同一表请求,但会阻塞对同一表的写请求;

对MyISAM的写操作,则会阻塞其他用户对同一表的读和写操作;

MyISAM表的读操作和写操作之间,以及写操作之间是串行的。

# 加读锁 
lock table table1 read;
# 加写锁
lock table film_text write; 
#释放锁
unlock tables;

六、InnoDB,行级锁的两种模式:共享锁和排他锁

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。另外,为了允许行锁和表锁共存,实现多粒度锁机制

InnoDB还有两种内部使用的意向锁,这两种意向锁都是表锁。

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

 InnoDB行锁模式兼容性列表

请求锁模式

   是否兼容

当前锁模式

X

IX

S

IS

X

冲突

冲突

冲突

冲突

IX

冲突

兼容

冲突

兼容

S

冲突

冲突

兼容

兼容

IS

冲突

兼容

兼容

兼容

  1. 意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;
  2. #事务通过以下语句给显示的给记录加锁
     共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
     排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
  3. InnoDB行锁是通过索引上的索引项加锁来实现的。(只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表级锁)
  4. 即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

七、InnoDB,间隙锁(Next-Key锁)

  1. 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
  2. #emp表中只有101条记录,其empid的值分别是 1,2,...,100,101 
    Select * from  emp where empid > 100 for update; 
    #上述是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
  3. 使用间隙锁的目的:防止幻读,满足恢复和复制的需要。

八、锁的态度:

乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

悲观锁:

  1. 悲观并发控制,阻塞一个事务以影响其他用户的方式来修改数据。如果一个事务的执行的操作某行应用了锁,那只有当这个事务把锁释放,其他事务才能执行与该锁冲突的操作,主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务成本的环境中。
  2. 流程:在对任意记录进行修改前,先尝试为该记录加上排他锁,如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
  3. 使用:在InnoDB中使用悲观锁必须关闭Mysql的自动提交属性,MySQL默认使用autocommit模式。需要set autocommit=0,即不允许自动提交。
  4. 用法:select * from tablename where id = 1 for update;
  5. 申请前提:没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞。for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。
  6. 优点与不足:悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数。
  7. MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住。

乐观锁:

  1. 乐观并发控制,假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据,如果其他事务又更新,正在提交的事务会进行回滚。
  2. 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新时,才会正式对数据的冲突与否进行检测,如果发现冲突,则返回用户错误信息,让用户决定如何去做。在对数据库进行处理时,乐观锁不会使用数据库提供的锁机制。
  3. 实现方式:版本号(记录数据版本)、时间戳。
  4. 流程:创建一张表时添加一个version字段,表示是版本号,修改数据的时候首先把这条数据的版本号查出来。
  5. 数据版本:为数据增加的一个版本标识,当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新,当我们提交更新时,判断数据库对应记录的当前版本信息与第一次取出的版本标识比对,若一致,表明这条数据没有被其他用户修改,予以更新,否则是过期数据(数据在操作期间被其他用户修改过,此时需要在代码中抛出异常或回滚)。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。
  6. 优点与不足:乐观并发控制相信事务之间的数据竞争的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。
  7. update是单线程,即如果一个线程对一条数据进行update操作,会获得锁,其他线程如果对同一条数据操作会阻塞,直到这个线程update成功后释放锁。
  8. 乐观锁不需要数据库底层支持。

https://blog.csdn.net/tanga842428/article/details/52748531

https://blog.csdn.net/nuoWei_SenLin/article/details/80470339

https://blog.csdn.net/qq_35642036/article/details/89554721

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值