天天看点

【MySQL】mysql中的锁机制

一、分类

mysql的锁机制不同的存储引擎支持不同的锁机制,分为表级锁、行级锁、页面锁。myisam和memory存储引擎采用的是表级锁(table-level locking);bdb存储引擎采用的是页面锁(page-level locking),但也支持表级锁;innodb存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁

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

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

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

二、表级锁

mysql的表级锁有两种模式:表共享读锁(table read lock)和表独占写锁(table write lock)。 

对myisam表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对 myisam表的写操作,则会阻塞其他用户对同一表的读和写操作。

myisam在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行更新操作 (update、delete、insert等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用lock table命令给myisam表显式加锁。

三、行级锁

innodb与myisam的最大不同有两点:一是支持事务(transaction);二是采用了行级锁。

行级锁也支持读锁和写锁两类。

mysql innodb引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。

innodb行锁是通过给索引上的索引项加锁来实现的,这一点mysql与oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。innodb这种行锁实现特点意味着:只有通过索引条件检索数据,并请求共享或排他锁时,innodb才使用行级锁,否则,innodb将使用表锁! 

四、锁的算法

record lock:单个记录的锁。

gap lock:间隙锁,锁定一个范围,不包括记录本身

next-key lock:gap lock+record lock

默认隔离级别(可重复读)下,默认加的是next-key lock(为了解决幻读问题),当索引中含有唯一属性时(唯一索引,主键索引),会降级为record lock。

在读已提交隔离级别下,加的是record lock

现在表z,有a,b两列,a是主键,全表只有一个主键索引。现在记录如下:(1,1)(3,1)(5,3)(7,6)(10,8)

select * from z where b=3 for update

因为b没有索引,所以走得是全表扫描。有因为加锁是通过对索引加锁实现,因为没有走索引,所有会锁整个表,也就是表锁

现在表z,有a,b两列,a是主键索引,b建立辅助索引。现在记录如下:(1,1)(3,1)(5,3)(7,6)(10,8)

select * from z where a=3 for update

主键索引加的锁是record lock,对记录(3,1)加锁

因为锁是通过对索引加锁实现的。所以这里需要对主键索引和辅助索引加锁,主键索引加的锁会由next-key锁退化成是record lock,辅助索引加的锁是next-key lock,锁定范围是(1,3)、3、(3,6)

五、select的几种类型

读取的是快照版本,也就是历史版本。普通的select就是快照读

读取的是最新版本。

update、delete、insert、select ...  lock in share mode、select ... for update是当前读。

默认的可重复读隔离级别,使用的是快照读

读已提交使用的是当前读

实现原理是通过mvcc机制实现,如果读取的行正处于update或delete中,读操作不会去等待行上x锁的释放,而是去读取行的快照数据。

mvcc,多版本并发控制技术。在 innodb 中,在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号。通过版本号和行锁,从而提高数据库系统并发性能。

一致性非锁定读可以极大的提高并发性能

不同的事务隔离级别,读取的快照版本是有差别的

读已提交隔离级别,总是读取最新的快照版本。可能会产生幻读

可重复读隔离级别,总是读取事务开始后第一次读取的快照版本。可以避免幻读的产生

默认配置下,采用可重复读的隔离级别,读取数据采取的是一致性非锁定读。

但是某些场景下需要对读取操作加锁来保证严格的数据一致性,这时候可以显式的对读取的记录进行加锁:

select *** for update(对读取记录加x锁)

给索引记录加锁,这种情况下跟update的加锁情况是一样的

select *** lock in share model(对读取记录加s锁)  

给记录假设共享锁,这样一来的话,其它事务只能读不能修改,直到当前事务提交

作者:leon66666 出处:http://www.cnblogs.com/wangzhongqiu/

继续阅读