什么是事务
- 概念
逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败 - 举例
举例而言,支付宝转账,这一个操作可以分解成两个单元,转账人扣掉转账的金额,收款人收到转账的金额,那么什么情况下才可以算转账成功呢?自然是转账人被扣款了,收款人也要收到款项了,这样这个操作才算成功了,因此转账这个操作就是一个事务,只有两个单元同时成功了才算成功,若两者中的任何一个单元失败,则两个单元都应该失败,而不会也不应该出现一个单元成功另外一个单元失败的情况。
事务的特性:
- 原子性
事务不可分割
以转账的例子而言,转账这个事务的原子性也即是说:转账的转出部分和转入部分不可以分开 - 一致性
事务执行数据前后一致
以转账的例子来说,转账这个事务的一致性也就是说:转账前转出前账户A的金额 + 转入前账户B的金额 = 转出后账户A的金额 + 转入后账户B的金额,也就是说总体的金额是不会改变的。也就是不会出现我们之前说的两个单元中一个成功一个失败的情况,假设会出现一个成功一个失败的情况:转出成功了,账户少了100,但是转入失败了,转账操作成功了,那么总的金额是不是就少了100,这样就不满足事务执行前后数据一致性了 - 隔离性
一个事务不应该受到其他事务的干扰
以转账的例子而言,当用户A和用户B同时并发的访问数据库,操作账户表(包括账户名–账户金额字段),数据库会为每一个用户的操作开启一个事务,事务和事务之间是相互感知不到的,相互隔离的。
如果事务的隔离性得不到保障的话就会出现读问题,下文会详细介绍 - 持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
事务的四种隔离级别
-
Read Uncommitted(读取未提交内容)
所有事务都可以看到其他未提交事务的执行结果
该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
例:用户A向用户B转账100元,但是不将该操作提交,读未提交的情况下,B查看账户时发现钱确实已到账,但用户A转账这个操作只要不提交,事务就会被回滚,之后B再查看账户时会发现钱没到账,这也就是出现了脏读。 -
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
导致这种情况的原因可能有:
有一个交叉的事务有新的commit,导致了数据的改变;
第一次
start transaction
A: select money form account where id = 1;(事务开启)
结果mondy:20
B: update account set money=10 where id=1;(事务开启)
B: commit(提交)
第二次 在事务结束之前
A: select money form account where id = 1; 读到了B事务更新的数据
发现结果是10
这样就出现了两次读数据结果不一样 -
repeatable 重复读
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
这样可以避免前面读提交中不可以避免的不可重复读,因为不再允许修改操作,但是不可以避免幻读,因为并没有不允许插入操作。 -
serializable 可串行化
可串行化是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。