学习笔记经供参考
目录
目录
事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
一、事务操作:
方法一:更改事务提交方式为手动,手动提交或者回滚
查看/设置事务提交方式
默认是自动提交
SELECT @@autocommit;
SET @@autocommit=0;
1 是自动提交 0 是手动提交
提交事务:
COMMIT;
回滚事务:
ROLLBACK;
当设置成手动提交之后,执行完事务需要手动提交,输入commit提交才会产生影响。
执行失败输入rollback则可以回滚数据,以确保数据的完整性一致性
还有一种方式可以在不修改事务提交方式的基础上进行事务控制
方法二:手动显示的开启事务
开启事务:
START TRANSACTION 或 BEGIN;
手动开启事务进行控制
二、事务四大特性:acid
原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
隔离性(Lsolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
三、并发事务问题
脏读:一个事务读到另外一个事务还没有提交的数据。
简单来说就是一个事物没提交的时候另一个事务读到了马上过期的数据
不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读,
一个事物第一次读和第二次读中间出现别的事务修改这条数据,导致两次读的不一样,会造成不可重复度
幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了幻影”。
在解决不可重复度基础上产生,当第一次读某条数据不存在时,该事务A所有读都和第一次一致,如果此时另外一个事务B进行插入这条数据,此时事务A执行插入此条数据发现存在,出现了幻觉一样的幻读问题
四、事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 Read uncommitted | √ | √ | √ |
读已提交 Read committed | × | √ | √ |
可重复度 Repeatable Read (默认) | × | × | √ |
串行化 Serializable | × | × | × |
从上到下 隔离级别越高,性能越低
查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别
SET[SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL
{READ UNCOMMITTED | READ COMMITTED |
REPEATABLE READ | SERIALIZABLE}
1、脏读:
读未提交的级别下存在事务A和B,当事务A对一条数据修改后未提交,事务B读取此条数据读取到的是未提交的数据,存在脏读问题,也就是读到了未确定的数据
将隔离级别升级到读已提交在事务A修改提交之后事务B才能读取到变化的数据
在读未提交隔离级别下,左边事务读到了右边事务未提交的数据
当右侧未提交的回滚之后,事务A的这次读取就会读到错的数据
切换到读已提交级别
事务B未提交的事务A就不会读取到,当B提交事务之后A才会读到
2、不可重复读:
在读已提交的隔离级别下开启两个事物,左侧事物读取一次,这时候右侧进行一个更新操作并提交,左侧事物未提交,并继续读取一次,读到了不一样的数据,一个事务中读到了不一致的数据,这就是不可重复度读问题。
将隔离级别升级至可重复读,开启两个事务,左侧读取了一次数据,右侧事务修改并提交,左侧未提交继续读取,数据不会发生改变,只有左侧事物也提交之后,读取的数据才会变化,解决的不可重复度的问题。
3、幻读
在可重复度的隔离级别下左侧事物没有id为17的数据,但是右侧事务此时插入一条id为17的数据并提交,左侧再插入就会报错,及时再次查询还是没有这条数据,也会提示重复,这就是幻读问题,出现了像幻觉一样的问题
当隔离级别升级至串行化之后,左侧事务先执行查询,右侧事务再进行插入操作就会进入阻塞状态,当事务A所有操作都完成之后才会执行事务B的操作,这样可以避免幻读,但是会造成效率的下降,因为串行化就相当于是一个单线程。
事务隔离级别越高,数据越安全,但是性能越低
五、事务原理
1、redo log
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redolog buffer)以及重做日志文件(redolog file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用.
当数据有更新时,首先在内存缓冲中修改数据,然后会同步到缓存日志当中,随后刷新到磁盘的日志当中,这里mysql支持三种缓存日志redo log buffe写入磁盘日志redo log file,分别是
(1)每秒写入磁盘日志
(2)每次提交都写入磁盘
(3)每次提交写入Os 缓存中 通过操作系统一步刷新到磁盘
redo log 记录的是数据页的变更,采用的记录是循环追加的形式,是一种顺序IO
写入redo log磁盘文件之后,再将Buffer Pool的脏数据刷新到磁盘文件
在发生故障的时间点,如果尚有脏页未写入磁盘,在重启mysql服务的时候,会根据redo log进行重做,从而达到事务的持久性这一特性。
2、undo log
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚 和 MVCC(多版本并发控制)是用来实现事务的原子性
可以认为当delete一条记录时,undolog中会记录一条对应的insert记录,反之undo log和redo log记录物理日志不一样,它是逻辑日志。亦然,当update一条记录时,它记录一条对应相反的updat记录。当执行rollback时,就可以从undolog中的逻辑记录读取到相应的内容并进行回滚。记录的就是反着来的逻辑操作。
Undo log销毁:undolog在事务执行时产生,事务提交时并不会立即删除undolog,因为这些日志可能还用于MVCC。
Undo log存储:undolog采用段的方式进行管理和记录,存放在前面介绍的rollback segment 回滚段中,内部包含1024个undolog segment.
3、MVCC
(1)当前读
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:
select... lock in share mode(共享锁),
select.... for update、update、insert、delete(排他锁)
都是一种当前读。
(2)快照读
简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
Read Committed:每次select,都生成一个快照读
Repeatable Read:开启事务后第一个select语句才是快照读的地方。
Serializable:快照读会退化为当前读。
(3)MVCC
全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MVSOL实现MVCC提供了一个非阻塞读功能。
MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
3.1 隐藏字段
创建表的时候mysql会为我们隐式的创建三个或者两个字段,分别是
DB TRX ID | 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID(自增)。 |
DB ROLL PTR | 回滚指针,指向这条记录的上一个版本,用于配合undolog,指向上一个版本。 |
DB ROW ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。 |
3.2 undolog版本链
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undolog日志只在回滚时需要,在事务提交后,可被立即删除。
而update、delete的时候,产生的undolog日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
当事务修改的时候,每一条的产生修改逻辑会存储到undolog中,
隐藏字段会存储修改的事务id和上一条旧数据逻辑的地址
3.3 readview
ReadView(读视图)是快照读 SQL执行时MVCC提取据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView中包含了四个核心字段:
m_ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
creator_trx_id | ReadView创建者的事务ID |
版本链访问规则:
trx_id代表当前事务的id:
①.trx_id ==creator_trx_id
可以访问该版本,说明数据是当前这个事务更改的。
②.trx_id<min_trx_id
可以访问该版本,说明数据已经提交了
③.trx_id>max_trx_id
不可以访问该版本,说明该事务是在ReadView生成后才开启
④. min_trx_id <= trx id <= max_trx_id
如果trx_id不在m ids中是可以访问该版本的,说明数据已经提交。
不同的隔离级别,生成ReadView的时机不同:
READ COMMITTED(读已提交):在事务中每一次执行快照读时生成ReadView。
REPEATABLE READ(可重复度):仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
读已提交:
读已提交的隔离级别下,每一次快照读都会生成一个readview,包括当前活跃事务集,最小活跃事务,预分配事务ID和创建者的事务ID,执行读操作时会去undolog中逐条查看是否符合readview的匹配规则,不符合则根据undolog版本链找到上一条,继续判断
比如要在事务5第一次查询之后进行一次查询,此时的活跃事务集有(3,4,5),最小是3,预测是6,创建者的事务ID是5
隐藏当前事务id为4,拿4去和匹配规则判断,发现没有符合的条件,此时根据隐藏列undolo中去找上一条
这条数据的事务id为3,和readview进行规则匹配,也不符合,继续上一条
事务id为2的发现是符合第二条的,可以读取这条。
这是实现读已提交的基本原理
可重复度:
可重复读的隔离级别下,只有第一次快照读会生成一个readview,后续都是去复用这个readview去判断的,所以读的结果都是和第一次一致的
undolog确保了事务的原子性
redolog确保了事务的持久性
MVCC+锁机制确保了事务的隔离性
undolog+redolog确保了事务的一致性