Mysql事务原理与锁机制
参考文章:https://lish98.blog.youkuaiyun.com/article/details/124130816
查看当前事务隔离级别
//查看当前事物级别:
SELECT @@tx_isolation;
设置 mysql 事务隔离级别
//设置read uncommitted级别:
set session transaction isolation level read uncommitted;
//设置read committed级别:
set session transaction isolation level read committed;
//设置repeatable read级别:
set session transaction isolation level repeatable read;
//设置serializable级别:
set session transaction isolation level serializable;
mysql 的事务隔离级别
-
read uncommitted 读未提交
set session transaction isolation level read uncommitted; BEGIN; UPDATE user_balance SET balance = balance + 500 WHERE id = 1;
set session transaction isolation level read uncommitted;
BEGIN;
SELECT * FROM user_balance WHERE id = 1
最终读取到未提交的数据(脏读)
-
read committed 读已提交,
- 解决了脏读问题,存在不可重复读问题
-
repeatable read 可重复读
-
解决可重复读问题
-
可重复读时,还未提交事务,
先修改
lisi 的balance为5000(事务开启前是10000),
再从事务中查询 lisi 的balance ,结果仍然是10000
- 存在幻读问题
串行化: 可以解决以上所有问题(个人理解:不存在并发,不就没有并发问题了嘛)
事务是如何实现的
-
事务的 隔离性 由 锁机制 实现。
-
而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。
-
REDO LOG 称为重做日志,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
-
UNDO LOG 称为回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
undo log
用来保证事务的原子性、一致性
类似写时复制
解释:当有多个调用者请求同一个资源时,调用者都会获得同一个初始资源,其中有调用者请求修改资源时,系统会复制一个模板资源,供该调用者修改,此时,其他调用者读取到的仍然是初始资源
undo log也是类似,当 mysql 进行 INSERT、UPDATE、DELETE
时,回滚时都会分别执行一次 DELETE、UPDATE、INSERT
,而mysql为了记录这些回滚的内容就引入的 undo log
。SELECT
查询不会记录到 undo log
中(毕竟不会修改数据)
undo log的作用
原文:https://blog.youkuaiyun.com/qq_36389060/article/details/124130816
回滚数据
会有人对undo日志有误解:undo用于将数据库物理的回滚到语句执行之前或事务之前的样子,而事实是,undo是逻辑日志,因此只是将数据库逻辑地恢复到之前的样子,该事务中所有的修改语句都被取消,但是数据结构和页本身在回滚之后可能大不相同(应该是其他事务完成了commit成功修改了其他数据)
比如:一个事务在修改当前一个页的某几条数据,同时也有其他事务也在修改同一页的另外几条数据,因此,不能将改页回滚到事务开始时的状态,会对其他事务的工作造成影响
MVCC(记录版本链)
undo的另一个作用是MVCC,即在innoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过hdo读取之前的行版本信息,以此实现非锁定读取
。解决读写同时高并发
以上三个隔离级别(除了串行化)都存在脏写问题(非常严重)
举个栗子:我们通过java读取到数据库中的值为 500,然后我们通过java将其减少 200 ,结果为 300 提交给数据库,此时,就可能会出现脏写问题
,注意,我们是抽出数据来操作后再返回给数据库,而数据库中该条数据可能已经发生了改变(比如在修改的时候恰好变成了800),那么我们提交的时候,这个800就会被改为300,这样就出现了脏写问题
其一解决方法: 不要在java(数据库外)更新数据库里面的数据,利用mysql的行级锁,可以用 update语句进行修 update account set balance=balance -200 where id 1;
面试题:查询操作方法需要使用事务吗
(rr,可重复读)需要加 读事务,readonly = true(spring中的)
举个栗子:
select * from user_balance where id = 1
,查询出zhangsan的balance为 400,此时,我们将lisi的balance由5000改为 10000,而如果我们加了事务,我们执行select * from user_balance where id = 2
查询出的lisi的balance仍然是原来的 5000,如果不加,查出来的就是10000
也就是加了事务,那么我们查出来的几个数据肯定是出自同一个时间点
如果查询操作较多的系统,采用 repeatable read 可重复读
,因为可能要不断生成报表
而对并发要求高的 则采用 read commit 读已提交,没有什么报表,rc 读操作可以不加事务,那么性能会更高