MySQL(二)
事务的四个特性和四种隔离级别都只是一些数据库中的概念,不同的数据库对这些的处理方式不同。
事务的四个特性
事务是一种包含执行SQL还有怎么进行SQL执行的操作序列,例如一条SQL我们开启了事务可以选择隔离级别和传播行为,数据库会根据我们的定义使用对应的方法执行SQL语句。(例如下文讲到的MySQL中ReadView在开启不同的隔离级别时会在不同的时期生成ReadView)
原子性(Atomicity):事务中的操作要么都执行,要么都不执行。
一致性(Consistency):事务开始前和开始后,数据的完整性不被破坏
隔离性(Isolation):每个事务之间互不干扰,为了不同的需求有了四个隔离级别(读未提交、读已提交、可重复度、串行化。四个隔离级别都是在高并发时才有的概念。)
持久性(durability):在事务提交之后,不管数据库有没有发生故障,此修改都不会改变。(例如在事务提交完成后数据库突然崩溃等情况都不会导致数据出现问题)
四种隔离级别
读未提交
事务A在进行读取操作时,事务B发生了更新操作,这时事务A读取到了事务B更新完后的操作,即关羽。但是如果事务B没有提交这个操作,而是对这个操作进行了回滚,那么这个数据显而易见是有问题的。这就是脏读
读已提交
事务A在进行读取操作时,事务B进行了更新操作。但是和读未提交不同的是,如果在事务A期间事务B没有进行提交操作,那么读取到的还是原先的数据.(这些都是数据库的一些概念知识,不同的数据库有不同的实现方式)。但是如果在此期间,事务B重复的对数据进行了更新,例如第一次更新为”关羽“,但是在第二次更新为了张飞,那么事务A的第一次读取和第二次读取到的数据不同。这便是不可重复读。(四种隔离级别讨论的都是在高并发时可能会产生的问题,即默认背景环境为高并发环境)。
可重复读
可重复读是在事务A期间不管事务B怎么进行操作,都只读取到第一次读取时的数据,在MySQL采用的时(MVCC)读取快照的方式来进行解决的。底层是使用undolog和ReadView来进行实现的(下文介绍)。但是如果事务A是范围查询,而此时有一个事务进行了插入数据操作并进行了提交,而这条数据是可以被事务A查询得到,有别的事务进行插入操作这导致来本来读取的数据是不会改变的,但是导致事务A读取到了不同的结果,这便是幻读。
串行化
即每个事务串行执行,这就没有并发问题,自然不会出现上述的脏读、不可重复读、幻读问题。
MySQL的MVCC实现(防止高并发问题有两种解决思想,分别是锁和快照,此处介绍的便是快照方式)
MySQL的MVCC是借助undolog和ReadView来进行实现的。
注意:MVCC不可以解决写-写问题,可以解决读-写问题
undolog
在undolog中存储了每条数据的迭代历史,例如git的版本控制,但是和git的版本控制不同的是undolog中的数据记录方式不同。git中记录的是相对上一个版本进行了那些修改,但是在undolog中记录的是四个字段(事务id、回滚指针、主键ID、记录内容)
事务id:在MySQL中全局自增,每一个事务的事务id都不相同
回滚指针:指向上一个版本的地址
主键id:数据的主键
记录内容:数据的内容
ReadView的创建
在创建时会先将当前数据库中活跃的事务(即还未提交的·事务)id取出来,组成一个数组并按降序排序,在ReadView中分别有一个最小的事务id值和最大的事务id值,最小id值即数组中的最小值,最大id值即即将产生的事务id。
在undolog中取出当前记录的记录。并对记录中的版本进行可见判断,判断规则如下:
如果id小于ReadView中的最大值,并且大于ReadView中的最小值,并且事务处于不活跃状态的即可见。最后取出可见数据中的事务id最大的值。
如果时读已提交隔离级别,则在每次进行select操作时都进行ReadView的构建,而可重复都指在事务的第一次select时进行创建。
锁LBCC
按照颗粒分有全局锁、表级锁、行级锁
按照功能分有读锁和写锁
全局锁
给整个MySQL加上锁,一般不用。
加锁方式
flush tables with read lock;
释放全局锁
unlock tables;
表级锁
表级锁分为四种,读锁、写锁、元数据锁和自增锁。
读锁又称共享锁,两个事务可以同时给同一记录加读锁
写锁又称排他锁,一般为增删改时加上。
元数据锁是保护在读取数据时保护表结构不会被修改
自增锁
行级锁
三种行级锁都是MySQL数据库自动添加,不需要手动进行加锁或是释放锁
行级锁分为三种,记录锁、间隙锁和临键锁
记录锁:在delete、update和唯一主键的for update和for share时默认添加,将此纪录上锁。(除了for share时添加读锁,其他添加写锁)
间隙锁:在唯一索引未命中时添加,例如有主键分别为1,5,10的数据。当条件为3时,1-5之间的间隙将被上锁。
临键锁:由记录和记录前的间隙组成。默认加锁加的就是临建锁。
主键索引
等值查询:找到加上记录锁,未找到加上间隙锁
范围查询:范围内没有找到数据加间隙锁,如果找到数据则在找到数据位置加上临键锁
辅助索引
等值查询:找到记录添加记录锁,辅助索引找到位置添加间隙锁。未找到记录,辅助索引加间隙锁
范围查询:找到记录添加记录锁,辅助索引找到位置添加间隙锁。未找到记录,辅助索引加间隙锁
死锁
当两个或多个事务互相持有对方需要的资源时发生
例如
事务A在执行删除id=5的记录,事务B在删除id=6的数据,之后事务A需要更新id=6的数据,而事务B需要更新id=5的数据,此时双方都需要对方的资源就造成了死锁。
但是在MySQL中会自动探知死锁,当探知到了死锁之后会先将事务影响小的事务回滚,当另外的事务操作完成之后再执行此事务。
如何避免死锁
1、防止交叉更新
2、尽量保持事务的轻量
3、提高事务的运行效率(例如sql优化)
4、尽快提交事务
的资源就造成了死锁。
但是在MySQL中会自动探知死锁,当探知到了死锁之后会先将事务影响小的事务回滚,当另外的事务操作完成之后再执行此事务。
如何避免死锁
1、防止交叉更新
2、尽量保持事务的轻量
3、提高事务的运行效率(例如sql优化)
4、尽快提交事务