事务简介
事物的本质 => 锁和并发的结合体
优势:容易理解
劣势:性能一般
容易理解的模型性能不好,性能好的模型都不容易理解。
ACID保证事务完整性
bob给smith转1000元钱,要么钱在bob这,要么在smith这,不会让其他线程看到bob和smith的钱都为0的情况。
事务 - 单个事务单元
建立一个基于GMT_Modified的索引
读一行记录
写一行记录,同时更新这行记录的所有索引
删除整张表
插入,查询,更新,删除...
每一个操作都可以认为是一个事务。
事务 - 一组事务单元
事务单元之间需要等待需要操作的内容解锁之后,才能执行。
事务 - 产生的原因
事务单元之间的Happen-before关系
- 读写
- 读读
- 写读
- 写写
-
序列化读写
优势 - 不需要冲突控制
劣势 - 慢速 -
事务 - 排它锁
针对同一个单元的访问进行控制
两个事务单元如果没有冲突,就可以并行处理,并行。
如果两个事务单元共享数据,那就串行,加锁,只允许一个线程访问,则其他线程进不来。
- 事务 - 读写锁
写锁读锁分离开,读读并行,读写,写读,写写串行
可重复读:当这个数据读了之后,在锁没有释放的情况下,这次独到的数据,还是上一次的数据。
ACID的I一定意义的破坏了数据一致性,提升了运行速度、
事务 - MVCC
不是在原位写,本质是copy on write
实现了并发读,即写不阻塞读。
维持一个大事务,在运行它的时候,其他读的线程都在等待,导致慢速。
读写锁系统实现简单,MVCC实现复杂。
能够做到写不阻塞读
读 与 写 不错冲突,可以串行。
唯一需要并行的是写写。
并发度最好,系统实现的复杂度最高。
需要考虑的因素变多,
binlog需要维持多少,原来的老数据需要删掉。
事务处理常见问题
-
多个事务,谁先谁后?
-
如何故障恢复?
-
碰到死锁了怎么办?
多个事务,谁先谁后?
数据库使用MVCC后,一定会遇到的问题。
一个读操作发生在一个写操作之后,那么版本号:读 > 写。
逻辑时间戳:
(本质是为了标记事务单元和另一个事务单元之间谁先谁后)
SCN(oracle) , Trx_id(Innodb)等等
故障恢复
可能出现的错误情况
1,业务属性不匹配
业务发现不允许继续执行下去,就需要回滚。
记录下来当前事务前面做的操作的反向操作,来恢复数据。
2,系统崩溃
系统故障恢复回来以后,事务的执行状态是只有一半的,
系统不能继续维持锁,因为线程都消失了,事务操作是水做的不知道了。
在数据库进行恢复的时候,事务只进行到中间一步时,需要做回滚操作。
在数据库回滚完成(recover)之前,外部应用是不能监听使用数据库的。
死锁和死锁检测
2个线程参与
不同方向
相同资源
例子:Bob给smith转账,同时,Smith给Bob转账
尽可能不死锁
【死锁检测方法】碰撞检测:效率最高
【死锁检测方法】等锁超时:常见做法
事务 - ACID
原子性
要么都成功,要么都失败。
记录每执行一条之后的可以返回之前数据的undo sql语句,来保证了整个数据的可回归
一致性
can(happen before)
加锁,保证事务单元与实物单元之间的顺序化
一个事务单元完全完成,才对外可见
如果事务单元之间串行,那么一定是一致性的,但是并发就很差了。
隔离性
以性能为理由,对一致性的破坏
SQL 92 标准定义的隔离性
隔离界别
- (一)序列化读写
排它锁
单位时间内只能有一个人进来
性能差,意味着这个系统不可用
- (二)读写锁
- 可重复读
读锁不能被写锁升级
读读可并行,其他情况要串行
- 读已提交
可以将读锁升级
实现了:读读并行 读写并行(写读不能)
- 读未提交
只加写锁,读不加锁
实现了:
读读并行
读写并行
写渎并行
系统变成了,全部写串行,读操作并行
问题:可能读到写过程中的数据
隔离级别扩展
快照隔离级别
Copy on write 无锁编程
多版本并发控制
事务的ACID
原子性
一致性
- 隔离性(扩展:MVCC/SNAPSHOt ISOLATION)
- 持久性(扩展:持久性保证策略)
-
单机事务的典型异常应对策略
-
事务的调优原则
隔离性
事务版本,有回滚版本。
读未提交的隔离界别 => 因为读没有加锁,可能会出现读到其他事务单元的中间状态、
快照读 能在读到 ** 一致性 ** 的同时实现读未提交
系统会把 快照读映射到序列化读,因为sql92标准本身有问题不完善。
所以就把快照读映射到读未提交,读已提交上面去。
事物的定义滞后于事务的发展
分布式系统会使用新的隔离界别和方法,业务应用的发展已经远远超过传统意义对事物的定义。
持久性
为什么在数据库有实物这个概念,
事务将多个不同的命令组装出成一个事务单元。
记日志,保证每一次的curd操作不丢失,导致速度降低,降低并发度。
每一次的写,要能够保证持久性。
定义:
事务完成以后,钙食物对数据库所做的更改便持久的保存在数据库之中。
丢失数据的可能性
- 物理磁盘损坏(RAID的持久性)
- 如果不是每次写操作,都到磁盘,数据运行在内存,掉电之后数据丢失
但如果每次commit都要fsync都要写到磁盘,就会降低速度
解决方法
一份数据写到两个磁盘
需要保证写操作同时成功,或者同时失败
RAID的持久性
1.提交请求到内存就返回(不可靠)
2.将内存的数据打包到磁盘(定期打包,提升系统吞吐量)
核心目的:提升并行度
在计算机世界非常常见。
补充
tps是个并发指标
脏读的概念
单机事务典型异常应对策略
-
回滚
-
系统down机
Bob给Smith100元的数据库事务
lock bob and Smith 上锁
ver 1: Bob有100元 ,Smith有0元 查询bob有没有钱
ver 2: Bob有0元 ,Smith有0元 把Bob钱减掉
ver 3: Bob有0元 ,Smith有100元 把smith钱加上
Unlock bob and Smith 解锁
重启后进入recovery模式
提交后十五单元继续完成提交
未提交事务单元回滚
之后才提供外界访问,recovery体现了原子性。
为了防止recovery时候挂机,
recovery模式在读取事务执行过程中记得undo回滚日志时,也需要记录日志
事务调优原则
在不影响业务应用的前提下
- 减少锁的覆盖范围
myisam表锁 -> innodb 行锁
原位锁 -> MVCC
- 增加锁上并行的线程数
读锁写锁分离,允许并行读取数据
多线程并行读取
- 选择正确的所的类型
悲观锁
使线程到blocking状态,通知消息ok的状态切换回等待状态
适合并发症强比较严重的场景
乐观锁
适合并发症强比较不严重的场景
悲观锁,乐观锁,原位锁,排他锁,艘可以是读锁或写锁。