Mysql中的锁

全局锁

flush tables with read lock

执行后,整个数据库就处于只读状态了,这时其他线程执行以下操作,都会被阻塞:

  • 对数据的增删改操作,比如 insert、delete、update等语句;
  • 对表结构的更改操作,比如 alter table、drop table 等语句。

全局锁主要应用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

但是加上全局锁后,整个数据库都是只读状态,无法更新数据。

因此,一般也不建议用全局锁进行全库逻辑备份。InnoDB支持可重复读的隔离级别,那么在备份数据库之前可以先开启事务,会先创建 Read View,然后整个事务执行期间都在用这个 Read View,而且由于 MVCC 的支持,备份期间业务依然可以对数据进行更新操作。因为在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。

表级锁

innodb的 表锁,分成意向共享锁 和意向独占锁,表锁是innodb引擎⾃动加的,不⽤⾃⼰加。

  • 意向共享锁:加共享⾏锁时,必须先加意向共享锁;
  • 意向独占锁 :给某⾏加独占锁时,必须先给表加意向独占锁。

insert、update、delete,innodb会⾃动给那⼀⾏加⾏级独占锁。
普通的select啥锁都不加。但是可以手动加锁

//先在表上加上意向共享锁,然后对读取的记录加共享锁
select ... lock in share mode;

//先在表上加上意向独占锁,然后对读取的记录加独占锁
select ... for update;

行级锁

InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。

普通的 select 语句是不会对记录加锁的,因为它属于快照读。如果要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为当前读

//对读取的记录加共享锁
select ... lock in share mode;

//对读取的记录加独占锁
select ... for update;

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;
  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;(左开右开区间)
  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。(左开右闭区间)

Record Lock

Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的:

  • 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);
  • 当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。

总而言之,当一个事务对一条记录加了读锁后,其它事务也可读,但不可写;当一个事务对一条记录加了写锁后,其它事务不可读也不可写。

Gap Lock

Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

Next-Key Lock

Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

假设,表中有一个范围 id 为(3,5] 的 next-key lock,那么其他事务即不能插入 id = 4 记录,也不能修改 id = 5 这条记录

乐观锁和悲观锁

乐观锁和悲观锁都是针对读(select)来说的。

乐观锁

就是非常乐观,做什么事都往好处想; 对于数据库操作,就认为每次操作数据的时候都认为别的操作不会修改,所以不会加锁,而是通过一个类似于版本的字段来标识该数据是否修改过,在执行本次操作前先判断是否修改过,如果修改过就放弃本次操作重新再来。乐观锁适用于多读的应用类型,这样可以提高吞吐量;乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

  • 乐观锁不是数据库自带的,需要自己去实现。
  • 乐观锁是指 更新数据库时认为操作不会导致冲突,在操作数据时不加锁,而在进行更新后,再去判断是否有冲突了。

通常实现是这样的:在表中的数据进行更新时,先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。

  • 先查询出那条记录,获取出version字段。
  • 如果要对那条记录进行更新操作,则先判断此刻version的值是否与刚刚查询出来时的version的值相等。
  • 如果相等,则说明这段期间没有其他程序对其进行操作,则可以执行更新,将version字段的值加1。
  • 如果version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

悲观锁

就是非常悲观,做什么事都觉得不好;悲观锁就是在操作数据时,每次操作数据数据都会认为别的操作会修改当前数据,所以在进行每次操作时都要加锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。

悲观锁涉及到的另外两个锁:就是共享锁与排它锁(select **** for update)。
共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

因此,悲观锁在未通过索引条件检索数据时,会全表扫描,于是就会对所有记录加上 next-key 锁(记录锁 + 间隙锁),相当于把整个表锁住了。导致其他程序不允许“加锁的查询操作”,影响吞吐。故如果在查询居多的情况下,推荐使用乐观锁。

  • 加锁的查询操作:加过排他锁的数据行在其他事务中是不能修改的,也不能通过for update或lock in share mode的加锁方式查询,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
  • 乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入居多,对吞吐要求不高,可使用悲观锁。

也就是一句话:读用乐观锁,写用悲观锁。

小结

悲观锁做事比较悲观,它认为同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁。

那相反的,如果多线程同时修改共享资源的概率比较低,就可以采用乐观锁。

乐观锁做事比较乐观,它假定冲突的概率很低,它的工作方式是:先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值