深入思考一下mysql的事务
事务的持久性:
一条更新语句是怎么执行的?
这里面涉及了 redolog 和 binlog (其实还有 unlog不过在这个地方没有说,先埋个坑》》》》》》》》》》)
使用MySQL是为了存储数据的,其实也就是数据的持久化,数据存储到磁盘中才会实现持久化。
update T set count=count+1 where id=2 (比如 执行这一条sql语句)

可以看到一条sql语句的执行,既有 binlog又有redolog. binlog与redolog主要有三点不同!!
binlog是SQL Server层的日志,是逻辑日志,可以存储的是输入的sql语句,与存储引擎无关系,用于数据备份!
redolog是Innodb存储引擎的日志,是物理日志,有固定的大小的,当容量满了之后,会覆盖前面写的redolog日志,用于数据库出现故障后的数据恢复。
这里的redolog操作了两次,一次是prepare状态,一次是commit状态。那么是变为了Commit状态后,才会进行将redolog持久化到磁盘中的吗?

( 2020/8/5 为什么要有redolog呢? 如果每次在内存中修改了数据库中的数据都将修改后的新值刷新回磁盘,那么就不需要redolog了,但是这太费时了,所以先存到redolog中来节省时间,当redolog的数据满了之后,再进行往数据库的磁盘进行数据的写入! )
【
redolog是循环写的! 大小是固定的,通过 checkpoint 和 write pos来控制。checpoint就是开始写的地址,write pos是现在要写入的位置。
】
redolog的写入分为了两个阶段,prepare 和 commit,这就是两阶段提交,为什么会有两阶段提交?
(或者说: prepare 和 commit这两个状态的作用是什么? 只有prepare 会怎么样?)
两阶段提交是为了保证 redolog 和binlog 两个日志文件中的数据信息保持一致!!!!!
两阶段提交怎么叫保证了数据信息的一致,如果在修改redoLog 为commit状态时失败了呢?
binlog是在commit后一次写入的,而redolog是一直在不停的写,commmit改变状态而已。
可以通过设置参数,来控制 redolog持久化的时机。
要注意一点哈!事务支持是在引擎层实现的! 比如 MyIsam存储引擎就不支持事务!!!
事务的隔离性:
显然是存在了多个并发的事务,才需要隔离,不然根本就没必要隔离!!
因为并发,也就出现了一些问题: 脏读、不可重复读、幻读
为了解决这些问题,引入了隔离级别: 读未提交、读已提交、可重复读、串行化
那么这些隔离级别是怎么实现的呢?(这才是思维层面的东西,怎么去解决一个现实存在的问题!!!!)
串行化:通过加锁实现的,这个很容易理解。 不过锁根据锁的粒度不同,也分为 : 表锁、页锁、行锁、间隙锁、next-key等
在每条记录更新的时候都会在回滚日志(undolog)中同时记录一条回滚操作。当事务出现报错时,就可以通过这个回滚日志进行回滚操作,同时也是通过这个实现的MVCC, 每个事务都有自己的版本号(或者称为视图?)。
在select 查询的时候,就是通过版本号结合这个undolog来进行回滚,一直回滚到当前版本号下的值,然后返回? 还是直接在undolog中不用进行类似回滚的操作,就能直接在回滚日志中得到结果??(这取决与回滚日志中,存储的是当时版本下的值,还是恢复到当时版本下的值的操作,这体现了就是思想!!!!!一个目标的实现有多种具体方式,但是选择哪种,体现的就是设计者自己的思想。)
所以,我们可以通过MVCC来实现可重复读的操作。读已提交也是通过这种方式实现的!不过就是查询操作该记录的最新的版本号的值,并且是已提交的状态!(所以在回滚日志中,还是记录事务是否已经commit对不对???)
回滚日志中的内容一直存在吗? 当然不是了,当事务执行完成,commit成功后,这个事务对应的回滚日志其实就可以删除了,但是由于可能还会存在比它版本号还低的事务没有提交,那么就不能删除该回滚日志,因为有可能版本低的事务还会用到,比如借助它的回滚日志实现可重复读??。
所以,最好不要用到长事务!!!
mysql通过 :
1)begin 或者 start transaction来开启事务, 通过commit或者 rollback来提交或者回滚事务。
(在执行commit之前,事务中的操作就在内存中操作了是不?并且写入到了redolog、binlog、undolog中了,执行commit操作,只是加了个标记而已? 表示成功执行了,并且修改redoLog中的状态?让其持久化? 如果不是commit状态,redoLog不会进行持久化?)
所以,一个重要的问题! 回滚日志存储的是什么? 回滚的操作还是之前的值?
2)set autocommit=0 关闭掉这个线程的自动提交, (思想都是相同的啊,rabbitMQ中也有自动确认类似的思想),这样事务就自动开启,直到执行commit或者rollback才结束。 所以,大佬建议,不要关闭自动提交,容易产生长事务!
全局锁:将所有的表都锁住,这时只能进行读操作,用在全局备份的时候。(主要就是 MyIsam再用,如果InnoDB,可以用MVCC来解决)
flush tables with read lock
表锁: 注意一点,这里的加锁是某个线程进行的加锁,那么在并发情况下(多线程),其它线程就会被阻塞了
行锁:有可能会造成死锁!在一个线程的一个任务在执行update语句的时候,会锁住它所要操作的记录行,那么其它线程就没法进行访问了!!(这里的锁的底层的和java中的锁应该差不多,思想应该是一样的)。直到事务提交完成,锁才会被释放,在这之前,其它线程要操作这个被锁定的行,就会发生阻塞!(看到了没有,有死锁产生的条件了,如果各自占有了某行然后试图去操作对方的行,就发生死锁了)
解决死锁的方式:
1)直接就阻塞等待,然后等待一定时间后式释放掉资源 (这个等待时间比较长,线上业务不太可行)
2)进行死锁检测 (innodb_deadlock_detect 参数设置为on, 当然默认也是开启的!),这个会消耗CPU资源比较严重!通过控制并发度来解决这个问题,保证对某一行进行更新的线程的数量控制在一定范围内。
undolog实际上是和表存在一起的!!每行记录都会有一个版本号,用来记录最新的创建或者修改这个记录的版本号,还有还有一个指针,执行它之前的信息及之前的版本号,之前的信息又会执行更前一次的信息,这就是undolog(回滚日志!!!)
在MVCC下,select查询就是进行的这种快照读!!!快照读,就是在事务创建的时候,为每个事务添加一个版本号。在执行select查询的时候,只会查询到版本号小于或者等于这个版本号的记录,版本号大于这个事务id (版本号)的, 不可见!
更新数据是先读后写的,这个读就是当前读, 也就是读取的最新数据!如果A事务更新了某一行,但是A事务还没有提交,此时B事务要来更新这一行,怎么办? B事务所在的线程会被锁住!!! 对记录进行更新操作时会对该行进行上锁,提交完事务后,才会解锁!!
事务的回滚是怎么实现的呢?(原子性!)
浙公网安备 33010602011771号