这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
认识事务
场景:用户从A账号转账100元到B账号 。
可能出现的问题:
- 转账操作的第一步执行成功A账户上的钱少了100元,但是第二步执行失败或者未执行便发生系统崩溃,导致B账户没有相应增加100元。
- 转账操作刚完成就发生了系统的崩溃,系统重启恢复时丢失了崩溃前的转账记录
- 同时又另一个用户转账给B账户,由于同时对B账户进行操作,导致B账户金额出现异常
事务的概念:数据库事务是访问并可能更新数据库中各种数据项的一个程序执行单元
事务的组成:一个数据库事务通常包含对数据库进行读或写的一个操作序列
一个典型的数据库事务
BEGIN TRANSACTION //事务开始
SQL1;
SQL2;
COMMIT/ROLLBACK //事务提交或回滚
事务的特性相关
- 数据库事务可以包含一个或多个数据库操作,但这些操作构成逻辑上的一个整体。
- 构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部执行不成功。
- 构成事务的所有操作,要么全都对数据库产生影响,要么全都不产生影响,即不管事务是否执行成功,数据库总能保持一致性状态。
- 以上即使在数据库出现故障以及并发事务存在的情况下依然成立
事务是如何解决前面的问题?
解决方式:对于上面转账的例子,可以将上面转账相关的所有操作包含在一个事务中
BEGIN TRANSACTION //事务开始
A账户减少100元
B账户增加100元
COMMIT/ROLLBACK //事务提交或回滚
- 当数据库操作失败或者系统出现崩溃,系统能够以事务为边界进行恢复,不会出现A账户金额减少而B账户未增加的情况。
- 当有多个用户同时操作数据库时,数据库能够以事务为单位并发控制,使多个用户对B账户的转账操作相互隔离
事务使系统能够更方便的进行故障恢复以及并发控制,从而保证数据库状态的一致性。
事务的特性
原子性 Atomicity :事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
一致性 Consistentcy :事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态。
隔离性 Isolation :并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样
持久性 Durability :事务一旦提交,其对数据库的更新就是持久的。任何事务和故障都不会导致数据丢失。
事务的并发异常分析
丢失更新 Lost Update :是指事务覆盖了其他事务对数据的已提交修改,导致其他事务的修改好像丢失了一样。
脏读 Dirty Read :是指一个事务读取了另一个事务未提交的数据
不可重复读 Unrepeatable Read :指一个事务对同一数据的读取结果前后不一致
幻读 Phantom Read :指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。
事务的隔离级别
事务操作同一批数据的时候所导致的问题可以通过设置隔离级别来解决。
隔离级别的分类(从低到高)
- 读未提交 READ UNCOMMITTED
- 读已提交 READ COMMITTED -- ORACLE 默认级别
- 可重复读 REPEATABLE READ -- MYSQL 默认级别
- 串行化 SERIALIZABLE
隔离级别从小到大安全性越来越高,但是效率越来越低
丢失更新
- 悲观锁机制
假定这样的问题是高概率的,最好一开始就锁住,免得更新老是出错。
添加共享锁方式:select * from account lock in share mode ;
添加排它锁方式:select * from account for upfate ;
- 乐观锁机制
假定这样的问题是小概率的,最后一步做更新的时候再锁住,免得锁住时间太长影响其他人做有关操作。
在表中增加一个类型是timestamp字段,并将其设置只要该表进行插入或修改操作时都会更新改字段为最新时间。
在修改数据时通过检查timestamp是否改变判断出当前更新基于的查询是否已经是过时的版本。