同一时刻,多个并发用户同时访问同一个数据时,仅仅通过事务机制,无法保证多个用户同时访问同一个数据的数据一致性,有必要引入锁机制实现MySQL的并发访问,锁机制是实现多用户并发访问的基石。
9.6.1 锁机制的必要性
并发用户访问同一数据,锁机制可以避免数据不一致问题的发生。如图9.21所示。
图9.21 锁机制
9.6.2 MySQL锁机制的基础知识
1.锁的粒度
锁的粒度是指锁的作用范围。锁的粒度可以分为服务器级锁(server-level locking)和存储引擎级锁(storage-engine-level locking)。
MyISAM存储引擎支持表锁。InnoDB存储引擎支持表锁以及行级锁。
2.隐式锁与显式锁
MySQL锁分为隐式锁和显式锁。MySQL自动加锁称为隐式锁。数据库开发人员手动加锁称为显式锁。
3.锁的类型
锁的类型包括读锁(read lock)和写锁(write lock),其中读锁也称为共享锁,写锁也称为排他锁或者独占锁。读锁允许其他MySQL客户机对数据同时读,但不允许其他MySQL客户机对数据任何写,如图9.22所示。
图9.22 读锁(read lock)和写锁(write lock)
写锁不允许其他MySQL客户机对数据同时读,也不允许其他MySQL客户机对数据同时写,原理如图9.23所示。
图9.23 写锁约束
4.锁的钥匙
多个MySQL客户机并发访问同一个数据时,如果MySQL客户机A对该数据成功地施加了锁,那么只有MySQL客户机A拥有这把锁的“钥匙”,也就是说:只有MySQL客户机A能够对该锁进行解锁操作。
5.锁的生命周期
锁的生命周期是指在同一个MySQL服务器连接内,对数据加锁到解锁之间的时间间隔。
9.6.3 MyISAM表的表级锁
任何针对MyISAM表的查询操作或者更新操作,都会隐式地施加表级锁,隐式锁的生命周期非常短暂,且不受数据库开发人员的控制。
有时需要延长表级锁的生命周期,MySQL为数据库开发人员提供了显示地施加表级锁以及显示地解锁的MySQL命令,原理如图9.24所示。
图9.24 表级锁原理
注意事项:(www.xing528.com)
➢ read与write选项的功能在于施加表级读锁还是表级写锁。
➢ MySQL客户机A使用lock tables命令可以同时为多个表施加表级锁(包括读锁或者写锁),并且加锁期间,MySQL客户机A不能对“没有锁定的表”进行更新及查询操作,否则将抛出“表未被锁定”的错误信息。
➢ 如果需要为同一个表同时施加读锁与写锁,需要为该表起两个别名,以区分读锁与写锁。
➢ read local与read选项之间的区别在于:如果MySQL客户机A使用read选项为某个MyISAM表施加读锁,加锁期间,MySQL客户机A以及MySQL客户机B都不能对该表进行插入操作。如果MySQL客户机A使用read local选项为某个MyISAM表施加读锁,加锁期间,MySQL客户机B可以对该表进行插入操作,前提是新记录必须插入到表的末尾。
9.6.4 InnoDB表的行级锁
InnoDB提供了两种类型的行级锁,分别是共享锁(S)和排他锁(X),其中共享锁也叫读锁,排他锁也叫写锁。
在查询(select)语句或者更新(insert、update以及delete)语句中,为受影响的记录施加行级锁的方法也非常简单。
方法1:在查询(select)语句中,为符合查询条件的记录施加共享锁,语法格式如下:
select * from表where条件语句lock in share mode;
方法2:在查询(select)语句中,为符合查询条件的记录施加排他锁,语法格式如下:
select * from表where条件语句for update;
方法3:在更新(insert、update以及delete)语句中,InnoDB存储引擎将符合更新条件的记录自动施加排他锁(隐式锁)。即InnoDB存储引擎自动地为更新语句影响的记录施加隐式排他锁。
9.6.5 InnoDB表的意向锁
考虑如下场景:MySQL客户机A获得了某个InnoDB表中若干条记录的行级锁,此时MySQL客户机B出于某种原因需要向该表显式地施加表级锁(使用lock tables命令即可),MySQL客户机B为了获得该表的表级锁,需要逐行检测表中的行级锁是否与表级锁兼容,而这种检测需要耗费大量的服务器资源。
试想:如果MySQL客户机A获得该表若干条记录的行级锁之前,MySQL客户机A直接向该表施加一个“表级锁”(这个表级锁是隐式的,也叫意向锁),MySQL客户机B仅仅需要检测自己的表级锁与该意向锁是否兼容,无需逐行检测该表是否存在行级锁,就会节省不少服务器资源,如图9.25所示。
图9.25 意向共享锁(IS)和意向排他锁(IX)
MySQL提供了两种意向锁:意向共享锁(IS)和意向排他锁(IX)。
(1)意向共享锁(IS):向InnoDB表的某些记录施加行级共享锁时,InnoDB存储引擎会自动地向该表施加意向共享锁(IS)。也就是说:执行“select * from 表where条件语句lock in share mode;”后,InnoDB存储引擎在为表中符合条件语句的记录施加共享锁前,InnoDB会自动地为该表施加意向共享锁(IS)。
(2)意向排他锁(IX):向InnoDB表的某些记录施加行级排他锁时,InnoDB存储引擎会自动地向该表施加意向排他锁(IX)。也就是说:执行更新语句(例如insert、update或者delete语句)或者“select * from 表 where 条件语句 for update;”,InnoDB存储引擎在为表中符合条件语句的记录施加排他锁前,InnoDB会自动地为该表施加意向排他锁(IX)。
9.6.6 InnoDB行级锁与索引之间的关系
InnoDB表的行级锁是通过对“索引”施加锁的方式实现的这就意味着只有通过索引字段检索数据的查询语句或者更新语句,才可能施加行级锁;否则InnoDB将使用表级锁,使用表级锁势必会降低InnoDB表的并发访问性能。
9.6.7 使用间隙锁避免幻读现象
MySQL默认的事务隔离级别为repeatable read,保持事务的隔离级别repeatable read不变,利用间隙锁的特点,对查询结果集施加共享锁(lock in share mode)或者排他锁(for update),同样可以避免幻读现象,同时也不至于降低MySQL的并发访问性能。
9.6.8 死锁与锁等待
默认情况下,InnoDB存储引擎一旦出现锁等待超时异常,InnoDB存储引擎既不会提交事务,也不会回滚事务,这是十分危险的。一旦发生锁等待超时异常,应用程序应该自定义错误处理程序,由程序开发人员选择进一步提交事务,还是回滚事务。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。