在线分布式数据库原理与实践
事务简介
锁
并发
优势:容易理解
劣势:性能较低
容易理解的模型性能都不好,性能好的模型都不容易理解。
一个事务单元
例子: Bob只有100块要全部给Smith(0元)
分析:线程①操作Bob的账号时,会加锁,当有其他线程②③要操作时只能等待,保持数据的 一致性
如果没有加锁,当①从Bob账号中减少100,而Smith账号要等2ms才能到账,那在这2ms期间如果②去访问Bob账号,发现Bob里面是0元,Smith也是0元,就出现了一致性的问题,是不能接受的。
线程②③要么是看到Bob有100块,要么是看到Smith有100块,这就是数据库的另一特性 持久性,
而一个事务要么成功,要么失败,不会存在Bob减少了100块,而Smith收不到这100块的情况,这也是数据库的另一特性 原子性
哪些地方用到了事务?
- 写数据、读数据、建立索引、删除表,每个操作都是一个事务。
一组事务单元
例如:① 事务1:Bob给Smith100块,②事务2:Smith给Joe100块
分析:事务2会等待事务1完成后才会开启。
事务单元之间的Happen-Before关系
读写 写读 读读 写写
如何已最快的速度完成事务?又能保证上面四种操作的逻辑顺序?
方式1: 排队法
优势:不会冲突、不会死锁
劣势:性能低
方式2: 排他锁
将一个事务拆分成多个事务,给每个事务加锁,前提是两个事务不能操作同一数据
例如Bob给Smith100, Alice给Joe100块。
反例:Bob给Smith100, Smith给Joe100块。
方式3: 读写锁
只给写加锁,读不加锁(隔离性破坏了一致性)
优势:性能提高
劣势:多次读取的数据可能不一致
方式4: MVCC
写的时候会有一个log记录下当前位置,读的时候会检查是不是跟当前写的匹配,当前写的不匹配,会往前查找,直到查到为止。
本质就是copy on write, 能够做到写不阻塞读(写的时候可以并发的读)
(目前主流数据库采用的是这种的方式)
优势:性能很高。
代价:系统的复杂度变高
事务常见的问题
-
谁先谁后?
一个读请求应该读哪个写之后的数据呢?
逻辑时间戳 SCN(Oracle) Trx_id(Innodb) … -
故障恢复
① 业务数据不匹配
如:转账中账户余额不足,就需要回滚,执行业务的反向操作。
② 系统崩溃
如:给Smith账户增加100时,服务器crash了,重启服务器后会有一个recovery的过程,处于这个过程中的时候其他应用是无法访问的。 -
死锁与死锁检测
产生死锁的原因: 1.两个线程 2.不同方向 3.相同资源
单机事务解决方案:
① 尽可能不死锁
② 碰撞检测
把事务单元和事务单元的锁记录下来,当新申请的锁进来的时候检测有没有已经申请过锁,如果锁已经申请,并没有释放,那么会释放一边的锁。
③ 等锁超时
分布式解决方案:
事务的ACID
原子性:Undo日志回滚到之前的版本
一致性:Can(Happen-Before):所有操作完成之后才会可见,否则不可见。
强一致性:加锁,将多个事务顺序执行,并发不高。
隔离性:
- 以性能为理由,破坏一致性
- 排它锁: 序列化读写(Serializable)
- 读写锁:
- 可重复读,读锁不能被写锁升级
- 读已提交(read commited)读锁能被写锁升级
- mvcc: 快照读的情况下能保证在读到一致性的同时实现读为提交
持久性:提交完成后数据就会放入磁盘或网络中的磁盘中。- RAID的持久性
写写有没有可能并行呢?