mysql数据库的锁机制
- 0、锁
- 1、MySQL中的锁
-
- 1.1、锁的类型
- 1.2、锁的粒度
- 表锁
- 行锁
- 页锁
- 总结
- 2、InnoDB中的锁
-
- 意向锁
- 行锁的算法
- 一致性非锁定读
- 一致性锁定读
- 3、锁带来的问题
- 4、死锁
0、锁
锁,在计算机中,是协调多个进程或县城并发访问某一资源的一种机制。
在数据库当中,除了传统的计算资源(CPU、RAM、I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源。如何保证数据并发访问的一致性、有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素。
经典原文之谈谈MySQL的锁
1、MySQL中的锁
mysql中的锁有lock和latch两种概念:
Latch成为闩(shuan)锁(轻量级的锁),工作在内存InnoDB缓冲池中,用来给缓冲池中的热点数据(链表)进行加锁,锁定时间要求非常短。若时间长,则应用性能会非常差;
在InnoDB中,Latch分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
Latch更详细的讲解可以参考:
LATCH(闩锁)——易产生的问题以及原因分析
关于MySQL latch争用深入分析与判断
Lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。
并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。
1.1、锁的类型
对数据的操作其实只有两种,也就是读和写,InnoDB 实现了标准的行级锁,也就是共享锁(Shared Lock)和互斥锁(Exclusive Lock)。
共享锁(读锁),允许事务读一行数据。
排他锁(写锁,又叫互斥锁),允许事务删除或更新一行数据。
因为共享锁代表了读操作、互斥锁代表了写操作,所以我们可以在数据库中并行读,但是只能串行写,只有这样才能保证不会发生线程竞争,实现线程安全。
1.2、锁的粒度
Lock锁根据粒度主要分为表锁、页锁和行锁。不同的存储引擎拥有的锁粒度都不同,如MyISAM的表锁,InnoDB的行锁;
表锁
表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。
表锁的好处:表锁机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。
所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。
表锁的缺点:锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并发度大打折扣。
表锁使用场景:使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
行锁
行级锁定最大的特点就是锁定对象的粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。
行锁优点:由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
行锁缺点:虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。
此外,行级锁定也最容易发生死锁。
行锁使用场景:使用行级锁定的主要是InnoDB等事务型存储引擎。
页锁
页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。
页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。
另外,页级锁定和行级锁定一样,会发生死锁。
在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。
页锁使用场景:使用页级锁定的主要是BerkeleyDB存储引擎
总结
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
2、InnoDB中的锁
意向锁
上节提到InnoDB 支持多种粒度的锁,也就是行锁和表锁。为了支持多粒度锁定,InnoDB 存储引擎引入了意向锁(Intention Lock)。
那什么是意向锁呢?我们在这里可以举一个例子:如果没有意向锁,当已经有人使用行锁对表中的某一行进行修改时,如果另外一个请求要对全表进行修改,那么就需要对所有的行是否被锁定进行扫描,在这种情况下,效率是非常低的;
不过,在引入意向锁之后,当有人使用行锁对表中的某一行进行修改之前,会先为表添加意向互斥锁(IX),再为行记录添加互斥锁(X),在这时如果有人尝试对全表进行修改就不需要判断表中的每一行数据是否被加锁了,只需要通过等待意向互斥锁被释放就可以了。
与上一节中提到的两种锁的种类相似的是,意向锁也分为两种:
意向共享锁(IS):事务想要在获得表中某些记录的共享锁,需要在表上先加意向共享锁。
意向互斥锁(IX):事务想要在获得表中某些记录的互斥锁,需要在表上先加意向互斥锁。
意向锁其实不会阻塞全表扫描之外的任何请求,它们的主要目的是为了表示是否有人请求锁定表中的某一行数据。
行锁的算法
InnoDB存储引擎有3种行锁的算法,其分别是:
Record Lock:单个行记录上的锁。
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。
Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。
Gap Lock的作用是为了阻止多个事务将记录插入到同一个范围内,设计它的目的是用来解决Phontom Problem(幻读问题)。在MySQL默认的隔离级别(Repeatable Read)下,InnoDB就是使用它来解决幻读问题。
mysql间隙锁链接
幻读是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL可能会返回之前不存在的行,也就是第一次执行和第二次执行期间有其他事务往里插入了新的行。
Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。
一致性非锁定读
一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(MVCC)的方式来读取当前执行时间数据库中行的数据。如果读取的这行正在执行DELETE或UPDATE操作,这时读取操作不会向XS锁一样去等待锁释放,而是会去读一个快照数据。
在事务隔离级别RC和RR下,InnoDB存储引擎使用非锁定的一致性读。然而对于快照数据的定义却不同,在RC级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在RR级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。
一致性锁定读
前面说到,在默认隔离级别RR下,InnoDB存储引擎的SELECT操作使用一致性非锁定读。但是在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作。
SELECT … FOR UPDATE (X锁)
SELECT … LOCK IN SHARE MODE (S锁)
3、锁带来的问题
通过锁定机制可以实现事务隔离性要求,使得事务可以并发的工作。锁提高了并发,但是却会带来潜在的问题。
InnoDB存储引擎在RR级别就已经解决了所有问题,但是它和Serializable的区别在哪里呢?
区别就在于RR级别还存在一个丢失更新问题,而SERIALIZABLE无论对于查询还是更新都会进行锁定操作。
4、死锁
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。