【MySQL 45讲-6&7】第6&7讲 数据库的锁

总览图

在这里插入图片描述

06 | 全局锁和表锁

锁的设计初衷:处理并发问题

  • 锁的分类

    • 全局锁
    • 表级锁
    • 行锁

全局锁

  • 对整个数据库实例加锁

  • 加全局读锁的命令:
    Flush tables with read lock(FTWRL)

    • 数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。这些语句会被阻塞
  • 典型使用场景:全库逻辑备份

    • 即:把整个库的表都select出来存成文本

    • 用mysqldump使用参数-single-transaction来启动一个事务,拿到一致性视图,这样数据就能够正常更新。使用前提: 引擎要支持这个隔离级别。适用使用事务引擎的库。

    • InnoDB推荐使用-single-transaction

    • MyISAM不支持事务的引擎,因此只能使用FTWRL

    • 为何不使用set global readonly=true?

        1. readonly会被用来做其他逻辑,比如判断一个库是主库还是备库,所以修改global变量的影响太大了,不建议使用
        1. 异常处理机制上有差异。
        • 使用FTWRL,发生异常会自动释放锁
        • 使用readonly,发生异常不会释放锁

表级锁

  • 表锁

    • 语法:
      lock tables …… read/write

    • 释放锁

        1. unlock tables
        1. 客户端断开时自动释放
元数据锁MDL

metadata lock

在访问一个表时,MDL会被自动加上作用: 保证读写的正确性

MySQL 5.5 版本中引入了 MDL

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

给一个小表加一个字段,导致整个库挂了?

加字段需要扫描全表数据
事务中的MDL锁,在语句执行开始时申请,语句结束后并不会马上释放,而是等整个事务提交后再释放。

所以在互相等待中,产生了死锁,然后资源消耗完毕。数据库挂掉。

那么,如何给小表安全的加字段?

    1. 先解决长事务

    在做DDL变更时有长事务:可先暂停DDL或者kill掉长事务

    1. alter table中设定等待时间

    时间到了自动放弃,不阻塞后面的业务语句,之后DBA在重试任务。

举个栗子

  • 备份一般在备库上执行,再用-single-transaction做逻辑备份时,如果主库上的一个小表做了DDL(加了一列),会有什么现象?
分析
 Q1:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
		Q2:START TRANSACTION  WITH CONSISTENT SNAPSHOT;
		/* other tables */
		Q3:SAVEPOINT sp;
		/* 时刻 1 */
		Q4:show create table `t1`;
		/* 时刻 2 */
		Q5:SELECT * FROM `t1`;
		/* 时刻 3 */
		Q6:ROLLBACK TO SAVEPOINT sp;
		/* 时刻 4 */
		/* other tables */

在备份开始的时候,为了确保 RR(可重复读)隔离级别,再设置一次 RR 隔离级别 (Q1);

启动事务,这里用 WITH CONSISTENT SNAPSHOT 确保这个语句执行完就可以得到一个一致性视图(Q2);

设置一个保存点,这个很重要(Q3);

show create 是为了拿到表结构 (Q4),然后正式导数据 (Q5),回滚到 SAVEPOINT sp,在这里的作用是释放 t1 的 MDL 锁 (Q6)。

DDL 从主库传过来的时间按照效果不同,我打了四个时刻。题目设定为小表,我们假定到达后,如果开始执行,则很快能够执行完成。

参考答案如下:

如果在 Q4 语句执行之前到达,现象:没有影响,备份拿到的是 DDL 后的表结构。

如果在“时刻 2”到达,则表结构被改过,Q5 执行的时候,报 Table definition has changed, please retry transaction,现象:mysqldump 终止;

如果在“时刻 2”和“时刻 3”之间到达,mysqldump 占着 t1 的 MDL 读锁,binlog 被阻塞,现象:主从延迟,直到 Q6 执行完成。

从“时刻 4”开始,mysqldump 释放了 MDL 读锁,现象:没有影响,备份拿到的是 DDL 前的表结构。

总览图

在这里插入图片描述

07 | 怎么减少行锁对性能的影响?

行锁

  • 并不是所有引擎都支持行锁
  • MyISAM不支持,InnoDB支持

两阶段锁

  • 两阶段锁协议

    • 行锁在需要时才加上,不是不需要了就释放,而是等到事务结束才释放。
  • 如果事务中需要锁多个行,就把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。

死锁和死锁检测

  • 互相等待对方资源释放,一直拿不到锁,所以形成了死锁

  • CPU消耗接近100%,但整个数据库每秒执行不到100个事务,可用死锁检测方法

死锁出现后处理的策略
    1. 直接进入等待,直到超时

    超时时间的设置不能太长或者太短

    1. 主动检测,发现死锁后主动回滚事务(常用)

    死锁检测要耗费大量CPU资源

如何解决由热点行更新导致的性能问题?
  • 问题在死锁检测耗费太多资源了

    1. 保证业务一定不会出现死锁时,就把死锁检测关掉
    1. 控制并发度,比如同一行同时最多只有10个线程在更新
    • 但是这个方法不太可行,因为客户端很多
  • 因此,并发控制要做在数据库服务端

      1. 在中间件实现
      1. 修改MySQL源码
    • 总结:在进入引擎之前先排队
  • 不从数据库着手,能否从设计上优化?

    • 将一行改成逻辑上的多行来减少锁冲突

      • 比如十个记录,账户总额等于十个记录的值的总和。

要删除一个表中的前1000行,选择哪种方法

  • 第一种,直接执行 delete from T limit 10000;
    第二种,在一个连接中循环执行 20 次 delete from T limit 500;
    第三种,在 20 个连接中同时执行 delete from T limit 500。

选择在一个连接中循环执行20次:delete from T limit 500

不选择:直接执行delete from T limit 10000

  • 单个语句占用时间长,锁的时间也比较长;而且大事务还会导致主从延迟。

不选择:在20个连接中同时执行delete from T limit 500

  • 会人为造成锁冲突
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值