数据库的书籍中介绍到事务有四大特性:ACID
原子性:一个事务要么全部成功,要么全部失败,回滚到初始状态(Undo日志实现)。
- version1: A 100; B 0
- version2: A 0 ;B 0
- version3: A 0; B 100;
如果B获取失败,第二步失败,需要回滚到version1;
如果B增加失败,第三步失败,需要回滚到version1;
如何实现,需要在version2 后面和version3后面各加一个undo,记录到log表里;
---------------------------------------------------------------------------------------------
一致性:(happen-before),保证能看到系统内的所有修改
- lock A B
- version1: A 100; B 0
- version2: A 0 ;B 0
- version3: A 0; B 100;
- unlock A B
其他事务需要在锁外等待,但是如何做到高效的读写并行?
----------------------------------------------------------------------------------------------
隔离性(读已提交,读未提交)
提升性能,破坏一致性。
序列化读写(serializable),采用了排他锁,强一致但是性能差。
可重复度(repeatable read),采用了读写锁,读锁不能被写锁升级,(读读并行)。
读已提交(read committed),采用了读写锁,但是读锁可以被写锁升级,(读读并行,读写并行,写读不可以)
读未提交(read uncommitted),只加了写锁读未枷加锁(读读并行,读写并行,写读并行),可能读到中间状态。
隔离性扩展(MVCC)快照隔离级别(SNAPSHOT):
本质上是copy on write + 无锁编程
传统的是读锁,在外面等等待。
MVCC:版本号的控制,每一次的更新,在旧版本的回滚段里。如果当前事务被占用,并发的事务可以从回滚段里上一版本里面的undo的数据。 这个时候可以保证读写并行和写读并行。
这种方式效率和读未提交的效率差不多,但是他能在保证读一致性的前提下读到数据,实现读未提交。
缺点:写>读 时会增加系统成本。纯写操作会记录大量的log
-----------------------------------------------------------------------------------------------
持久性
事务完成后,事务对数据库所做的更改持久化保存到数据库里,但是如何保证数据不丢失?
RAID的持久性 (持久性?延迟?)
ABC三块磁盘,如果实现A坏掉了数据丢失?
每一次commit都会内存到磁盘,内存数据丢失?系统性能下降?
RAID controller 同时写入ABC中(也是个事务
- 提交请求到内存后返回
- 打包内存数据到磁盘(块存储,达到块的大小存储磁盘速度快,攒够一批打包磁盘)
ACID核心目的就是为了:提升系统的并行度。
异常应对:
- 系统down机器,事务原子性操作只有一个标记commit,之后的请求必须正常的完成。
- 重启后进入recovery,将提交后的事务单元继续完成提交,未提交的食物单元回滚。
此时会根据日志进行恢复,并且关闭端口,保证ACID,和数据恢复。
事务调优原则:
在不影响业务应用的前提下:
- 减少锁的粒度,
Myisam的表锁->InnoDB的行锁
MVCC也是减小锁的范围,共享数据变成了很小的版本,每个版本都可以加锁,粒度很细。
- 增加锁上可并行的线程数,
读锁写锁分离,允许并行读取数据。
多线程并行读取
允许更多人读取
- 选择正确的类型
悲观锁 blocking状态,锁ok之后,进入才等待,换入换出。
适合并发争抢比较严重的场景
乐观锁
适合并发争抢不太严重的场景