003|事务到底可见不可见

一、事务的理解

查看数据库的事务配置:

mysql> show variables like 'transaction_isolation';

+-----------------------+----------------+

| Variable_name | Value |

+-----------------------+----------------+

| transaction_isolation | READ-COMMITTED |

+-----------------------+----------------+

1、什么是事务

事务是指一条或者一组语句 组成一个单元,这个单元要么都成功要么都失败。

 

例如:张三有1000元,李四有1000元;但是 张三给了李四400,那么李四就有1000,张三还剩 500;

update table user set money=500 where name = "张三";

update table user set money=1500 where name = "李四";

那么 我们肯定要保证这两条语句要么都成功,要么都失败。这时候就用到了事务。

 

2、事务的4大特性(ACID

A(原子性 Atomicity):

原子性指事务是不可分割的,要么都执行成功,要么都失败;

C(一致性Consistency):

事务必须使得数据库状态从一个一致性状态,变为另一个一致性状态;

I(隔离性 Isolation):

指一个事务的执行,不被其他事务所影响;

D(持久性Durability):

持久性指一个事务一旦提交之后 对数据库饿修改是永久的;

 

二、创建一个事务

事务其实可以分为两大类:隐式事务 和 显示事务

隐式事务:SET IMPLICIT_TRANSACTIONS ON

比如我们 select、insert、update、delete  都是隐式事务;

 

显示事务

指带有明显开始和结束标记;

步骤一:禁用自动提交

set autocommit = 0;

 

步骤二:开启事务

start transaction;

步骤三:sql 语句

update table user set money=500 where name = "张三";

update table user set money=1500 where name = "李四";

步骤四:结束事务 --> 显示提交或回滚

commit 或 rollback;

 

自动提交事务:

指一旦执行 自动提交;

 

三、事务的隔离级别

上面说的事务,单个执行都不会有问题,但是当多个事务同时执行的时候,就会出现经典的三个问题,

脏读,幻读,不可重复读;

 

脏读

两个事务,事务A读取了一条事务B修改过的数据,事务B发现不对,我不能修改,进行了事务回滚,事务A 读到的就是无效的数据;

 

不可重复读:

两个事务,事务A多次读取同一条数据,事务B在事务A读取的过程中,不断的修改并提交这条数据,导致事务A读到的数据不一致;

 

幻读:

两个事务,事务A根据某个条件,读取了一些数据,事务B,插入或删除满足这个条件的数据,导致事务A再次读取的时候,发现一会变多一会变少;

 

总结: 从上面来看,很多人或混淆 不可重复读 和 幻读,其实这两个侧重点不同,不可能重复读 侧重的是 修改数据;而幻读侧重的是 新增或删除;

 

 

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

 

mysql 默认隔离级别是:RR (repeatable- read),oracle默认是:RC(read-committed);

 

四、理解可重复读的实现

使用了MVVC 的控制方式,即Mutil-Version Concurrency Control,多版本并发控制,类似于乐观锁的一种实现方式。

实现方式:

InnoDB在每行记录后面保存两个隐藏的列来,分别保存了这个行的创建时间和行的删除时间。这里存储的并不是实际的时间值,而是系统版本号,当数据被修改时,版本号加1在读取事务开始时,系统会给当前读事务一个版本号,事务会读取版本号<=当前版本号的数据此时如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然而然的就读不到更新后的数据了

 

假设初始版本号为1:

INSERT

insert into user (id,name) values (1,'Tom');

 

 

idnamecreate_versiondelete_version
1Tom1 

下面模拟一下文章开头的场景:

SELECT (事务A)

select * from user where id = 1;

此时读到的版本号为1

UPDATE(事务B)

update user set name = 'Jerry' where id = 1;

在更新操作的时候,该事务的版本号在原来的基础上加1,所以版本号为2。

先将要更新的这条数据标记为已删除,并且删除的版本号是当前事务的版本号,然后插入一行新的记录

 

idnamecreate_versiondelete_version
1Tom12
1Jerry2 

 

SELECT (事务A)

此时事务A再重新读数据:

select * from user where id = 1;

由于事务A一直没提交,所以此时读到的版本号还是为1,所以读到的还是Tom这条数据,也就是可重复读

DELETE

delete from user where id = 1;

在删除操作的时候,该事务的版本号在原来的基础上加1,所以版本号为3

删除时,将当前版本号作为删除版本号

 

idnamecreate_versiondelete_version
1Jerry23

 

参考文档:https://www.cnblogs.com/lmj612/p/10598971.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值