【mysql事务】
mysql事务
- 一、什么是数据库事务?
- 二、事务的四大特性(ACID)?
- 三、并发事务可能出现的问题?
- 四、mysql如何解决并发事务带来的问题?
- (1).事务隔离级别
- (2).锁机制
- 五、核心机制:mvcc(多版本并发控制)
一、什么是数据库事务?
事务是mysql执行的最小单元,是作为单个逻辑工作单元执行的一系列操作;
这些操作作为一个整体向系统提交、要么都执行、要么都不执行
事务时一组不可再分割的操作集合(工作逻辑单元)
二、事务的四大特性(ACID)?
-
A -原子性:
事务是一个不可分隔的最小单元。事务中的所有操作,要么全部成功,要么全部失败。
实现机制:通过mysql的undoLog日志实现(回滚日志)实现。如果事务失败,系统按照undoLog将数据恢复到事务开始前状态。 -
C -一致性:
事务必须是数据库从一个一致性状态转变为另一个一致性状态。类似于转账前后,你和对方的总金额应该保持不变。
实现机制:一致性是原子性、隔离性和持久性的共同目标,需要应用程序和数据库共同维护。 -
I -隔离性:
多个事务并发执行时,一个事务的执行不影响其他事务。数据库系统提供了不同隔离级别来控制干扰程度。
实现机制:通过mysql的锁机制和mvcc实现。 -
D -一致性:
一旦事务被提交,对数据库的修改是永久的,即使系统发生故障(如断电、崩溃),数据也不会丢失。
实现机制:通过mysql的redolog(重做日志)实现。事务提交时,更改会先写入 Redo Log。即使数据还没写入磁盘就崩溃了,重启后也能根据 Redo Log 恢复数据。
三、并发事务可能出现的问题?
多个事务并发执行一定会产生相互争夺资源的问题
脏读:
指一个事务在处理过程中,读到了令一个事务未提交的事务。
当一个事务正在访问数据并且对其进行了修改,但是还没提交事务,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据的修改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是“脏读”,依据“脏数据”所做的操作可能是会出现问题的。
修改丢失:
是指一个事务读取一个数据时,另外一个数据也访问了该数据,那么在第一个事务修改了这个数据之后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,这种情况就被称为修改丢失。
不可重复读:
指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个
数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况就被称为*不可重复读。
幻读:
是指同一个事务内多次查询返回的结果集总数不一样(比如增加了或者减少了行记录)。
幻读与不可重复读类似,幻读是指一个事务读取了几行数据,这个事务还没结束,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读。
四、mysql如何解决并发事务带来的问题?
mysql主要是通过锁机制和事务隔离级别来解决并发事务带来的问题。
(1).事务隔离级别
- 读未提交:指一个事务还没提交,他的变更就可以被其他事务看到.
在此隔离级别下不能解决任何问题。 - 读已提交:指一个事务提交之后,他的变更才能被其他事务看到。
可以解决脏读问题 - 可重复读:指一个事务在执行过程中,跟这个事务启动时读到的数据时一致的。mysql中InnoDB存储引擎默认隔离级别。
可以解决脏读和不可重复读问题. - 串行化:会对记录加上读写锁,多个事务对这条记录进行读写操作时,如果出现读写冲突的情况,后访问的事务必须等待前一个事务执行完成,才可继续执行。
解决了幻读,不可重复读,脏读问题.
读已提交和可重复读主要是通过mvcc实现的
(2).锁机制
锁是隔离级别实现的底层机制。
-
按粒度分:
- 表级锁:锁住整张表。开销小,加锁快,但并发度低。MyISAM引擎主要使用表锁。
- 行级锁:只锁定需要操作的行。开销大,加锁慢,但并发度高。InnoDB引擎主要使用行锁。
-
按兼容性分
- 共享锁: 也称读锁。一个事务获取了某行的共享锁,其他事务可以读这行,但不能写它。
SELECT ... LOCK IN SHARE MODE;
- 排他锁:也称写锁。一个事务获取了某行的排他锁,其他事务既不能读也不能写这行。
SELECT ... FOR UPDATE;
- 共享锁: 也称读锁。一个事务获取了某行的共享锁,其他事务可以读这行,但不能写它。
-
InnoDB的特殊锁:
- 记录锁:锁住单条索引记录。
- 间隙锁:锁住一个索引区间,但不包括记录本身。这是解决幻读的关键。
- 记录锁 + 间隙锁。它锁住一条记录和该记录之前的间隙。这是InnoDB在可重复读隔离级别下默认的行锁算法,它能有效防止幻读。
五、核心机制:mvcc(多版本并发控制)
核心思想:为每一行数据维护多个历史版本,当一个事务需要读取数据时,它可以看到一个开始时的数据快照,而无需等待其他正在修改该数据的事务结束。这就实现了读写不阻塞,大大提升了并发性能。
MVCC的三大核心构件:
MVCC的实现依赖于三个核心部分:隐藏字段、Undo Log 和 Read View。
1.隐藏字段
InnoDB在每个数据行(存储在聚簇索引中)的后面自动添加了三个(实际上是两个半)隐藏字段:
- DB_TRX_ID(6字节):最近修改事务ID。记录最后一次插入或更新该行数据的事务ID。DELETE操作在InnoDB内部也被视为一次更新。
- DB_ROLL_PTR(7字节):回滚指针。指向该行数据上一个版本在Undo Log中的存储位置。所有的旧版本通过这个指针连接成一个版本链。
- DB_ROW_ID(6字节):行ID。如果表没有定义主键,InnoDB会自动生成这个隐藏字段作为聚簇索引的主键。如果表有主键,则不会生成。
2.UndoLog
Undo Log主要用于事务回滚和MVCC。它存储的是数据被修改之前的旧版本。
- 当执行INSERT时,Undo Log会记录对应主键,回滚时直接删除。
- 当执行UPDATE或DELETE时,Undo Log会记录修改前的整行数据(或修改的列),并包含DB_TRX_ID和DB_ROLL_PTR,该DB_ROLL_PTR指向上一个更早的版本。
这些Undo Log版本通过DB_ROLL_PTR指针串联起来,形成一个单向链表,即版本链。链表的头部是最新的数据,尾部是最旧的数据。
3.ReadView(读视图)
Read View是MVCC的“裁判”,它决定了当前事务能看到版本链中的哪个数据版本。
当事务执行一条快照读(普通的SELECT语句,非SELECT … FOR UPDATE)时,会根据当前的隔离级别创建一个(或复用)Read View。
Read View主要包含以下几个关键信息:
- m_ids:生成Read View时,系统中活跃的(未提交的)事务ID列表。
- min_trx_id:m_ids中的最小值。
- max_trx_id:生成Read View时,系统应该分配给下一个事务的ID。
- creator_trx_id:创建该Read View的当前事务的ID。
数据可见性判断算法:
一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:
- 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 前生成的,所以该版本的记录对当前事务可见。
- 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View后才启动的事务生成的,所以该版本的记录对当前事务不可见。
- 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
- 如果记录的trx_id在m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见
- 如果记录的trx_id没在m_ids 列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见
如果当前版本不可见,就顺着版本链的 DB_ROLL_PTR 找到上一个版本,然后重复上述判断规则,直到找到第一个可见的版本或到达链尾。
不同隔离级别下的差异:
MVCC的关键行为差异体现在Read View的创建时机上:
读已提交:
- 在每次执行select时,都会创建一个ReadView。
- 效果:因为每次读都重新看一眼当前系统的提交状态,所以总能读到其他事务最新提交的数据。这解决了脏读,但可能导致不可重复读和幻读。
可重复读:
- 在第一次select语句执行时都会生成一个readView,在整个事务的后续操作中都复用这个相同的Read View。
- 效果:因为“第一眼”看到的数据状态被定格了,所以在整个事务过程中,无论读多少次,看到的数据都是一致的。这解决了脏读和不可重复读,并通过快照在很大程度上避免了幻读。