一.并发控制
为什么要进行并发控制?
并发的任务对同一个临界资源进行操作,如果不采取措施,可能导致不一致,故必须进行并发控制。
技术上,通常如何进行并发控制?
通过并发控制保证数据一致性的常见手段有:
-锁
-数据多版本
二.锁
如何使用普通锁保证一致性?
(1)操作数据前 ,锁住,实施互斥,不允许其他并发任务操作
(2)操作完成后,释放锁,让其他任务执行
存在的问题?
太过粗暴,读任务也无法并行,任务执行过程本质上是串行的
于是出现了共享锁与排他锁
(1)共享锁(Share Locks,记为S锁),读数据时加S锁
(2)排他锁(eXclusive Locks,记为X锁),修改数据时加X锁
共享锁与排他锁的玩法
(1)共享锁之间不互斥,读读可以并行
(2)排它锁与任何锁互斥,写读,写写不可并行
写数据的任务没有完成,数据不能被其他任务读取,这对并发有较大影响
有没有可能进一步提高并发?
数据多版本
三.数据多版本
数据多版本是一种能进一步提高并发的方法,核心原理是:
(1)写任务发生时,将数据克隆一份,以版本号区分;
(2)写任务操作克隆的数据,直至提交;
(3)并发读任务可以继续读取旧版本数据,不至于阻塞;
提高并发的演进思路:
(1)普通锁,串行执行
(2)读写锁,读读并发
(3)数据多版本,读写并发
对应到InnoDB上,是怎么玩的?
四.redo,undo,回滚段
为什么要有redo日志?
数据库事务提交后,必须将更新后的数据刷到磁盘上,以保证ACID特性。磁盘随机写性能较低,如果每次都刷盘,会极大影响数据库吞吐量。
优化方法是,将修改行为先写到redo日志里(此时变为了顺序写),再定期将数据刷到磁盘上,能极大提高性能。
这里的设计方法:随机写优化为顺序写
如果某一时刻数据库崩溃,还没来得及刷盘的数据,在数据库重启后,会重做redo日志里的内容,保证已提交事务对数据产生的影响都刷到磁盘上。
一句话,redo日志用于保障已提交事务的ACID特性。
为什么要有undo日志?
数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到undo日志里,当事务回滚时,或者数据库崩溃时,可以利用undo日志,即旧版本数据,撤销未提交事务对数据库产生的影响。
一句话,undo日志用于保证,未提交事务不会对数据库的ACID产生影响。
什么是回滚段?
存储undo日志的地方,是回滚段。
undo日志和回滚段和InnoDB的MVCC密切相关,这里举个例子展开说明一下。
表:
t(id PK,name)
数据为:
1, shenjian
2, zhangsan
3, lisi
此时没有事务未提交,回滚段为空
接着启动了一个事务:
start trx;
delete(1, shenjian);
update set(3,lisi) to (3,xxx);
insert(4,wangwu);
并且事务处于未提交状态
可以看到:
(1)被删除前的(1, shenjian)作为旧版本数据,进入了回滚段;
(2)被修改前的(3, lisi)作为旧版本数据,进入了回滚段;
(3)被插入的数据,PK(4)进入了回滚段;
接下来,加入事务rollback,可以通过回滚段里的undo日志回滚
可以看到:
(1)被删除的旧数据恢复了;
(2)被修改的旧数据也恢复了;
(3)被插入的数据,删除了;
事务回滚成功,一切如故
四.InnoDB是基于多版本并发控制的存储引擎
MVCC就是通过“读取旧版本数据”来降低并发事务的锁冲突,提高任务的并发度
核心问题:
旧版本数据存储在哪里?
存储旧版本数据,对Mysql和InnoDB原有架构是否有巨大冲击?
(1)旧版本数据存储在回滚段里;
(2)冲击不大
InnoDB的内核,会对所有row数据增加三个内部属性:
(1)DB_TRX_ID,6字节,记录每一行最近一次修改它的事务ID;
(2)DB_ROLL_PTR,7字节,记录指向回滚段undo日志的指针;
(3)DB_ROW_ID,6字节,单调递增的行ID;
InnoDB为何能做到这么高的并发?
回滚段里的数据,其实是历史数据的快照(snapshot),这些数据时不会被修改,select可以并发读取它们。
快照读(snapshot read):这是一种一致性不加锁的读,就是InnoDB并发如此之高的核心原因之一。
这里的一致性指的是:事务读取到的数据,要么是数据开始前就已经存在的数据,要么是事务自身插入或者修改的数据。
什么样的select是快照读?
什么样的select是快照读?
除非显示加锁,普通的select语句都是快照读,例如:
select * from t where id>2;
这里的显示加锁,非快照读是指:
select * from t where id>2 lock in share mode;
select * from t where id>2 for update;
总结
(1)常见并发控制保证数据一致性的方法有锁,数据多版本;
(2)普通锁串行,读写锁读读并行,数据多版本读写并行;
(3)redo日志保证已提交事务的ACID特性,设计思路是,通过顺序写替代随机写,提高并发;
(4)undo日志用来回滚未提交的事务,它存储在回滚段里;
(5)InnoDB是基于MVCC的存储引擎,它利用了存储在回滚段里的undo日志,即数据的旧版本,提高并发;
(6)InnoDB之所以并发高,快照读不加锁;
(7)InnoDB所有普通select都是快照读;