MySQL锁
MySQL为了解决并发、数据安全的问题,使用了锁机制。
MySQL锁的分类
- 按锁的粒度分:行锁、页锁、表锁
- 按锁的使用分:共享锁(读锁)、排它锁(写锁)
- 按锁的思想分:乐观锁、悲观锁
表锁
表锁是锁粒度最大的一种锁,对当前操作整张表加锁,操作简单,资源消耗比较少,加锁快,不会出现死锁,因锁的粒度大,触发锁的冲突高,并发低, MyISAM和InnoDB都实现了表级锁
行锁
行锁是锁粒度最小的一种锁,对当前操作的行加锁。行级锁大大降低数据库操作的冲突。其加锁粒度最小,并发度最高, 但是加锁的开销大, 加锁慢,会出现死锁。InnoDB支持行级锁,包括几种:
- Record Lock:对索引加锁,锁定符合条件的行。其他事务不能修改,删除加锁项。
- Gap Lock:间隙锁,对索引的间隙加锁, 锁定记录的范围,不包括索引项本身, 其他事务不能在锁范围内插入数据,避免新的事务新增幻影行。
- Next-key Lock:锁定索引项本身和索引范围。即Record Lock和Gap Lock的结合。可解决幻读问题。
1、innodb对于行的查询使用Next-key Lock
2、Next-locking keying为了解决Phantom Problem幻读问题
3、当查询的索引含有唯一属性时,将Next-key Lock降级为Record key
4、Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
5、有两种方式显式关闭Gap锁:(除了外键约束和唯一性检查外,其余情况仅使用Record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1
页锁
页锁:MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。页级进行了折衷,一次锁定相邻的一组记录。BDB支持页级锁。开销和加锁时间界于表锁和行锁之间,会出现死锁。锁定粒度界于表锁和行锁之间,并发度一般。
共享锁
共享锁(Share Lock,简记S锁)又称读锁,事务T对数据对象A加S锁,事务T可以读A但不能修改A,其他事务可对A加S锁,但不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
排它锁
排它锁(Exclusive lock,简记X锁)又称写锁,若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。
共享锁和排它锁之间的区别
1、共享锁(S锁):如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁。获取共享锁的事务只能读数据,不能修改数据。
2、排它锁(X锁):如果事务T对数据A加上排它锁后,则其他事务不能再对A加任任何类型的封锁。获取排它锁的事务既能读数据,又能修改数据。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
悲观锁
悲观锁,指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)
悲观锁的流程
对记录修改前,先加上排它锁(Exclusive lock),如果加锁失败,表示该记录正在被修改,那么当前查询可能要等待或者抛出异常。如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。其间如果有其他对该记录做修改或加排它锁的操作,都会等待我们解锁或直接抛出异常。
乐观锁
乐观锁(Optimistic Locking)相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
乐观锁和悲观锁的优缺点
悲观锁采取先取锁在访问的策略,保证了数据的安全性,但是在效率方面,加锁增加了额外的开销,增加死锁机会,降低了并发性。
乐观锁相信事务的竞争概率比较小,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。