锁(locking)的机制是区分数据库系统和文件系统的一个关键特征。
锁机制用于管理对共享资源的并发访问。InnoDB存储引擎会在行级别上对表数据上锁,这固然不错。不过InnoDB存储引擎也会在数据库内部其他多个地方使用锁,从而允许对多种不同资源提供并发访问。例如:操作缓冲池中的LRU(Least Recently Used的缩写,即最近最少使用)列表,删除,添加,移动LRU列表中的元素,为了保证一致性,必须有锁的介入。数据库系统使用锁是为了对共享资源进行并发访问。保证数据的完整性和一致性。
一、各数据库不同或存储引擎不同,锁实现也不同
对于MyISAM引擎,其锁是表锁设计,并发情况下读没有问题,但是并发插入时的性能就要差一些了。
对于Microsoft SQL Server数据库,在Microsoft SQL Server2005版之前其都是页锁的,相对表锁的MyISAM引擎来说,并发性能有所提升。页锁容易实现,然而对于热点数据页的并发问题依然不能为例。
到了2005版本,Microsoft SQL Server开始支持乐观并发和悲观并发,在乐观并发下开始支持行级锁,但是其实现方式与InnoDB存储引擎的实现方式完全不同。在Microsoft SQL Server下,锁是一种稀有的资源,锁越多开销就越大,因此它会有锁升级,这种情况下,行锁会升级到表锁,这时并发的性能又回到了从前。
Oracle和InnoDB存储引擎锁的实现类似,提供一致性的非锁定读、行级锁支持。行锁没有额外相关开销,并可以同时得到并发性和一致性。
二、lock 与 latch
在数据库中,lock与latch都可以被成为“锁”,但是二者有着截然不同的含义。
latch一般称为闩(shuan)锁(轻量级的锁),因为其要求锁定的时间必须非常短,若持续的时间长,则应用的性能会非常差。在InnoDB存储引擎中,latch又可以被分为mutex(互斥锁)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
lock的对象是事务,用来锁定的是数据库中的对象,如 表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离界别释放的时间可能不同)。此外,lock正如大多数数据库中一样,是有死锁机制的。
(
当出现死锁以后,有两种策略:
- 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。
- 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
)
lock 与latch的对比
lock | latch | |
对象 | 事务 | 线程 |
保护 | 数据库内容 | 内存数据结构 |
持续时间 | 整个事务过程 | 临界资源 |
模式 | 行锁、表锁、意向锁 | 读写锁,互斥锁 |
死锁 | 通过waits-for graph、time out等机制进行死锁检测与处理 | 无死锁检测与处理机制,仅通过应用程序加锁的顺序(lock leveling)保证无死锁的情况发生 |
存在于 | lock Manager的哈希表中 | 每个数据结构的对象中 |
show engineinnodb mutex; 查看latch信息
三、InnoDB引擎中的锁
3.1 锁的类型
InnoDB存储引擎实现了如下两种标准的行级锁:
共享锁(S Lock),允许事务读一行数据。 select.....lock in share mode
排他锁(X Lock),允许事务删除或更新一行数据 select .......for update
锁兼容:如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即执行获取行r的共享锁,因为读取并没有改变行r的数据。
锁不兼容:但若有其他事务T3想获得r的排他锁,则其必须等待事务T1 T2释放行r上的共享锁。
排他锁和共享锁的兼容性
X | S | |
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
可以发现,X锁与任何的锁都不兼容,而S锁仅与S锁兼容。
此外,InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级上和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention Lock)。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度(fine granularity)上进行加锁。
若将上锁的对象堪称一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先需要对粗粒度的对象上锁。如下图:如果需要对页上的记录r上X锁,那么需要分别对数据库A、表、页上意向锁IX,最后对记录r上X锁。若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的的主要是为了在一个事务中揭示下一行将被请求的锁类型。
1)意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
2)意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁
IS | IX | S | X | |
IS | 兼容 | 兼容 | 兼容 | 不兼容 |
IX | 兼容 | 兼容 | 不兼容 | 不兼容 |
S | 兼容 | 不兼容 | 兼容 | 不兼容 |
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
检查MySQL锁请求状态 show engine innodb status
information_schema.INNODB_TAX 描述当前事务状态
information_schema.INNODB_LOCKS 描述当前锁状态
information_schema.INNODB_LOCK_WAITS 描述事务和锁的对应关系
3.2 一致性非锁定读
3.3 一致性锁定读
3.4 自增长与锁
3.5 外键和锁
四、锁的算法
4.1 行锁的三种算法
4.2 解决 Phantom Problem (幻读问题)
五、锁问题
5.1 脏读
5.2 不可重复度
5.3 幻读
六、阻塞
七、死锁
八、锁升级