mysql学习笔记五:锁

全局锁

全局锁就是对整个数据库实例加锁,

实现一:命令是 Flush tables with read lock (FTWRL),让整个库处于只读状态的时候,使用场景:引擎不支持事务,做数据库备份

实现二:mysqldump 使用参数–single-transaction,通过视图,需要数据库支持事务:innodb

表级锁

实现一:表锁, lock tables … read/write语法,与FTWRL类似,可以用unlock tables 主动释放,与客户端断开的时候自动释放。
缺点:对于 InnoDB 这种支持行锁的引擎,一般不使用 lock tables 命令来控制并发,毕竟锁住整个表的影响面还是太大。

实现二:元数据锁MDL(metadata lock),MDL 不需要显式使用,在访问一个表的时候会被自动加上。当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。
1.读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
2.读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

例:给一个小表加个字段,导致整个库挂了。


image.png

1.session A 先启动,这时候会对表 t 加一个 MDL 读锁。由于 session B 需要的也是 MDL 读锁,因此可以正常执行。
2.session C 会被 blocked,是因为 session A 的 MDL 读锁还没有释放,而 session C 需要 MDL 写锁,因此只能被阻塞。
3.如果只有 session C 自己被阻塞还没什么关系,但是之后所有要在表 t 上新申请 MDL 读锁的请求也会被 session C 阻塞。前面我们说了,所有对表的增删改查操作都需要先申请 MDL 读锁,就都被锁住,等于这个表现在完全不可读写了。
4.如果某个表上的查询语句频繁,而且客户端有重试机制,也就是说超时后会再起一个新 session 再请求的话,这个库的线程很快就会爆满

如何安全地给小表加字段:
1.如果你要做 DDL 变更的表刚好有长事务在执行,要考虑先暂停 DDL,或者 kill 掉这个长事务。
2.在 alter table 语句里面设定等待时间(MariaDB合并了 AliSQL 的这个功能, 支持 DDL NOWAIT/WAIT n 这个语法。)。

行锁

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

MySQL 的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。InnoDB 是支持行锁的,这也是 MyISAM 被 InnoDB 替代的重要原因之一。

两阶段锁:

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

例:事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。


image.png

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

死锁和死锁检测

当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。

例:事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。


image.png

解决策略:
1.通过参数 innodb_lock_wait_timeout 来设置等待超时时间。

2。死锁检测,将参数 innodb_deadlock_detect 设置为 on,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。

3.其他:a.控制并发度,需要足够的团队开发mysql引擎。b.把一个业务拆分多个表,随机对其中一个表修改。

你可能感兴趣的:(mysql学习笔记五:锁)