那些年背过的面试题——MySQL篇-事务

1、事务 4 大特性

**事务 4 大特性:**原子性、一致性、隔离性、持久性

原⼦性: 事务是最⼩的执行单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么全不执行

一致性: 执行事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;

隔离性: 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;

持久性: ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。

实现保证:

MySQL 的存储引擎 InnoDB 使用重做日志保证一致性与持久性,回滚日志保证原子性,使用各种锁来保证隔离性。

2、事务隔离级别

**读未提交:**最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

**读已提交:**允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发⽣。

**可重复读:**同⼀字段的多次读取结果都是⼀致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,会有幻读。

**串行化:**最⾼的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产⽣⼲扰。

3、默认隔离级别 - RR

**默认隔离级别:**可重复读;

同⼀字段的多次读取结果都是⼀致的,除非数据是被本身事务自己所修改;

可重复读是有可能出现幻读的,如果要保证绝对的安全只能把隔离级别设置成 SERIALIZABLE;这样所有事务都只能顺序执行,自然不会因为并发有什么影响了,但是性能会下降许多。

第二种方式,使用 MVCC 解决快照读幻读问题(如简单 select),读取的不是最新的数据。维护一个字段作为 version,这样可以控制到每次只能有一个人更新一个版本。

work_threads = 4 
// 说明:为了避免CPU上下文切换的开销,以及不必要的资源消耗,不建议将本参数设置得过大。
// 公式为: 
work_threads + (reader_threads + writer_threads) = CPU数

第三种方式,如果需要读最新的数据,可以通过 GapLock+Next-KeyLock 可以解决当前读幻读问题,

// 对于单盘挂载方式,磁盘读写线程分 别设置为 1即可 
// 如果磁盘做了RAID,那么需要酌情加大读写线程数,这样才能最大程度地发挥磁盘性能disk_rw_separated:磁盘读写是否分离 disk_reader_threads:单个磁盘读线程数 disk_writer_threads:单个磁盘写线程数

4、RR 和 RC 使用场景

事务隔离级别 RC(read commit) 和 RR(repeatable read)两种事务隔离级别基于多版本并发控制 MVCC(multi-version concurrency control)来实现。

5、行锁,表锁,意向锁

InnoDB 支持行级锁 (row-level locking) 和表级锁, 默认为行级锁

InnoDB 按照不同的分类的锁:

共享 / 排它锁 (Shared and Exclusive Locks):行级别锁,

意向锁 (Intention Locks),表级别锁

间隙锁 (Gap Locks),锁定一个区间

记录锁 (Record Locks),锁定一个行记录

表级锁:(串行化)

Mysql 中锁定粒度最大的一种锁,对当前操作的整张表加锁,实现简单 ,资源消耗也比较少,加锁快,不会出现死锁 。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。

行级锁:(RR、RC)

Mysql 中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。 InnoDB 支持的行级锁,包括如下几种:

记录锁(Record Lock): 对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项;

间隙锁(Gap Lock): 对索引项之间的 “间隙” 加锁,锁定记录的范围,不包含索引项本身,其他事务不能在锁范围内插入数据。

Next-key Lock: 锁定索引项本身和索引范围。即 Record Lock 和 Gap Lock 的结合。可解决幻读问题。

InnoDB 支持多粒度锁(multiple granularity locking),它允许行级锁与表级锁共存,而意向锁就是其中的一种表锁。

共享锁( shared lock, S )锁允许持有锁读取行的事务。加锁时将自己和子节点全加 S 锁,父节点直到表头全加 IS 锁

排他锁( exclusive lock, X )锁允许持有锁修改行的事务。 加锁时将自己和子节点全加 X 锁,父节点直到表头全加 IX 锁

意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S 锁)

意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X 锁)

6、MVCC 多版本并发控制

MVCC 是一种多版本并发控制机制,通过事务的可见性看到自己预期的数据,能降低其系统开销。(RC 和 RR 级别工作)

InnoDB 的 MVCC, 是通过在每行记录后面保存系统版本号 (可以理解为事务的 ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的 ID。这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的,防止幻读的产生。

1.MVCC 手段只适用于 Msyql 隔离级别中的读已提交(Read committed)和可重复读(Repeatable Read).

2.Read uncimmitted 由于存在脏读,即能读到未提交事务的数据行,所以不适用 MVCC.

  1. 简单的 select 快照度不会加锁,删改及 select for update 等需要当前读的场景会加锁

原因是 MVCC 的创建版本和删除版本只要在事务提交后才会产生。客观上,mysql 使用的是乐观锁的一整实现方式,就是每行都有版本号,保存时根据版本号决定是否成功。Innodb 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行所有快照连接起来。

版本链

在 InnoDB 引擎表中,它的聚簇索引记录中有两个必要的隐藏列:

trx_id

这个 id 用来存储的每次对某条聚簇索引记录进行修改的时候的事务 id。

roll_pointer

每次对哪条聚簇索引记录有修改的时候,都会把老版本写入 undo 日志中。这个 roll_pointer 就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的 undo 日志没有这个属性,因为它没有老版本)

每次修改都会在版本链中记录。SELECT 可以去版本链中拿记录,这就实现了读 - 写,写 - 读的并发执行,提升了系统的性能。

上一篇:Javascript正则表达式


下一篇:PYTHON自学笔记(一)vscode配置-环境变量设置