第六章 锁
一、实验环境
宿主机系统:windows7
虚拟机:OracleVMVirtualBox
Linux:ubuntukylin-14.04.1-amd64.iso
jdk:1.7.0_101
mysql:5.7.12
书上的mysql版本:5.6.6
二、什么是锁
锁是数据库熊区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访问,提供数据的完整性和一致性。
不同数据库对锁的实现也不同。
innoDB锁的实现和oracle类似,提供一致性的非锁定读、行级锁支持。航迹锁没有相关额外的开销,并可以同时得到并发性和一致性。
三、lock和latch
latch:闩锁(轻量级锁),要求锁定的时间必须非常短。若时间长,则性能差。
innodb中可分为:mutex(互斥锁)、rwlock(读写锁)。目的是保证并发线程操作临界资源的正确性,通常没有死锁检测机制。
show engine innodb mutex
lock:对象是事务,用来锁定数据库中的对象,如表,页,行。并且一般lock的对象仅在事务commit或rollback后进行释放。有死锁机制。
三、innodb中的锁
共享锁(S Lock):允许事务读一行数据
排它锁(XLock):允许事务删除或更新一行数据
一致性非锁定读:通过行多版本控制的方式来读取当前执行时间数据库中行的数据。如果所读取的行正在进行update或delete操作,这是读取操作不会因此去等待行上X锁的释放,而是会读取行的快照,通过undo段来完成。
read committed事务隔离级别,总是读取行的最新版本,如果行被锁定,则读取该行版本的最新快照。
replication read 事务隔离级别,总是读取事务开始时的行数据。
一致性锁定读:
select ... for update:对读取的行加一个X锁。
select ... lock in share mode:对读取的行加一个S锁。
事务提交后,锁释放。
自增长与锁:
得到自增长计数器的值:select max(auto_inc_col) from t forupdate;
锁不是在事务完成后释放,而是完成对自增长值插入的SQL语句后立即释放。
insert ...select 大数据量插入会影响性能。
重要参数:innodb_autoinc_lock_mode来控制自增长模式。可选值0,1,2
外键和锁:
如果没有显式的给外键列加索引,innodb会自动给其加一个索引。可以避免表锁。
外键的插入或者更新,首先要查询父表中的记录,采用的是select ... lock in share mode 主动对父表加一个S锁。如果父表已加X锁,则子表上的操作会被阻塞。
锁的算法:
行锁的三种算法:
record lock:单个行记录上的锁。会去锁住索引记录,如果没有设置索引,innodb会使用隐式主键来进行锁定。
gap lock:间隙锁,锁定一个范围,但是不包含记录本身。阻止多个事务将记录插入到同一范围内。
next-key lock :锁定一个范围,且锁定记录本身,是前两种相加。对于行的查询,采用此算法
默认的事务隔离级别即repeatable read 下,采用next-keylocking 来避免phantom problem(幻像问题即不可重复读)。即:在同一事务下,连续执行两次同样的sql语句可能导致不同的结果,第二次的sql语句可能会返回之前不存在的行。
锁问题(3个):
1.脏读:指的是在不同的事务下,当前事务可以读到另外事务未提交的数据,即可以读到脏数据。
脏数据:事务对缓冲池中行记录的修改,还未提交。
脏读发生的条件是:需要事务的隔离级别为read uncommitted。
2.不可重复读:在一个事务内多次读取同一数据集合。这个事务未结束时,另外一个事务页访问该同一数据集合,并做了DML操作。这样,在一个事务内两次读到的数据不一样。
与脏读的区别:脏读读到的是未提交的数据,不可重复读,读到的是已经提交的数据。
不可重复读发生的条件:事务隔离级别为read committed。
innoDB的默认事务隔离级别是read repeatable 采用next-key lock算法,避免了不可重复读现象。
3.丢失更新:
一个事务的更新会被另外一个事务的更新操作锁覆盖,导致数据不一致。
在当前数据库任何隔离级别下,都不会导致丢失更新的问题。但是逻辑意义上有可能。
1)事务T1查询一行数据,放入本地内存,并显示给一个终端用户User1
2)事务T2查询一行数据,放入本地内存,并显示给一个终端用户User2
3)User1修改这行记录,并提交。
4)User2修改这行记录,并提交。
如此,User1提交的更新操作丢失了。
要避免该问题发生,需让事务在这种情况下的操作变成串行化,而不是并行操作。
步骤1中,对用户读取的记录加上一个排他X锁(select ... for update),步骤2也加X锁(select ... for update),则必须等到步骤3完成后才能完成步骤4.
阻塞:一个事务中的锁需要等待另一个事务中的锁释放它所占的资源,就是阻塞。
参数:innodb_lock_wai_timeout来控制等待时间。 动态参数
参数:innodb_rollback_on_timeout是否在等待超时时执行回滚操作,默认为off代表不回滚。静态参数。
死锁: 两个或两个以上的事务在执行的过程中,因为夺锁资源而造成的一种互相等待的现象。
解决方法,不要有等待,则1、回滚,2超时。
innodb采用主动的死锁检测方式:wait-for-graph(等待图)