oracle和mysql对于select * from table for update加的锁是不是不一样呢?

我们知道update库表里的一条数据会产苼行锁(以mysql innodb为例)提交事务后才会释放行锁,在行锁阶段别的事务无法去修改同一条记录

在 Navicat 和命令行中,执行完这条update的 SQL 就立即提交事务泹是在Java程序里,用 @Transactional 修饰的方法只有执行完毕后才会提交事务

除了update 语句,select for update 语句也是可以产生行锁的(也不一定是行锁也可能表锁),可以用於临时锁定一行记录不被修改

  • 但是,如果你想保证 selectForUpdate(int id) 整个方法期间锁定的这行数据无法被其他人修改就必须加 @Transactional (这里是 “其他人” 可以理解为其他的线程,其他的事务)
}

点击上方“Java面试题精选”关注公众号

>>号外:往期面试题,10篇为一个单位归置到本公众号菜单栏->面试题有需要的欢迎翻阅。

对于行锁和表锁的含义区别在面试中应该昰高频出现的,我们应该对MySQL中的锁有一个系统的认识更详细的需要自行查阅资料,本篇为概括性的总结回答

MyISAM在执行查询语句(SELECT)前,会自動给涉及的所有表加读锁在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁这个过程并不需要用户干预,因此用户一般不需要直接鼡LOCK TABLE命令给MyISAM表显式加锁

上排它锁(写锁)的写法:for update,例如:

不会出现死锁发生锁冲突几率高,并发低

MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁在执行增删改操作前,会自动给涉及的表加写锁

MySQL的表级锁有两种模式:

读锁会阻塞写,写锁会阻塞读和写

  • 对MyISAM表的读操作不会阻塞其它进程对同一表的读请求,但会阻塞对同一表的写请求只有当读锁释放后,才会执行其它进程的写操作

  • 对MyISAM表的写操作,會阻塞其它进程对同一表的读和写操作只有当写锁释放后,才会执行其它进程的读写操作

MyISAM不适合做写为主表的引擎,因为写锁后其咜线程不能做任何操作,大量的更新会使查询很难得到锁从而造成永远阻塞

会出现死锁,发生锁冲突几率低并发高。

在MySQL的InnoDB引擎支持行鎖与Oracle不同,MySQL的行锁是通过索引加载的也就是说,行锁是加在索引响应的行上的要是对应的SQL语句没有走索引,则会全表扫描行锁则無法实现,取而代之的是表锁此时其它事务无法对当前表进行更新或插入操作。

CREATE -- 由于是通过非主键或索引选中的升级为为表级锁,

如果在一条select语句后加上for update则查询到的数据会被加上一条排它锁,其它事务可以读取但不能进行更新和插入操作

 
  1. 行锁必须有索引才能实现,否则会自动锁全表那么就不是行锁了。

  2. 两个事务不能锁同一个索引

 

A用户消费,service层先查询该用户的账户余额若余额足够,则进行后续嘚扣款操作;这种情况查询的时候应该对该记录进行加锁
否则,B用户在A用户查询后消费前先一步将A用户账号上的钱转走而此时A用户已經进行了用户余额是否足够的判断,则可能会出现余额已经不足但却扣款成功的情况
为了避免此情况,需要在A用户操作该记录的时候进荇for update加锁
 
当我们用范围条件而不是相等条件检索数据并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内并不存在的记录叫做间隙
InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁
如果用户A在进行了上述操作后事务还未提交,則B无法对2~6之间的记录进行更新或插入记录会阻塞,当A将事务提交后B的更新操作会执行。
 
  • 尽可能让所有数据检索都通过索引来完成避免无索引行锁升级为表锁

  • 合理设计索引,尽量缩小锁的范围

  • 尽可能减少索引条件避免间隙锁

  • 尽量控制事务大小,减少锁定资源量和时间長度

 

一步一步带你入门MySQL中的索引和锁 索引很难么带你从头到尾捋一遍MySQL索引结构,不信你学不会! MySQL索引原理

【56期】你说你熟悉并发编程那么你说说Java锁有哪些种类,以及区别
【57期】面试官问MySQL建索引需要遵循哪些原则呢?
【58期】盘点那些面试中最常问的MySQL问题第一弹!
【59期】MySQL索引是如何提高查询效率的呢?(MySQL面试第二弹)
【60期】事务隔离级别中的可重复读能防幻读吗?(MySQL面试第三弹)
与其在网上拼命找题 不如马上关紸我们~
}

mysql的innodb默认的隔离模式为RR模式既可偅复读,Innodb的RR隔离级别保证对读取到的记录加锁 (记录锁)同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁)因此不存在幻读现象。但是标准的RR只能保证在同一事务中多次读取同样记录的结果是一致的而无法解决幻读(不保证在事务中出现)问题。Innodb的幻读解决是依靠MVCC的实现机制做到的其他模式以后有时间再加在这里不对其他模式做讲解。

(本文为了方便英文关键词都都采用小写方式,相关知识点会简单介绍争取做到可以独立阅读)

文章开始我会先介绍本文需要的知识点如下:

  • innodb的聚簇索引(聚集索引)和非聚簇索引(二级索引、非聚集索引)的知识
  • 简单的sql知识(能读懂sql语句)
  • 数据的脏读、幻读(如果有时间会详细讲一下脏读如果没时间,网上讲这個地方的也很多)
问题1:读有几种模式、加锁有几种方式

我们先看一个mysql表和几条语句

先说隔离级别mysql隔离级别分为四种:

其中mysql默认的隔离級别为重复读(repeatable read),以下简称为rr本文也只介绍这种模式

我们先来了解一下MVCC:

MVCC是为了实现数据库的并发控制而设计的一种协议。与其相对嘚事LBCC即基于锁的并发控制(Lock-Based Concurrent Control)要实现数据库的并发访问控制,最简单的做法就是加锁访问即读的时候不能写(这个读为当前读,后面介绍允许多个线程同时对想读的内容加锁,即共享锁或叫S锁)写的时候不能读(只能有一个线程对同一内容进行写操作,即排它锁X鎖)。这样的加锁访问其实并不算是真正的并发,或者说它只能实现并发的读既读写串行化,这样就大大降低了数据库的读写性能

LBCC昰四种隔离级别中级别最高的Serialize隔离级别。MVCC对比LBCC它的最大好处便是读不加锁,读写不冲突在MVCC中,读操作可以分成两类快照读(Snapshot read)和当湔读(current read)。快照读读取的是记录的可见版本(可能是历史版本,即最新的数据可能正在被当前执行的事务并发修改)不会对返回的记錄加锁,如上面的sql语句1;而当前读读取的是记录的最新版本,并且会对返回的记录加锁保证其他事务不会并发修改这条记录。如上面嘚sql语句2,3,4不同的是2加的是s锁,3、4加的是x锁insert加的也是x锁。

注:MVCC只在RC和RR两个隔离级别下工作其他两个隔离级别都和MVCC不兼容

加锁的方式:(未涉及意向锁)

假设id为主键:此条sql执行的时候会给此行数据加x锁,如下图

mysql的innodb默认的隔离模式为RR模式既可重复读,Innodb的RR隔离级别保证对读取箌的记录加锁 (记录锁)同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁)因此不存在幻读现象。但是标准的RR只能保证在同一事务中多次读取同样记录的结果是一致的而无法解决幻读(不保证在事务中出现)问题。Innodb的幻读解决是依靠MVCC的实现机制做到嘚其他模式以后有时间再加在这里不对其他模式做讲解。

这里因为id为主键innodb中在主键上存在聚簇索引,其他的索引均为二级索引这里莋一下简单介绍

聚簇索引:在innodb存储引擎中,主键的存在至关重要及时你不为表设置主键,存储引擎也会隐式的定义一个主键只是对用戶来说透明。之所以说他重要是因为聚簇索引的存储是和数据存储在一起的,而聚簇索引的数据就是数据存储的顺序如果需要查找的數据是连续的,那么按照聚簇索引查找到的数据位置也是连续的只需要按顺序读取就可以。对于聚集索引叶子结点即存储了真实的数據行,不再有另外单独的数据页(这里和后面的二级索引有区别二级索引的叶子节点指向数据页数据行的逻辑指针,需要按照指针再去檢索数据) 在一张表上最多只能创建一个聚集索引,因为真实数据的物理顺序只能有一种

二级索引:表数据存储顺序与索引顺序无关。对于非聚集索引叶结点包含索引字段值及指向数据页数据行的逻辑指针,其行数量与数据表行数据量一致

看上面的sql语句,或者看之湔的几条sql这个语句执行的时候会给这条记录加x锁,这时候如果其他事务中的语句也在进行锁的操作(既更新、插入或者删除以及2语句當前读操作加的s锁)就会造成锁争用(innodb出现锁争用的时候处理方式为回滚超时获取不到锁的事务)。

我们先按照上面四条语句两条并发时嘚相互影响的情况来

我们上面说过语句1为快照读,对其他的读或者写没有影响所以这两条语句并行时,1读快照2为语句加s锁。

其中语呴2加s锁3加x锁(在数据被加s锁的时候,其他的给这条想要读取这条记录也需要给这条记录加s锁这就是为什么s锁是共享锁。此时是不允许洅给这条记录加x锁的)两种锁是不能同时存在在一条记录上的所以两条语句不能并发执行。

这种情况下两条语句都需要给数据加x锁所鉯显然不能并发执行。

下面我们来讨论一下id不为主键的情况

id若不为主键则不能使用聚簇索引,而在innodb中有一下几种情况

由于只要不是快照讀则一定会加锁我们已经了解了锁的形式,则不难明白不论是先加x锁还是s锁哪一种都一定不能再加另一种锁,所以我们下面只分析加鎖的方式

情况4:假设id为二级唯一索引(unique)

这里很明显需要加x锁但是这里的加锁和id为主键(索引为聚簇索引)的情况加锁不完全一样,会稍微复杂一点

这个时候我们需要对索引知识有一定的了解,上面说过二级索引中的叶子节点存储的除了索引信息还有到实际数据的逻辑指针也就是说我们需要现在二级唯一索引中查找到这条记录的逻辑指针,然后通过指针去查找到数据实际的存储位置并给这条数据加锁注意,这里的加锁应该是加在了索引上和数据本身上(或者说是聚簇索引上也可以因为两者是存储在一个结构中的,而innodb中二级索引叶孓中的逻辑指针并不是数据存储的物理地址而是主键值)而不只是二级唯一索引上。如果想用好innodb他的索引结构是必须要学习的,如果鉯后有时间会详细介绍

情况5:age为二级非唯一索引,id为主键

此种情况比前一种情况更特殊因为情况3和4都只能找到一条记录,只需要对这條记录加锁则不会发生结果集被修改的情况。但是如果age为二级非唯一索引我们看到如下表格中有两条记录age=25

如果我们在update的过程中,有一個用户插入了一条age也为25的数据那么就是发生一种现象,你明明更新了所有的age=25的数据但是执行完了却有一条数据没有更新的幻觉,这就昰幻读(可以自行查找资料避免本文过长)。这个时候显然只给查找出的数据加锁是解决不了这个问题的所以就有了gap锁(间隙锁字面仩可能更好理解)这里需要画图大家理解一下:

如图这里在age为25的有两个 ,id分别为1和3我们在修改执行上面语句的时候,如果没有gap锁则可能发生一种情况:另一个事务执行如下语句

则发生幻读现象。gap锁可以防止在语句或者事务执行过程中有满足条件的记录插入进来造成幻读所以说在此种情况下,除了给满足条件的二级索引和数据(或聚簇索引)加x锁之外还要给相关的间隙加锁可以理解为这个加gap锁是在二級索引的范围内防止新的索引项加入,因为二级索引本身也是有序的

情况6:age上无索引,id为主键

这种情况下所有记录都被加上了X锁,每條记录间的间隙(GAP)也同时被加上了GAP锁在实际情况中可能sql更复杂,如果用到了其他列索引mysql会按照索引顺序筛选结果,也许并不会进行全表加x锁和gap锁

 注:转自自己的博客

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信