mysql锁


内容来源:https://blog.csdn.net/qq_40378034/article/details/90904573
根据加锁的范围,mysql的锁大致可以分成全局锁、表级锁、行锁三类

1、全局锁

全局锁就是对整个数据库实例加锁。MySQL提供了一个全局加锁的方法,命令是Flush tables with read lock。当需要让整个库处于只读状态时,可以使用这个命令,之后其他线程的以下语句会被阻塞:

  1. 数据增、删、改(insert、delete、update)
  2. 数据定义语句(建表、修改表结构等)
  3. 更新类事务的提交语句
    全局锁的使用场景是做全库逻辑备份,也就是把整个库每个表都select出来存成文本。
    但是让整个库都只读,可能出现以下问题:
  4. 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
  5. 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的binlog,会导致主从延迟

在可重复读隔离级别下开启一个事务能够拿到一致性视图

官方自带的逻辑备份工具是mysqldump。当mysqldump使用参数–single-transaction的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正常更新的。single-transaction只适用于所有的表使用事务引擎的库

1.既然要全库只读,为什么不适用set global readonly=true的方式?

  • 在有些系统中,readonly的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库。因此修改global变量的方式影响面更大
  • 在异常处理机制上有差异。如果执行Flush tables with read lock命令之后由于客户端发生异常断开,那么MySQL会自动释放这个全局锁,整个库回到可以正常更新的状态。而将整个库设置为readonly之后,如果客户端发生异常,则数据库会一直保持readonly状态,这样会导致整个库长时间处于不可写状态,风险较高

2、表级锁

MySQL里面表级锁有两种:

  1. 表锁
  2. 元数据锁(meta data lock / MDL)

2.1、表锁

语法是lock tables ... read/write可以用unlock tables主动释放锁,也可以在客户端断开的时候自动释放。

lock tables语法除了会限制别的线程的读,也限制了本线程接下来的操作对象。

如果在某个线程A执行lock tables t1 read, t2 write;这个语句,则其他线程写t1、读写t2的语句都会被阻塞。同时,线程A在执行unlock tables之前,也只能执行读t1、写t2的操作,连写t1都不允许

2.2、MDL

MDL不需要显示的使用,在访问一个表的时候会被自动加上。

MDL的作用是,保证读写的正确,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做了变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定不行。

MySQL5.5版本引入了MDL:

  1. 当对一个表做增删改查操作的时候,加MDL读锁;
  2. 当对表结构变更时,加MDL写锁;
  3. 读锁之间不互斥,因此可以有多个线程同时对一张表增删改查;
  4. 写锁是互斥的,用来保证变更表结构操作的安全性。因此如果有两个线程同时给一个表加字段,其中一个要等待另一个执行完才能开始执行。

给一个表加字段,或者修改字段,或者加索引,需要扫描全表的数据。在对大表操作的时候,需要特别小心,以免对线上服务造成影响。

事务中的MDL锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。

1.如果安全地给小表加字段?

首先要解决长事务,事务不提交,就会一直占着DML锁。在MySQL的information_schema库的innodb_trx表中,可以查到当前执行的事务。如果要做DDL变更的表刚好有长事务在执行,要考虑先暂停DDL,或者kill掉这个长事务

2.如果要变更的表是一个热点表,虽然数据量不大,但是上面的请求很频繁,而又不得不加个字段,该怎么做?

在alter table语句里面设定等待时间,如果在这个指定的等待时间里面能够拿到MDL写锁最好,拿不到也不要阻塞后面的业务语句,先放弃。之后再通过重试命令重复这个过程

例子

在这里插入图片描述

  1. session A先启动,这时候会对表t加一个MDL读锁。
  2. 由于session B需要的也是MDL读锁,因此可以正常执行。
  3. 之后sesession C会被blocked,是因为session A的MDL读锁还没有释放,
  4. session C需要MDL写锁,因此只能被阻塞。
  5. 如果只有session C自己被阻塞还没什么关系,但是之后所有要在表t上新申请MDL读锁的请求也会被session C阻塞。所有对表的增删改查操作都需要先申请MDL读锁,就都被锁住,等于这个表现在完全不可读写了

3、行锁

MySQL的行锁是在引擎层由各个引擎自己实现的。但是不是所有引擎都支持行锁,比如MyISAM引擎就不支持行锁。

行锁就是针对数据表中行记录的锁。比如事务A更新了一行,而这时候事务B也要更新同一行,则必须等待事务A的操作完成后才能进行更新。

3.1、两阶段锁协议

在这里插入图片描述
事务A持有的两个记录的行锁都是在commit的时候才释放的,事务B的update语句会被阻塞,直到事务A执行commit之后,事务B才能继续执行

在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议

如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放

假设要实现一个电影票在线交易业务,顾客A要在影院B购买电影票。业务需要涉及到以下操作:

1.从顾客A账户余额中扣除电影票价

2.给影院B的账户余额增加这张电影票价

3.记录一条交易日志

为了保证交易的原子性,要把这三个操作放在一个事务中。如何安排这三个语句在事务中的顺序呢?

如果同时有另外一个顾客C要在影院B买票,那么这两个事务冲突的部分就是语句2了。因为它们要更新同一个影院账户的余额,需要修改同一行数据。根据两阶段锁协议,所有的操作需要的行锁都是在事务提交的时候才释放的。所以,如果把语句2安排在最后,比如按照3、1、2这样的顺序,那么影院账户余额这一行的锁时间就最少。这就最大程度地减少了事务之间的锁等待,提升了并发度

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值