事务特性:acid;aid为了实现c
原子性:一个事务要么全执行,要么全不执行-》回滚
sqlite是备份
其他是失败执行语义的反向操作-》算法
一致性:
完整性约束在一个事务执行后没有被破坏:主键约束,外键约束,自定义约束:转账两个账户余额的和应该不变 -》约束和回滚实现
持久性:
无论发生什么:崩溃等 ,都不会出现磁盘上数据的逻辑错误
其实就是无论发生什么都没有错误(任何错误)
隔离性:
事务之间的影响问题
问题:
脏读:读到了其他事务正在修改过程中的临时数据
事务A正在访问并修改数据, 事务B读到了A未提交的数据(脏数据)
不可重复读:
在事务A中先后读取同一个数据, 两次读取的数据不一样, 针对的是update操作
幻读:
2次读取的过程中,其他事务插入/删除了【有关】数据,针对的是insert和delete操作
事务A按照一定条件进行数据读取, 期间事务B插入或删除了相同搜索条件的新数据, 事务A再次按照原来的条件进行读取时, 发现了事务B新插入或删除的数据. 也就是说, 在事务A中按照某个条件先后两次查询数据库, 两次查询结果的条数不同, 这种现象叫做幻读
脏写:将临时无用数据写入了
第一类丢失更新:事物A 回滚时 让事物B已提交的数据都被回退了
第二类丢失更新:事物A读取数据后,事物B修改了数据,但是事物A仍然带着第一次读取的数据进行提交,导致事物B的修改丢失。这个问题有点像读已提交。
解决方法-》加锁:
写锁(排他锁):给数据加上,其他事务 不能写,也不能读,而且数据只能加一个,其实也可以读-》但是不保证【问题】的发生
但是注意:写锁对于自己这个事务来说,等于是不加锁的
读锁(共享锁):给数据加上,所有事务不能写(只有晋升为写锁才能写),只能读,数据可以加多个事务的。
晋升写锁:数据只加了一个事务的读锁
但是注意:读锁对于自己这个事务也是加锁的,整个事务的所有读都一样
范围锁:一定操作范围内的数据不能被读取和修改,用于解决幻读问题
加锁后的结果-》隔离级别:
1 可串行化:对事物所有读写的数据,都加上读锁、写锁、和范围锁 就能实现串行化,效率低
2 可重复读:对事物涉及的数据加全周期的读锁,写锁;这样全周期读的数据都一样(读锁),也可以保证事务结束写操作实现(写锁):其他事务不能操作。
但是没有解决幻读问题
3 读已提交:事物A读取数据部分加读锁,但是未施加全周期的读锁,但是没有解决幻读问题,可重复读问题
不可重复读:事务中可能有2次读取数据部分,这样如果其他事务修改,就2次读取数据不一致了-》不可重复读了
4 读未提交:对事物涉及的数据只加全周期的写锁,没有解决幻读问题,可重复读问题,脏读问题,但是没有脏写问题
脏读:全周期的写锁,相当于自己不加锁,自己可能读到自己的脏数据(临时数据)
5 完全不加锁:有所有问题
出现脏写问题,就已经连事物的原子性都无法保证了。所以一般隔离级别都不会包括他
完全不隔离还会导致第一类丢失更新的问题,就是事物A回滚时,使得事物B已提交的数据被修改。
读未提交解决第一类丢失问题
可重复读解决第二类丢失问题
MVCC 无锁的隔离场景优化方案:主流的商业数据库都采用了这种方案。
全称是 Multi-Version Concurrency Control 多版本并发控制
MVCC的基本思路
MVCC的"无锁"是特指读取数据时,不需要加锁。
MVCC的基本思路是对数据库的任何修改都不会覆盖之前的数据。而是产生一个新版本副本与老版本共存,以此达到读取时可以完全不加锁的目的。
新版本和老版本,可以理解为每一行记录都有两个看不见的字段,CREATE_VERSION和DELETE_VERSION,这两个字段记录的都是事物的ID。这里事物的ID是一个全局严格递增的数值。
数据被插入时:CREATE_VERSION记录插入数据的事物ID
数据被删除时:DELETE_VERSION记录删除数据的事物ID
数据被修改时:将修改视为“旧数据删除、新数据新增”则原有数据的。DELETE_VERSION和新数据的CREATE_VERSION为本次修改数据的事物ID。
MVCC优化事物隔离的场景
隔离级别是可重复读:总是读取CREATE_VERSION 小于等于当前事物ID的记录,如果有多个就取事物ID最大的一个。这样事物A在读取数据时,事物B更新了数据产生了新的事物ID的CREATE_VERSION记录也不会被读取,事物A仍然读取的是其事物开始时的数据。这个数据不会被更改,天然不需要加锁。
隔离级别是读已提交:每次读取最新的版本即可,这样每次都是读取到最后Commit的数据。
可串行化和读未提交用不上MVCC
串行化的目标是阻塞其他事物的读取和写入,MVCC是做读取时无锁优化的,自然是用不上。
读未提交 是需要直接读取未提交的数据,这种场景下直接修改原始数据即可,无需版本字段。
写入+写入的场景下MVCC无能为力
MVCC针对的是“读+锁”场景的优化,对于“写+写”的场景加锁几乎是唯一可行的解决方案。
2.3.5 乐观加锁和悲观加锁
一般来说提到的锁都是悲观锁(Pessimistic Locking)数据库认为需要先加锁再访问数据,不然肯定会出问题。而乐观锁策略(Optimistic Locking)认为竞争是偶然情况,没有竞争是普遍情况,应该一开始不加锁出现竞争时在补救。这种思路是 (Optimistic Concurrency Control,OCC)“乐观并发控制”
乐观锁和悲观锁的性能优劣主要看并发竞争的剧烈程度,如果竞争剧烈乐观锁反而更慢。