什么是事务
事务的定义
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
假如A转账给B 100元,先从A的账户里扣除100元,再在B的账户上加上100元。如果扣完A的100元后,还没来得及给B加上,银行系统异常了,最后导致A的余额减少了,B的余额却没有增加。所以就需要事务,将A的钱回滚回去,就是这么简单。
事务的四大特性
- 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。
- 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序 串行执行的结果相一致。
- 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
- 持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
事务并发存在的问题
假设现在有张表
CREATE TABLE `account` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`balance` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `un_name_idx` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据如下
id | name | balance |
---|---|---|
1 | 张三 | 100 |
2 | 李四 | 200 |
更新丢失(lost update)
A和B事务并发执行,A事务执行更新后,提交;B事务在A事务更新后,B事务结束前也做了对该行数据的更新操作,然后回滚,则两次更新操作都丢失了。
- 第一类丢失更新(回滚丢失,Lost update)
事务A和B并发执行,A事务修改数据并提交事务,然后B事务回滚事务,A事务已经提交的数据被回滚。
时间节点 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | - |
T2 | - | 开始事务 |
T3 | 查询账号余额 100 元 | - |
T4 | - | 查询账号余额 100 元 |
T5 | 存入 100 元,账户余额 200 元 | - |
T6 | 提交事务 | - |
T7 | - | 取款10元,账户余额 90 元 |
T8 | - | 回滚事务 |
T9 | - | 账户余额 100 元(A事务更新丢失) |
- 第二类丢失更新(覆盖丢失/两次更新问题,Second lost update)
事务A和B并发执行,A事务修改数据并提交事务,然后B事务修改数据并提交,A事务已经提交的数据被覆盖。
时间节点 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | - |
T2 | - | 开始事务 |
T3 | 查询账号余额 100 元 | - |
T4 | - | 查询账号余额 100 元 |
T5 | 存入 100 元,账户余额 200 元 | - |
T6 | 提交事务 | - |
T7 | - | 取款10元,账户余额 90 元 |
T8 | - | 提交事务 |
T9 | - | 账户余额 90 元(A事务更新被覆盖) |
脏读(dirty read)
A和B事务并发执行,A事务读取了B事务修改后未提交的数据,如果之后B事务进行事务回滚,则事务A读到的数据是不存在的脏数据。
时间节点 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | - |
T2 | - | 开始事务 |
T3 | - | 查询账号余额 100 元 |
T4 | - | 取款10元,账户余额 90 元 |
T5 | 查询账号余额 90 元 | - |
T6 | 存入 100 元,账户余额 190 元 | - |
T7 | - | 回滚事务 |
T8 | - | 账户余额 100 元 |
T9 | 提交事务 | - |
T10 | 账户余额 190 元(数据被B事务干扰) | - |
不可重复读(unrepeatable read)
A和B事务并发执行,A事务读取了B事务修改并已经提交的数据,导致A事务内多次读取同一记录但结果却不相同不稳定。
时间节点 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | - |
T2 | - | 开始事务 |
T3 | 查询账号余额 100 元 | - |
T4 | - | 查询账号余额 100 元 |
T5 | - | 取款10元,账户余额 90 元 |
T6 | - | 提交事务 |
T7 | 查询账号余额 90 元(数据被B事务干扰) | - |
幻读(phantom read)
A和B事务并发执行,A事务读取了B事务提交的新增数据,这时A事务将出现幻读的问题。幻读一般发生在计算统计数据的事务中。
时间节点 | A事务 | B事务 |
---|---|---|
T1 | 开始事务 | - |
T2 | - | 开始事务 |
T3 | 查询ID大于1的账户数量:1 | - |
T4 | - | 新增账户(id:3,name:王五,balance:300) |
T5 | - | 提交事务 |
T6 | 查询ID大于1的账户数量:2 (莫名其妙多出一条数据) | - |
可以看出前面几种问题都偏重于对同一记录的修改更新,幻读偏重于多条记录的增删,它们之间是递进的。
事务的四大隔离级别
ISO标准定义了4个事务隔离等级,分别是
- 读未提交(Read Uncommitted)
- 读已提交(Read Committed)
- 可重复读(Repeatable Read)
- 串行化(Serializable)
MySQL查看和设置隔离级别
查看MySQL当前事务隔离级别
命令:
* show variables like 'tx_isolation'
* show variables like 'transaction_isolation'
* select @@tx_isolation
* select @@transaction_isolation
设置本次会话的事务隔离级别
该设置只对本次会话的事务生效,不影响其他会话,随着本次会话关闭而失效。
set session transaction isolation level read uncommitted / read committed / repeatable read / serializable
设置全局会话的事务隔离级别
该设置不影响当前已连接的会话,新会话使用新的事务隔离级别。
set global transaction isolation level read uncommitted / read committed / repeatable read / serializable
设置一次操作的事务隔离级别
该设置只对下一次事务生效,随着事务的提交而失效。
set transaction isolation level read uncommitted / read committed / repeatable read / serializable
MySQL的事务四大隔离级别
我们通过MySQL看看四大隔离级别的效果
读未提交(READ UNCOMMITTED)
读未提交是隔离级别最低的一种,打开两个MySQL连接,分别设置隔离级别,然后两个事务交替执行,如下:
可以看到,事务A可以读取到事务B未提交的数据(事实上事务B也可以读到事务A的),如果一方回滚事务,则会造成脏读等严重问题。
读已提交(Read Committed)
可以看到,此隔离级别可以有效解决脏读的问题,但还是无法避免不可重复读的问题
可重复读(Repeatable Read)
可以看到,此隔离级别可以有效解决不可重复读的问题,接下来我们看看会不会出现幻读。
显然,还是有幻读的问题。
串行化(Serializable)
可以看到,串行化隔离最严格,可以避免幻读问题,但效率也是最低的。
在选择隔离级别时,需要考虑开发的业务环境和需求,以选择最适合自己业务的隔离级别。
MySQL隔离级别的实现原理
待续...
博主真是太厉害了!!!
不错不错,我喜欢看
怎么收藏这篇文章?
不错不错,我喜欢看 https://www.237fa.com/