MySQL实战 | 06/07 简单说说MySQL中的锁

发布时间:2020-08-08 04:02:10 作者:不正经程序员
来源:ITPUB博客 阅读:236

MySQL实战 | 06/07 简单说说MySQL中的锁

锁是计算机协调多个进程或纯线程并发访问某一资源的机制。

在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。

如何保证数据并发访问的一致性、有效性是所在有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。

为什么要有锁?

使用数据库,避免不了并发问题,当并发事务同时访问一个资源时,有可能导致数据不一致,因此需要一种机制来将数据访问顺序化,以保证数据库数据的一致性

锁就是其中的一种机制。

我们可以用公厕做个比喻。

公厕是可供多个消费者使用的,因此可能出现多个人同时需要使用厕所的情况。

但是,厕所只有一个,总不能大家一起吧?

为了避免冲突,于是厕所里装了锁,某一个人在上测试时,可以在里面用锁锁住,其他人就不能再从外面打开了,只能等待。

等里面的人出来了,从里面把锁打开,外面的人才能进去。

下面,带你一起梳理下 MySQL 的锁管理机制和锁的执行流程,先有一个大致的脉络。

MySQL 的锁管理机制

MySQL实战 | 06/07 简单说说MySQL中的锁

1、全局读锁 — FLUSH TABLES WITH READ LOCK(SQL层)

2、表级 table-level 数据锁(SQL层)

3、Meta-data 元数据锁:在 table cache 缓存里实现的,为 DDL(Data Definition Language)提供隔离操作。

4、存储引擎特有机制 — row locks行锁,page locks页锁,table locks表级,版本控制(在引擎中实现)

相对其他数据库而言,MySQL 的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。

MySQL 的锁执行流程

MySQL实战 | 06/07 简单说说MySQL中的锁

1、计算语句使用到的所有表;

2、在每个表:打开表,从 table cache 缓存里得到 TABLE 对象,并在此表加上 meta-data 元数据锁;

3、等待全局读锁后改变数据;

4、在每个表:锁表,在表加上 table-level 数据锁;

5、执行语句:调用:handler::write_row()/read_rnd()/read_index() 等;隐式地调用引擎级 engine-level 锁机制;

6、在每个表:释放表的数据锁;

7、在每个表:释放表的 DDL 锁并把表放回 table cache 缓存里;

下面,我们开始简单针对每一种锁,看下都有什么特点。

全局锁

加了全局锁后,整个库变为只读状态,所有的写操作都会被阻塞,包括:

加全局锁的命令:Flush tables with read lock,即 FTWRL。

全局锁的主要使用场景是全库的逻辑备份,加了全局锁进行备份时有一定的使用风险:

1、若在主库备份,备份期间只读,会影响业务;
2、若在从库备份,从库只读,无法及时同步主可以的更新,造成主从不一致;

mysqldump --single-transaction

也许你还记得,我们在之前讲事务的时候,有一个隔离级别叫做可重复读,也就是设置了隔离级别进入事务后,别的事务更改数据不会影响当前的读取。

使用 mysqldump 命令,结合 --single-transaction 参数,可以将隔离级别设置为:REPEATABLE READ。

并且随后再执行一条 START TRANSACTION 语句,让整个数据在 dump 过程中保证数据的一致性,这个选项对 InnoDB 的数据表很有用,且不会锁表。

为了确保使用 --single-transaction 命令时,最终 dump 文件的有效性。需没有下列语句 ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE,因为一致性读不能隔离上述语句。所以如果在 dump 过程中,使用上述语句,可能会导致 dump 出来的文件数据不一致或者不可用。

为啥不直接使用 mysqldump --single-transaction 来备份?

因为,有些引擎不支持事务啊,比如 MyISAM 引擎,所以,现在大家都在力推用 InnoDB 替代 MyISAM。

set global readonly=true?

set global readonly=true 也可以将全局表设为只读状态,有啥区别呢?

首先,修改 global 变量的方式影响面更大,不建议使用。

另外,异常处理机制上和 FTWRL 有差异:

注意点

表级锁

表锁

语法

LOCK TABLES tbl_name ; # 不影响其他表的写操作

解锁也是:

UNLOCK TABLES;

注意点:

P.S. MYSQL 的 read lock 和 wirte lock

read-lock:允许其他并发的读请求,但阻塞写请求,即可以同时读,但不允许任何写,也叫共享锁
write-lock:不允许其他并发的读和写请求,是排他的(exclusive),也叫独占锁

元数据锁(MDL:metadata lock)

元数据锁不需要显式使用,在访问一个表的时候会自动加上

它的作用主要是保证读写的正确性。

如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

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

因此,需要避免长事务,因为长事务会造成锁一直不能释放,后续的操作会堆积,这个库的线程很快就会爆满。

行锁

行锁是引擎层实现的,像 MyISAM 引擎就直接不支持行锁,这些引擎在并发控制只能用表锁!

InnoDB 的行锁

两阶段协议:

当需要锁多个行时,尽量把影响并发的锁往后放,这样可以最大程度的减少事务之间的锁等待,提升并发度。

另外,InnoDB 的 行锁建立在索引的基础上,锁的是索引。因此,如果更新的列没建索引会锁住整个表。

死锁

MySQL实战 | 06/07 简单说说MySQL中的锁

不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源。

死锁对策

1、主动等待超时,由参数 innodb_lock_wait_timeout 设置,但是业务无法等待;

2、主动死锁检测(innodb_deadlock_detect=on

发生死锁后,InnoDB 一般都可以检测到,并使一个事务释放锁回退,另一个则可以获取锁完成事务。

另外,我们可以采取以下方式避免死锁:

另外,死锁检测也非常耗费资源,判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n) 的操作。

比如有 1000 个并发线程要同时更新同一行,那么死锁检测操作就是 100 万这个量级的,这将消耗大量的 CPU 资源。

如何解决死锁检测耗费资源的情况?

1、关掉死锁检测,需要保证不会发生死锁;
2、控制并发,对应相同行的更新,在进入引擎之前排队;

更新一条记录时具体什么时候用行锁什么时候是表锁

引擎支持行锁就行锁,比如 innodb;

引擎不支持行锁就表锁,比如 myisam;

Online DDL 的过程

在 MySQL5.6 中,开始支持更多的 alter table 类型操作来避免 copy data,同时支持了在线上 DDL 的过程中不阻塞 DML 操作,真正意义上的实现了 Online DDL。

MySQL实战 | 06/07 简单说说MySQL中的锁

1、拿 MDL 写锁
2、降级成 MDL 读锁
3、真正做 DDL
4、升级成 MDL 写锁
5、释放 MDL 锁

1、2、4、5 如果没有锁冲突,执行时间非常短。
第 3 步占用了 DDL 绝大部分时间,这期间这个表可以正常读写数据,是因此称为「online」

总结

上述特点来看,很难说哪种锁更好,只能相对于所处的业务场景来选择更加适合的锁机制。

如果仅从锁的角度来看,表级锁更适合以查询为主的应用场景,而行级锁则更适合于大量按索引条件并发更新少量数据的应用场景

对于平时常用的存储引擎,MyISAM 采用的是表级锁,InnoDB 采用的是行级锁加表级锁。

参考:
https://dwz.cn/oudQ7cM9

推荐阅读:
  1. mysql数据库查询语句的写法
  2. mysql如何重装

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mysql 中的 简单

上一篇:Docker:一场令人追悔莫及的豪赌

下一篇:集合:安卓开发中怎么注意细节?一文告诉你四十个

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》