MySQL事务
一:事务
概述
对于一个或者多个SQL语句,要么全部执行成功,要么全部执行失败,例如:在电商网站中,顾客下单、付款、及商品减库存应该在一个事务中,防止下单付款成功时减库存出现异常,发货失败的情况,所以事务中某个环节出异常,之前执行的所有SQL语句应该回滚。
1.1事务的特性
事物的四大特性分别是原子性、一致性、隔离性、持久性 , ACID特性。
- 原子性(atomicity):一个事务是一个不可分割的最小单元,事务中的所有操作要么全部提交成功,要么全部失败回滚。
- 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
- 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
基本语法
start transaction #关闭自动提交事务 begin #开始一个事务 rollback #事务回滚 commit #事务确认
默认情况下,MySQL是自动提交(autocommit)的,也就是说,如果不是显式地开启一个事务,每个查询都会被当做一个事务执行 commit。
查看事务是否开启了自动提交,可以通过设置 autocommit来修改自动提交模式:
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> set autocommit = 1; Query OK,0 row in set (0.00 sec)
如果设置了autocommit=0,当前连接所有事务都需要通过明确的命令来提交或回滚。
ACID只是个概念,事务最终目的是要保障数据的可靠性,一致性
1.2事物的并发问题
脏读,不可重复读,幻读,丢失修改
- 脏读:一个事务读取到了另外一个事务未提交的数据,读到其他事务的未提交的“更新”数据。
例子:事务A更新数据,将a更新为b,事务B读取数据为b,此时A进行事务回滚,此时数据为a,事务B读到的记录为b则是非法数据。 - 不可重复读:指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
例子:事务A更新数据,将数据a更新为b,那么B事务读取到的数据就是b,此时事务A再将数据b更新为c,那么事务B第二次读取到的记录是c,两次读取的结果不一样。 - 幻读:一个事务读取到了另外一个事务未提交的数据,只是读到的是其他事务“插入”的数据,它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
- 丢失修改:指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。
解决办法
产生并发不一致的问题主要原因是破坏了事务的隔离性,解决办法是通过并发控制来保证隔离性,并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂,数据库系统提供了事务的隔离级别,让用户更轻松的解决并发一致的问题
1.3事物隔离级别
读未提交,读已提交,可重复读,串行化
- 读未提交(Read Uncommitted):所有事务可以看到其他事务未提交的执行结果,读写并行,性能高,会造成脏读。
- 读已提交(Read Committed):所有事务只能看到其他事务已提交的执行结果,会产生不可重复读以及幻读问题。
- 可重复读(Repeatable read):MySQL默认的级别,所有事务可以看到其他事务已提交后的修改后的数据,但是第一次读取到这个修改后的数据,其他事务继续修改了数据并提交,这个事务读取的也是第一次都到的值,不会读到修改后的新值。无法读写并行,存在幻读问题。
- 可串行化(Serializable):最高隔离级别,所有并发执行的事务都进入队列,挨个串行执行。会造成数据不一致问题。
默认情况下,MySQL 的隔离级别是可重复读(repeatable read)。
MySQL的隔离级别是可以进行调整的,但是新的隔离级别会在下一个事务开始时才生效。
查看事务隔离级别
mysql> show variables like '%tx_isolation%'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | REPEATABLE-READ | +---------------+-----------------+ 1 row in set (0.00 sec)
指定修改的事务隔离级别的范围
session:表示修改的事务隔离级别将应用于当前 session(当前 cmd 窗口)内的所有事务。
global:表示修改的事务隔离级别将应用于所有 session(全局)中的所有事务,且当前已经存在的 session 不受影响。
使用 SET TRANSACTION 语句修改 session 的隔离级别
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; Query OK, 0 rows affected (0.01 sec) mysql> show variables like '%tx_isolation%'; +---------------+------------------+ | Variable_name | Value | +---------------+------------------+ | tx_isolation | READ-UNCOMMITTED | +---------------+------------------+ 1 row in set (0.00 sec)
使用 SET TRANSACTION 语句修改全局事务的隔离级别
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; Query OK, 0 rows affected (0.00 sec) mysql> select @@global.tx_isolation; +-----------------------+ | @@global.tx_isolation | +-----------------------+ | REPEATABLE-READ | +-----------------------+ 1 row in set (0.00 sec)
二:事务实现原理
重做日志,回滚日志,锁技术是实现事务的基础
2.1原子性的实现
事务的原子性通过undo log
(回滚日志)来实现的,( undo log )叫做回滚日志,用于记录数据被修改前的信息,undo log
记录事务修改之前版本的数据信息,当进行插入,删除以及更新操作时生成的undo log
,因此假如由于系统错误或者rollback操作而回滚的话可以根据undo log
的信息来进行回滚到没被修改前的状态。
Undo log销毁: undo log
在事务执行时产生,事务提交时,并不会立即删除 undo log
,因为这些日志可能还用于 MVCC。
Undo log存储: undo log
采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含1024个 undo log segment。
2.2持久性的实现
事务的持久性性是通过 redo log
(重做日志)来实现的, ( redo log ) 叫做重做日志,记录数据被修改后的信息,用来实现事务的持久性,该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改(插入,删除,更新)信息都会存到该日志中,用于在刷新脏页到磁盘时,发生错误时进行数据恢复使用。
start transaction; select balance from bank where name="zhangsan"; // 生成 重做日志 balance=600 update bank set balance = balance - 400; // 生成 重做日志 amount=400 update finance set amount = amount + 400; commit;
执行过程如下
mysql
为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool
(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。当断电的时候,还没来得及执行红色箭头操作,会导致丢失部分已提交事务的修改信息,所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log
持久化到磁盘,系统重启之后在读取redo log
恢复最新数据。
2.3隔离性的实现
事务的隔离性是通过 (读写锁+MVCC
)来实现的,通过锁机制、MVCC
来保证事务的隔离性,在SQL
标准里定义了四种隔离级别,每一种级别都规定一个事务中的修改,哪些是事务之间可见的,哪些是不可见的。
级别越低的隔离级别可以执行越高的并发,但同时实现复杂度以及开销也越大。
隔离性是要管理多个并发读写请求的访问顺序。这种顺序包括串行或者是并行。
注意:写请求不仅仅是指insert操作,又包括update操作。
数据可靠性与性能之间的权衡
- 可靠性性高的,并发性能低(比如
Serializable
) - 可靠性低的,并发性能高(比如
Read Uncommited
)
2.4一致性的实现
而事务的终极大 boss 一致性是通过原子性,持久性,隔离性来实现的,通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
start transaction; select balance from bank where name="zhangsan"; // 生成 重做日志 balance=600 update bank set balance = balance - 400; // 生成 重做日志 amount=400 update finance set amount = amount + 400; commit;
1.假如执行完 update bank set balance = balance - 400;之发生异常了,银行卡的钱也不能平白无辜的减少,而是回滚到最初状态。
2.又或者事务提交之后,缓冲池还没同步到磁盘的时候宕机了,这也是不能接受的,应该在重启的时候恢复并持久化。
3.假如有并发事务请求的时候也应该做好事务之间的可见性问题,避免造成脏读,不可重复读,幻读等。在涉及并发的情况下往往在性能和一致性之间做平衡,做一定的取舍,所以隔离性也是对一致性的一种破坏。
三:MVCC
概述
MVCC
(MultiVersion Concurrency Control
) 叫做多版本并发控制.
InnoDB
的 MVCC
,指维护一个数据的多个版本,使得读写操作没有冲突,是通过在每行记录的后面保存两个隐藏的列来实现的。这两个列, 一个保存了行的创建时间,一个保存了行的过期时间,当然存储的并不是实际的时间值,而是系统版本号。
主要实现思想:通过数据多版本来做到读写分离,从而实现不加锁读进而做到读写并行。
其实现:依赖于undo log
和read view
undo log
:记录某行数据的多个版本的数据read view
:用来判断当前版本数据的可见性
当前读
当前读指的是读取的记录是最新版本,读取时还保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。select...lock in share mode(共享锁),select...for update,update,insert,delete(排它锁)都是一种当前读。
隐藏字段
在定义的字段后面还存在三个隐藏字段
本文作者:爱慕
本文链接:https://www.cnblogs.com/aimu69/p/16021884.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步