MySQL高级——锁与事务
Posted oahaijgnahz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL高级——锁与事务相关的知识,希望对你有一定的参考价值。
mysql高级(二)——锁与事务
文章目录
一、MySQL锁机制**
1.1 锁与其应用场景
- MySQL的锁按照数据操作类型分为:读锁(共享锁)、写锁(独享锁)【读读共享,读写、写写互斥】
- 按照数据操作的粒度分为:行锁 和 表锁
锁的类型和适合的场景
- 表锁:适合读多场景,偏向MyISAM存储引擎,开销小,加锁快,锁粒度大,并发度低。
- 行锁:适合事务场景,偏向InnoDB存储引擎,开销大,加锁慢,锁粒度小,并发度高。
读阻塞写、写阻塞读
-
读阻塞写:当一个会话在一个表中加了读锁,那么其他会话也能读此表数据,但是其他表的写数据请求会被阻塞。并且当前表在未释放此表的读锁之前,不能对其他表进行操作,不能对自己读锁定的表进行写数据操作。
-
读阻塞写:当一个会话在一个表中加了写锁,那么其他会话 读写 此表数据阻塞。本会话可以读写此表。
总结一下就是:读锁阻塞写,写锁阻塞读写
手动锁定一行实现一个事务
begin;
select * from test where id=8 for update;
--do your job
commit;
行锁升级为表锁的情况
无索引或者索引失效的情况下(如SQL中varchar字段的查询没带引号,但MySQL内部优化进行了自动类型转换,但导致索引失效了),行锁升级为表锁。隐秘又恐怖的情况
间隙锁的问题(范围查询加锁)
MySQL在对数据进行范围条件操作而不是等值条件操作时,InnoDB会给符合条件的已有数据索引项加锁。而存在范围中的根本不存在的记录也会被加锁,其他会话对这个不存在的记录行进行新增操作会被阻塞,这种锁机制被称为——间隙锁。但间隙锁可以用来在repeatable read的隔离级别下防止幻读的发生,所以MySQL在repeatable read隔离级别下也是不会发生幻读的。(范围查询靠间隙锁防止幻读)
next-key锁(非聚簇索引等值查询加范围锁,InnoDB默认加锁方式)
实际是行锁+间隙锁。会将等值查询非聚簇索引包含后,在该索引前后一段范围都会被加锁。这个做法可以实现当前读下,不可重复读隔离级别下的幻读问题避免。但由于范围锁定,并发写入时性能会下降。
对于聚簇索引的等值查询,InnoDB使用的是行锁(Record Lock)
1.2 事务与MySQL隔离级别
1.2.1 事务与事务的ACID属性***
MySQL的事务是由一组SQL语句组成的逻辑处理单元,具有事务的四个属性ACID:
- 原子性(Atomic):事务是个原子操作,要么成功要么失败。
- 一致性(Consistent):事务开始和完成时,数据保持一致状态。所有事务对一个数据的读取结果都是一致的。
- 隔离性(Isolation):支持事务的系统提供一定的隔离性,保证事务在并发环境下能不受干扰地完成,对其他事务不可见。
- 持久性(Durable):事务完成后,对数据有持久化机制,保证即使系统故障也能恢复数据。
1.2.2 并发场景事务存在的问题**
- 更新丢失:多个事务对同一数据行进行修改时,彼此不知道对方的存在,造成一方的数据丢失。
- 脏读:事务A读取了事务B修改了尚未提交的数据,此时事务B回滚,A读取的数据无效。
- 不可重复读:事务A多次读取某一行数据,但期间事务B对此行数据修改并提交,两次读取数据不一致。
- 幻读:事务A读取到了事务B的新增数据,不符合隔离性。
1.2.3 MySQL事务隔离级别
隔离级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读(read uncommitted) | 最低级别,只能保证不读取物理损坏数据 | 是 | 是 | 是 |
已提交读(read committed) | 语句级 | 否 | 是 | 是 |
可重复读(repeatable read) | 默认级别,事务级 | 否 | 否 | 是 |
串行化(serializable) | 最高级别,事务级 | 否 | 否 | 否 |
也就是MySQL默认配置下是存在幻读问题的(但MySQL通过当前读使用Next-Key Lock、Gap Lock,快照读使用MVCC机制来防止幻读的发生),隔离级别和并发是负相关的,隔离级别越高越趋向于串行执行,这与并发能力是矛盾的。所以根据业务场景需求,来采用相应的隔离级别。
1.2.4 MVCC协议
MVCC工作在repeatable read和read committed两种隔离级别下。
InnoDB引擎下(MVCC+行锁),上述两种隔离级别,MySQL对读数据不加行锁而是使用多版本控制的方式,写数据加行锁实现并发控制。这样读写操作不会阻塞(只有写写阻塞),但需要额外的存储空间来实现,使用空间换时间的方法。(基于版本号控制,可以实现快照读,每个事务有递增的事务版本号,也是可以在可重复读隔离级别下避免幻读;当前读则是select...for update、select...in share mode
等,MVCC无法避免幻读发生,则需要Gap Lock、Record Lock和Next-Key Lock的支持)
MVCC在每行后面保存几个隐藏的列:版本号 和 回滚指针 等。而每一个事务在启动的时候,都有一个唯一的递增的事务版本号。
比如写操作是通过给对应行加排他锁后,存储原数据行快照到undo log中,然后完成自己的修改后保存当前行数据并修改创建版本号和回滚指针指向undo log链尾。
在快照读操作时,会创建Read View保存对当前事务不可见的其他活跃事务,事务要读取某个记录行时,会根据这个Read View来对比判断快照的可见性。 而当前读则是等值查询对聚簇索引加Record Lock,非聚簇索引加Next-Key Lock,范围查询加Gap Lock来通过加读锁实现事务的。
由此,Repeatable Read和Read Committed的隔离级别实现,实际是RR在首次select时创建Read View并在此事务中不再变化。而RC则在每次select时创建新的Read View,在事务中会读到其他事务修改的结果(不可重复读)
MVCC的事务回滚是通过一个undo log
将每个数据行的快照链接起来,通过undo log
来回滚事务。
二、MySQL主从复制*
MySQL主从复制的作用:
-
主数据库出现问题,可以切换到从数据库。
-
可以进行数据库层面的读写分离。
-
可以在从数据库上进行日常备份。
MySQL主从复制分为三步(MySQL复制是异步的且串行化的):
- master将操作写入binary log中。其中的记录过程叫做二进制日志事件。
- slave将master的二进制日志事件拷贝到其中继日志中(relay log)。
- slave重做中继日志中的事件,将改变应用到自己的数据库中。
canal从MySQL抽取增量数据也是这个原理,它将自己伪装成一个slave。
参考:MySQL MVCC
以上是关于MySQL高级——锁与事务的主要内容,如果未能解决你的问题,请参考以下文章
详解 MySql InnoDB 中的三种行锁(记录锁间隙锁与临键锁)