【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);二是采用了行级锁。
行级锁也支持读锁和写锁两类。
1、如何加锁?
mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
2、实现原理
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,并请求共享或排他锁时,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
四、锁的算法
- record lock:单个记录的锁。
- gap lock:间隙锁,锁定一个范围,不包括记录本身
- next-key lock:gap lock+record lock
默认隔离级别(可重复读)下,默认加的是next-key lock(为了解决幻读问题),当索引中含有唯一属性时(唯一索引,主键索引),会降级为record lock。
在读已提交隔离级别下,加的是record lock
1、例1
现在表z,有a,b两列,a是主键,全表只有一个主键索引。现在记录如下:(1,1)(3,1)(5,3)(7,6)(10,8)
select * from z where b=3 for update
因为b没有索引,所以走得是全表扫描。有因为加锁是通过对索引加锁实现,因为没有走索引,所有会锁整个表,也就是表锁
2、例2
现在表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)加锁
select * from z where b=3 for update
因为锁是通过对索引加锁实现的。所以这里需要对主键索引和辅助索引加锁,主键索引加的锁会由next-key锁退化成是record lock,辅助索引加的锁是next-key lock,锁定范围是(1,3)、3、(3,6)
五、select的几种类型
1、快照读
读取的是快照版本,也就是历史版本。普通的SELECT就是快照读
2、当前读
读取的是最新版本。
UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE是当前读。
默认的可重复读隔离级别,使用的是快照读
读已提交使用的是当前读
3、一致性非锁定读
实现原理是通过MVCC机制实现,如果读取的行正处于update或delete中,读操作不会去等待行上X锁的释放,而是去读取行的快照数据。
MVCC,多版本并发控制技术。在 InnoDB 中,在每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号。通过版本号和行锁,从而提高数据库系统并发性能。
一致性非锁定读可以极大的提高并发性能
不同的事务隔离级别,读取的快照版本是有差别的
- 读已提交隔离级别,总是读取最新的快照版本。可能会产生幻读
- 可重复读隔离级别,总是读取事务开始后第一次读取的快照版本。可以避免幻读的产生
4、一致性锁定读
默认配置下,采用可重复读的隔离级别,读取数据采取的是一致性非锁定读。
但是某些场景下需要对读取操作加锁来保证严格的数据一致性,这时候可以显式的对读取的记录进行加锁:
- select *** for update(对读取记录加X锁)
- 给索引记录加锁,这种情况下跟UPDATE的加锁情况是一样的
- select *** lock in share model(对读取记录加S锁)
- 给记录假设共享锁,这样一来的话,其它事务只能读不能修改,直到当前事务提交