这里是我自己整理的一些资料,大家不懂的可以相互学习呀。。。

mysql 共享锁和排他锁以及乐观锁

Mysql ZZT 1868次浏览 已收录 0个评论

悲观锁

###悲观锁:

    悲观锁中(前后已经添加事务),$sql = select * from test for update,同一时间内只能有一个这样的事务在运行,其他的事务处于锁中,所以当地一条 $sql 运行的时候,第二条$sql是阻塞等待的形式,但是执行select * from test 是不用阻塞的,可以直接查询相互数据。

转载自:MySQL中的共享锁与排他锁
在 MySQL中的行级锁,表级锁,页级锁中介绍过,行级锁是Mysql中锁定粒度最细的一种锁,行级锁能大大减少数据库操作的冲突。行级锁分为共享锁和排他锁两种,本文将详细介绍共享锁及排他锁的概念、使用方式及注意事项等。

共享锁(Share Lock)

共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获得共享锁的事务只能读数据,不能修改数据。
用法

SELECT ... LOCK  IN SHARE MODE;

在查询语句后面增加 LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。

排他锁(eXclusive Lock)

共享锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的锁,其它事务也不能对A做update,insert,delete操作,因为在innodb中这些操作默认加了排他锁,可以进行select 操作因为查询的时候是不加任何锁的
用法

SELECT ... FOR UPDATE;

在查询语句后面增加 FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。

意向锁

InnoDB还有两个表锁:
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是InnoDB自动加的,不需要用户干预。

对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);对于一般的Select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。
共享锁: SELECT ... LOCK IN SHARE MODE;
排他锁: SELECT ... FOR UPDATE;
注意事项

行级锁与表级锁

行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁。行级锁的缺点是:由于需要请求大量的锁资源,所以速度慢,内存消耗大。
行级锁与死锁

MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。
在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。 在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。
当两个事务同时执行,一个锁住了主键索引在等待其他相关索引,一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。
有多种方法可以避免死锁,这里只介绍常见的三种

1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

乐观锁

乐观锁与悲观锁的应用场景:

摘自知乎:
作者:vczh
链接:https://www.zhihu.com/question/29420056/answer/44299842
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这跟乐观悲观其实没关系,正确的解决方法只有两种:

1、你读一行记录的时候就上读锁,在你释放掉这个锁之前,所有的人都没法写,包括你自己——除非你自己在后面升级成写锁(也是SQLServer为什么会有更新锁的原因,更新有很多复杂的策略,所以搞出了各种类型的锁)。但是如果两个人同时上了读锁,那没有任何一个人可以成功更新成写锁。因此如果你原本就打算读后写,就应该上更新锁。更新锁是一种可以阻止别人读的读锁,就是用来在你决定更新的时候升级成写锁的。为了优化各种复杂的情况,SQLServer一共有几十种锁可以用来兼容或者互斥地锁同一个对象,总觉得读写锁已经被玩坏了……

2、你写一行记录的时候,要把记录原本的内容也上传上去(当然一般上传的都是当时拿到的版本号),服务器那边会比较一下,如果记录被别人改过了,就驳回你的请求,然后你自己重新看着办。一般来说GUI程序更适合用这个,拿到服务器的驳回请求之后,重新获取一下内容,然后告知用户,让用户自己决定下一步怎么办,要取消呢,还是要改呢。这样的话服务器就很容易做了,反正用户的手速是很慢的,你就算只接上mutex也不会有任何性能上的问题,也没有什么乐观悲观。当然无论怎么做,这两种做法的overhead都是很大的,所以才会搞出MVCC这种东西。虽然MVCC优化了很多读的东西,但是一旦你面对的情况是大家一拥而上一起抢着写,那总是会退化到上面两种场景中的其中一种。
总结: 对于GUI的场景,用乐观所可以很好的提高服务器响应速度和用户体验,有时候必须等待结果的响应,程序自己处理时用悲观锁应该会好一点。这里借鉴的是sqlserver,但是思想应该是通用的。


乐趣公园 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明mysql 共享锁和排他锁以及乐观锁
喜欢 (1)

文章评论已关闭!