目录
事务(Transaction)是用来维护数据库完整性的,它能够保证一系列的MySQL操作要么全部执行,要么全不执行。
例子1:
在京东购物下订单的时候,商家库存要减少,订单增加记录,付款我的账号少100元...操作要么全部执行,要么全不执行,不会出现我的账号少100元而没有订单增加记录
例子2:
转账操作:A账户要转账给B账户,那么A账户上减少的钱数和B账户上增加的钱数必须一致,也就是说A账户的转出操作和B账户的转入操作要么全部执行,要么全不执行;如果其中一个操作出现异常而没有执行的话,就会导致账户A和账户B的转入转出金额不一致的情况,为而事实上这种情况是不允许发生的,所以为了防止这种情况的发生,需要使用事务处理
事务及其特征
事务的概念
事务指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS(数据库管理系统)中的事务管理子系统负责事务的处理。
注:目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持
事务的特性
事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。
但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性、一致性、隔离性和持久性。这四个特性简称为ACID特性。
(1)原子性(Atomicity)
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。
使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性
(2)一致性(Consistency)
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。
例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。
(3)隔离性(Isolation)
隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。
例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。
(4)持久性(Durability)
持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。
事务并发问题
事务的ACID特性可以解决了敏感操作(如:转账、下单操作)的数据完整性问题,确保不会出现转账的时候一边已经付款,另一边没有收到这笔转账或者用户已经下单商品,而商家却没有收到订单的问题等。
这是如今网上银行转账、网络支付、网购等操作的基础,也是网络世界上数字货币的基石。但是,随着时间的推移,网络世界的日益壮大,数字货币的日渐欣荣,事务在高并发操作上的表现往往不尽人意,下面介绍事务在高并发操作上的几个问题。
脏读
当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的
时间点 | 事务A | 事务B |
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询余额为100 | |
4 | 余额增加至150 | |
5 | 查询余额为150 | |
6 | 事务回滚 |
不可重复读
指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读
时间点 | 事务A | 事务B |
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询余额为100 | |
4 | 余额增加至150 | |
5 | 查询余额为100 | |
6 | 提交事务 | |
7 | 查询余额为150 |
幻读
幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读
时间点 | 事务A | 事务B |
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询id<3的所有记录 | |
4 | 插入一条记录id=2 | |
5 | 提交事务 | |
6 | 查询id<4的所有记录 |
不可重复读和幻读的区别:不可重复读的重点是修改数据的内容,幻读的重点在于新增或者删除数据内容或结构
为了解决事务并发操作上的问题,mysql引入了事务锁
事务隔离级别(事务锁)
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表(事务锁)
事务隔离级别的实现原理主要是通过对数据库操作加锁以及使用多版本并发控制(MVCC)来实现的。不同的隔离级别意味着对数据库操作的锁定程度和可见性的不同
事务的隔离级别从低到高依次为读未提交、提交读、可重复读以及可串行化,隔离级别越低,越能支持高并发的数据库操作(因为级别越低,限制就越弱)
- 可串行化(Serializable):这是最高的隔离级别,事务串行化执行,一个失误的执行必须等待其他事务结束。这种隔离级别可以防止脏读、不可重复度和幻读的情况发生,但是会降低并发性能。
- 可重复读(Repeatable Read):在这个隔离级别下,事务不会被看成是一个序列,但是当前正在执行事务的变化仍然不能被外部看到。也就是说,如果用户在另外一个事务中执行同条SELECT语句数次,结果总是相同的。
- 提交读(Read Committed):这个隔离级别提供了事务之间最小限度的隔离。事务读取时加读锁,事务写入时加写锁,能解决脏写问题,但是可能出现脏读、不可重复度和幻读的情况。
- 读未提交(Read Uncommitted):这是最低的隔离级别,事务读取时不加锁,事务写入时加写锁。在这个级别下,可能会出现脏读、不可重复度和幻读的情况。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
READ UNCOMMITTED | × | × | × |
READ COMMITTED | √ | × | × |
REPEATABLE READ | √ | √ | × |
SERIALIZABLE | √ | √ | √ |
注:√ 代表解决问题,×代表会出现问题
-- 查看默认的事务隔离级别 MySQL默认的是repeatable read
select @@transaction_isolation;
-- 设置事务的隔离级别 (设置当前会话的隔离级别)
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
关于事务的几个命令
下面是一个示例,演示了如何使用开启事务、提交事务、回滚事务
START TRANSACTION; -- 开始事务
-- 执行一些SQL语句,例如插入、更新或删除操作
INSERT INTO customers (name, email) VALUES ('John Doe', 'john@example.com');
UPDATE orders SET status = 'shipped' WHERE order_id = 123;
COMMIT; -- 提交事务,使更改永久生效
如果在事务执行期间发生错误或异常,可以使用ROLLBACK
语句来回滚事务并撤销对数据的所有更改
START TRANSACTION; -- 开始事务
-- 执行一些SQL语句,例如插入、更新或删除操作
INSERT INTO customers (name, email) VALUES ('John Doe', 'john@example.com');
UPDATE orders SET status = 'shipped' WHERE order_id = 123;
-- 发生错误或异常,回滚事务
ROLLBACK;