1. mysql常见的三个存儲引擎:
- InnoDB存储引擎:InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键,MySQL5.5以后默认使用InnoDB存储引擎
- MyISAM存储引擎:MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务,不支持外键。
- MEMORY存储引擎:MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问,不支持事务,不支持外键。
显而易见,这些存储引擎都是有不同的使用场景的,如果表对事物的要求高,应该使用InnoDB引擎,如果表对事物的要求不高,同时是以查询和添加为主,我们考虑使用MyISAM存储引擎或者MEMORY存储引擎。
2. MVCC原理解析
mysql数据行隐藏字段
字段 | 含义 |
---|---|
DB_ROW_ID | 隐含的自增ID(隐藏主键),用于唯一标识表中的每一行数据,如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。 |
DB_TRX_ID | 该字段存储了当前行数据所属的事务ID。每个事务在数据库中都有一个唯一的事务ID。通过 DB_TRX_ID 字段,可以追踪行数据和事务的所属关系。 |
DB_ROLL_PTR | 该字段存储了回滚指针(Roll Pointer),它指向用于回滚事务的Undo日志记录。 |
2.1 介绍
MVCC(Multi-Version Concurrency Control),即多版本并发控制。这里的多版本是指一条数据可能会有多个版本。
MVCC的主要目的是为了提高数据库并发性能,用更好的方式去处理读写冲突,即做到读写冲突时,也可以不加锁。
MVCC的多版本并发控制是通过undo log和Read View来实现的。
2.2 原理
undo log
在执行insert,update,delete操作时,会插入undo log;Undo Log的作用主要有两个方面:
- 当事务需要回滚时,MySQL可以通过Undo日志中的旧值将数据还原到事务开始之前的状态,保证了事务回滚的一致性。
- 通过使用Undo日志,MySQL可以为每个事务提供独立的事务视图,使得事务读取数据时能看到一致且符合隔离级别要求的数据版本。
不同事务或者同一事务对同一条数据操作时会,会使记录行的undo log成为一条链表(版本链),链首为最新数据,链尾为最早的数据。
举例:
1、事务A插入了一条新记录:insert into user(id, name) values(1, "小明’)
2、现在来了一个事务B对该记录的name做出了修改,改为 “小王”。
在事务B对数据修改时,数据库会对该行加排它锁,然后把该行的数据复制到undo log中,拷贝完毕后,修改该行数据name=“小王”,并且修改该行事务ID为事务B的事务ID,并将回滚指针指向拷贝到undo log中的副本记录,表示上一个版本就是它,然后提交事务后,释放锁。
3、此时又来了个事务C修改同一个记录,将name修改为 “小红”。(同步骤2)
Read View
一致性视图,用来判断版本链中哪个版本对当前事务可见。
在事务进行快照读时,生成读视图,记录并维护当前时刻系统活跃的事务ID(每个事务都会分配一个事务ID,且是递增的)。值得注意的是,Read View只针对 RC(读已提交) 和 RR(可重复读)级别,RU(读未提交)与串行化用不上。
Read View可见性原则
Read View 会维护以下几个字段:
字段 | 含义 |
---|---|
m_ids | Read View 创建时其他未提交的活跃事务 ID 列表。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)。 |
m_creator_trx_id | 创建该 Read View 的事务 ID。 |
m_low_limit_id | 目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见。 |
m_up_limit_id | 活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则 m_low_limit_id为m_up_limit_id 。小于这个 ID 的数据版本均可见。 |
Read View 可见性具体判断如下:
db_trx_id为当前访问数据行上的事务ID
-
db_trx_id < up_limit_id || db_trx_id == creator_trx_id(显示)
如果数据事务ID小于read view中的最小活跃事务ID,则可以肯定该数据是在当前事务启之前就已经存在了的,所以可以显示。或者数据的事务ID等于creator_trx_id ,那么说明这个数据就是当前事务自己生成的,自己生成的数据自己当然能看见,所以这种情 况下此数据也是可以显示的。
-
db_trx_id >= low_limit_id(不显示)
如果数据事务ID大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不显示。 如果小于则进入下一个判断 -
db_trx_id是否在活跃事务(trx_ids)中
不存在:则说明read view产生的时候事务已经commit了,这种情况数据则可以显示。
已存在:则代表我Read View生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的。
3. 事务的隔离级别
- 读未提交(READ UNCOMMITTED):这是事务的最低隔离级别,即一个事务还没有提交,其他的读取操作就可以获取到事务的修改数据
原始数据:
事务1 | 操作介绍 | 事务2 | 操作介绍 | |
---|---|---|---|---|
操作 | 1、SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; | 设置隔离级别为读未提交 | ||
操作 | 2、START TRANSACTION; | 开启事务1 | ||
操作 | 3、UPDATE transational_test SET account = account + 50 WHERE id = 1; | 修改数据库 account+50 | ||
操作 | 4、START TRANSACTION; | 开启事务2 | ||
操作 | 5、SELECT * FROM transational_test | 查询数据库数据 | ||
操作 | 6、ROLLBACK; | 回滚修改操作 |
由于我已经回滚,但是读到了回滚前的数据,造成了脏读
- 读已提交(READ COMMITTED):不存在脏读问题,但是存在不可重复读问题
事务1 | 操作介绍 | 事务2 | 操作介绍 | |
---|---|---|---|---|
操作 | 1、SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; | 设置隔离级别为读已提交 | ||
操作 | 2、START TRANSACTION; | 开启事务1 | ||
操作 | 3、UPDATE transational_test SET account = account + 50 WHERE id = 1; | 修改数据库 account+50 | ||
操作 | 4、START TRANSACTION; | 开启事务2 | ||
操作 | 5、SELECT * FROM transational_test; | 查询数据库数据 | ||
操作 | 6、COMMIT; | 提交事务1 | ||
操作 | 7、SELECT * FROM transational_test; | 查询数据库数据 |
步骤5的查询结果:
步骤7的查询结果: 事务提交后,再次查询,结果与上一步不一致,产生了不可重复读的问题
- 可重复读(REPEATABLE READ)(数据库默认使用):没有脏读,且可重复读,但是可能出现幻读
事务1 | 操作介绍 | 事务2 | 操作介绍 | |
---|---|---|---|---|
操作 | 1、SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; | 设置隔离级别为可重复读 | ||
操作 | 2、START TRANSACTION; | 开启事务1 | ||
操作 | 3、INSERT INTO transational_test VALUE(2,200); | 添加一条记录 | ||
操作 | 4、START TRANSACTION; | 开启事务2 | ||
操作 | 5、SELECT * FROM transational_test; | 查询数据库数据 | ||
操作 | 6、COMMIT; | 提交事务1 | ||
操作 | 7、SELECT * FROM transational_test; | 查询数据库数据 | ||
操作 | 8、SELECT * FROM transational_test FOR UPDATE; | 查询数据库数据 |
步骤5执行结果:
步骤7执行结果:
步骤8执行结果:
发现了没,在没有update操作,或者select 上加for update时,是没有出现幻读的,但是一旦有,则会出现前后查询不一致的情况(幻读)
- 串行化(serializable):可以避免一切脏读,不可重复读,幻读