Mysql----锁

什么是锁

锁是数据库系统区别于文件系统的一个关键特性,锁机制是用来管理对共享资源的并发访问,是为了保证数据库中数据的一致性,使共享资源在被并发访问时变得有序而设计的一种规则。

InnoBD中的行锁

InnoDB存储引擎会在行级别上对表数据上锁。实现了两种标准的行级锁:
共享锁(shared lock),也叫作读锁(read lock),允许事务读一行数据。
排他锁(exclusive lock),也叫作写锁(write lock),允许事务删除或更新一行数据。

注意:一个事务想要对数据行进行操作,前提是先拿到锁。举一个睡觉的例子(方便理解,这里指写锁):一个房间只有一把锁,我想要进去睡觉,只有拿到锁,把房间锁起来之后,才能踏实的睡觉,这时其他人是进不来的,我不会受到打扰,当我休息完,把锁再给下一个人,他再开始休息。

共享锁(读锁)

一个事务获取了一个数据行的读锁,其他事务也能获得该行对应的读锁,即一个事务在读取一行数据时,其他事务也可以读,是共享的,所以读锁也叫做共享锁,但是其他事务不能获得写锁,也就不能对该行数据进行增删改的操作,因为当前事务正在读数据,如果其他事务要对该行数据进行操作,就会产生数据不一致的现象。
读锁是共享的,或者说是相互不阻塞的,多个事务可以在同一时刻同时读取同一个资源。

排他锁(写锁)

一个事务获取了一个数据行的写锁之后,其他事务都不能再获取该行的任意类型的锁(包括读锁和写锁),只有这样,才能确保在给定的时间里,只能有一个用户能执行写入,并防止其他用户读取正在写入的同一资源。下面举个例子:
在这里插入图片描述
上面的例子中,当执行事务B的update语句时,事务B会被阻塞,直到事务A执行commit之后,事务B才能继续执行。事务A持有两个记录的行锁(id为1 和id为2 两行),都是在commit的时候才释放。也就是说,在InnoDB事务中,行锁是在需要的时候才加上的,但不是不需要了就立刻释放,而是等到事务结束时才释放(事务A的第一条SQL执行完之后,不会释放行锁,必须当前事务执行完毕之后才会释放),这个就是两阶段锁协议。
两阶段锁协议指的是加锁和解锁是两个阶段,事务A对两行数据进行加锁,这两行数据只能在同一个阶段完成,在这个阶段,不允许对已经加锁的数据项进行解锁操作,解锁也只能在最后一个阶段执行。在一个事务内,加锁和解锁的操作不能交叉执行。

共享锁与排他锁的关系

兼容性

如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为仅仅读取并没有改变行r的数据,这种情况叫做锁兼容(Lock Compatible),但是如果有其他事务T3想要获取行r的排他锁,则必须等待事务T1、T2释放行r上的共享锁,这种现象叫做锁不兼容,下图展示了共享锁(S)和排他锁(X)的兼容性。
在这里插入图片描述
从上图可以看出排他锁(X)是与任何锁都不兼容,兼容是指对同一行记录锁的兼容情况。

一致性非锁定读

一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据,即使读取的行正在进行更新或者删除操作,这时读取操作也不会因此去等待行锁的释放,这样大大提高了读取的效率。
这时读的就不会是正在操作的这一行数据,而是该行的一个快照数据。快照数据是指该行之前版本的数据,该实现是通过undo段来完成,而undo用来在事务中回滚数据,这种技术称为行多版本技术,由此带来的并发控制,称之多版本并发控制(MVCC),因此快照数据本身是没有任何额外的开销,此外,读取快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改操作。
可见,非锁定读机制极大地提高了数据库的并发性,在InnoDB存储引擎的默认设置下,这也是默认的读取方式,即读取不会占用和等待表上的锁。
在事务隔离级别为读已提交和可重复读(InnoDB默认隔离级别)下,InnoDB存储引擎使用一致性非锁定读。然而二者对于快照数据的定义却不相同。在读已提交事务隔离级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据。而在可重复读事务隔离级别下,总是读取事务开始时的行数据版本。

一致性锁定读

如果用户需要显示地对数据库读取操作进行加锁以保证数据逻辑的一致性,也就是必须等待该行数据的排他锁被释放再去读取,而这就要求数据库支持加锁语句,即使是对Select 的只读操作,InnoDB存储引擎对于select 语句支持两种一致性的读操作:

SELECT ··· FOR UPDATE
SELECT ···LOCK IN SHARE MODE

SELECT ··· FOR UPDATE 对读取的行记录加一个排他锁,其他事务不能对已锁定的行加上任何锁。SELECT ···LOCK IN SHARE MODE 对读取锁的行记录加一个共享锁,其他事务可以向被锁定的行加共享锁,但是如果加排他锁,就会被阻塞。
需要注意的是这两条语句必须放在事务里面,必须随着事务的提交而释放。

表级锁

Mysql中的表级别锁有两种,一种是表锁,一种是元数据锁(meta data lock MDL)。

表锁

表锁是Mysql中最基本的锁策略,并且是开销最小的策略,它会锁整张表,一个用户对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作,只有没有写锁时,其他读取的用户才能获得读锁,读锁之间是不相互阻塞的,也就是行锁和表锁差别是粒度不一样,行锁锁的是一行数据,表锁锁的是一张表的数据。

InnoDB存储引擎支持多粒度(granular)锁定,这种锁定允许事务在行级锁和表级锁同时存在,为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁(Intention Lock),意向锁是表级别的锁,它将锁定的对象分为多个层次,意向锁意味着希望在更细粒度(fine granularity)上进行加锁。
将上锁的对象想象成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁。那么首先需要对粗粒度的对象上锁。如下图,如果需要对页上的记录r 上排他锁,那么分别需要对数据库A、表、页上意向锁,最后再对记录r上排他锁。若其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。举例来说:如果现在要对记录r加排他锁,在这之前有事务对表1加上了读表锁,因为表1已经被加上了读表锁,所以对记录r加排他锁会失败,必须等待事务对表1 的操作完成。
在这里插入图片描述
意向锁为表级别的锁,有以下两种类型:
1、意向共享锁(Intention Share Lock,IS Lock),事务想要获取一张表中某几行的S锁,但是获取之前,先要获取该表的IS锁。
2、意向排他锁(Intention exclusive Lock,IX Lock),事务想要获取一张表中某几行的X锁,但是获取之前,先要获取该表的IX锁。

意向锁之间是相互兼容的。表级别意向锁与行级别锁的兼容性如下:
在这里插入图片描述

元数据锁(MDL)

在Mysql 5.5版本中引入了MDL,用于保证表中元数据的信息,也就是表结构的信息。MDL不需要显示的使用,在访问一个表的时候会被自动加上。MDL作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。因此,在MySQL 5.5版本中引入了MDL,当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。
注意:
1、读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
2、读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

死锁

死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种相互等待的现象。如下图:

在这里插入图片描述
这时候,事务A在等待事务B释放id=2的行锁,而事务B在等待事务A释放id=1的行锁。 事务A和事务B在互相等待对方的资源释放,就是进入了死锁状态。

解决死锁的策略:
1、一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。
2、另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数innodb_deadlock_detect设置为on,表示开启这个逻辑。

锁升级

锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说,数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁。如果在数据库的设计中认为锁是一种稀有资源,而且想要避免锁的开销,那数据库中会频繁出现锁升级的现象,这种升级保护了系统资源,防止系统使用太多的内存来维护锁,在一定程度上提高了效率。

SQL Server在一下两种情况可能发生锁升级:
1、由一句单独的sql语句在一个对象上持有的锁的数量超过了阈值,默认的阈值为5000,如果是不同的对象,则不会发生锁升级。
2、锁资源占用的内存超过了激活内存的40%时就会发生锁升级。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值