Mysql锁分析
本篇文章关于Mysql锁的一些知识点,比较杂乱,后续会整理更新。
背景:
锁概念:
锁是数据库系统区别于文件系统的关键特点。锁机制用于管理对共享资源的并发访问,提供数据的完整性和一致性。
多粒度封锁协议:
数据库是一个共享的资源,可以供多个用户使用,这样会出现在同一时刻会有大量的事务提交数据库,造成严重的问题,因此并发控制是衡量数据库性能的一个重要的标志。
封锁是实现并发控制的一个重要技术。封锁主要有两种类型:排它锁(X锁)和共享锁(S锁)。排它锁和共享锁的兼容矩阵如下所示:
从图中可以看出当一个事务只有共享锁时,另一个事务才可以加共享锁;但排他锁和共享锁是互斥的。
封锁对象的大小称为封锁粒度。封锁对象可以会逻辑单元,也可以是物理单元。逻辑单元可以是属性集合,元组,关系,索引;物理单元可以是物理页,块。
封锁粒度与系统的并发度和并发控制的开销密度密切相关。封锁粒度越大,数据库能封锁的单元越少,并发度越小,系统开销越小;封锁粒度越小,数据库能封锁的单元越大,并发度越大,系统开销越大。
多粒度封锁:
-
多粒度树:多粒度树的根节点是整个数据库,表示最大的数据粒度,叶节点表示最小的数据粒度。
图2
图2是一个三级粒度树,最大的粒度是数据库,依次是关系和元组。多粒度封锁协议允许在多粒度树的每个节点独立的加锁。对一个节点加锁意味着所有子节点(子节点的子节点...)也被加上所有同类型的锁,因此封锁分为:显示封锁和隐式封锁(显示封锁是直接在节点上加锁;隐式加锁表示其父节点(父节点的父节点...)被加锁)。
- 但是在多粒度树上进行封锁存在很大的问题。比如,在一个节点上封锁,首先要检查该节点上是否有显示封锁的冲突;还要检查该节点所有父节点带来的隐式封锁的冲突;此外,还要检查该节点在所有子节点隐式封锁的冲突,效率非常低下,因此引入了意向锁的概念。
-
意向锁:如果对一个节点加意向锁,则说明该节点的下层节点正在被加锁;对任一节点加锁时,必须先对它的上层节点加意向锁。比如上面的对粒度封锁树,如果对元组加锁,则必须对它所在的关系加意向锁。这样加锁时就只关心本身和上层父节点,而不需要再检查子节点了,意向锁主要有常用的三种:
- 意向共享锁(IS锁):表示加锁节点的子节点意向加S锁。
- 意向排他锁(IX锁):表示加锁节点的子节点意向加X锁。
-
共享意向排他锁(SIX锁):如果对一个对象加SIX锁,表示对它加S锁,再加IX锁。
图3
Mysql知识点:
Mysql锁类型:
- X锁(排他锁):当数据库上加锁时,只允许加锁的事务进行读取和修改数据,而其他事务不能读取也不能修改数据。
- S锁(共享锁):当一个事务A对数据加锁时,可以读取数据,但不能更新数据;其他事务可以在A加锁的情况下再加S锁,进行读取数据,但是不可以修改数据,也不可以加X锁。
- 意向锁:对任一节点加锁时,必须先对它的上层节点加意向锁。
事务边界:
Mysql默认是自动提交事务的,因此一条sql语句其实就是一个事务。
图4
如果一个事务中包含多个sql语句,则需要用begin和commit显示的指定事务的边界。
Mysql锁粒度:
Mysql有三种逻辑上的锁概念:
- 表级锁:开销小,加锁快,不会发生死锁;但锁冲突概率大,并发度低
- 行级锁:开销大,加锁慢,会发生死锁;锁定粒度小,并发度高
- 页面锁:开销和加锁介于表级锁和行级锁,会发生死锁;锁定粒度也介于两者之间,并发度一般
语法:
- 表锁定:
#锁定表
LOCK TABLES
tb_name1 [AS alias] {READ[LOCAL]|[LOW_PRIORITY] WRITE}
tb_name2 [AS alias] {READ[LOCAL]|[LOW_PRIORITY] WRITE}
...
#释放表锁定
UNLOCK TABLES;
- 行锁定:
select * from table where ... for update;
- 页面锁定:
不常用
InnoDB锁:
- 只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
- InnoDB是一个多线程并发的存储引擎,内部的读写都是用多线程来实现的。
- 一致性非锁定行读锁:InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据。
- InnoDB锁分为三类:
- Record Lock:单个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next-Key Lock:范围锁并且锁定该记录
- InnoDB锁使用不当会带来三种问题:
- 丢失更新:两个会话同时更新一条记录,可能导致其中一个会话的更新丢失。
- 脏读:脏数据:一个事务可以读到另一个事务中未提交的改变,不符合数据库的隔离性,在读未提交的隔离性下,会发生脏读
- 不可重复读:一个事务两次读取的数据不一致,另一个事务在两次读取之间改变了数据