一篇文章让你深刻理解事务的四大特性ACID(酸碱平衡)

如果你改变不了环境,那你可以先改变你自己。如果现在的你成为不了“某某某”、那你可以先成为你自己。

开场白

应用层开发的业务系统架构90%以上都是业务流,数据流(入库、加工,计算、处理)的整合,打通并连接一个个信息孤岛,而数据库就是关键核心的一环。随在架构演进中后期还会涉及分库分表,跨库的分布式事务,分布式事务我们后期会准备一个章节来进行谈。

这个在面试中也是经常被问到一个高频问题,相信事务的四大特性大多数开发者都听过一耳朵,但能深刻理解的并不多。应用层的开发者几乎每天都在跟各种数据库产品打交道。在业务逻辑代码中会经常涉及到事务的管理,有很多场景中会遇到事务四大特性、事务隔离级别,事务传播属性等问题,那么在业务系统开发中事务就是一个绕不开的话题。

一、事务的四大特性(ACID)

1、基本概念

  • 原子性(Atomicity) 事务是一个或者多个SQL语句组成的整体,要么全部执行成功,要么全都执行失败 ,事务执行之后,不允许停留在中间某个状态。

-- 当前事务要么全部执行成功,要么全都执行失败,不会出现中间状态
START TRANSACTION;  -- 启动事务机制
update sys_user set nickname ='许友运' where username ='youyun.xu';
update sys_permission set name='许友运' where id = 1;
COMMIT; -- 提交事务
  • 一致性(Consistency) 一致性要求,事务在开始前和结束后,数据库的完整性约束没有被破坏。
  • 隔离性(Isolation) 事务的执行是相互独立的,它们不会相互干扰,一个事务不会看到另一个正在运行过程中的事务的数据。
  • 持久性(Durability)一旦事务提交成功,事务中所有的数据操作都必须被持久化保存到数据库中,即使提交事务后,数据库崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。

2、动手验证隔离性

测试步骤一在验证之前先查询修改前数据库表中的原始数据

测试步骤二第一个事务执行了修改数据的脚本,但先不要提交事务

-- 第一个事务执行SQL修改数据、但不提交事务
START TRANSACTION;  -- 启动事务机制
update sys_user set nickname ='许友运' where username ='youyun.xu';
update sys_permission set name='许友运' where id = 1;

测试步骤三第二个事务执行SQL脚本查询数据、发现数据未发生任何改变

-- 第二个事务查询数据(数据库未发生任何改变)
START TRANSACTION;
select * from sys_user where username ='youyun.xu';
select * from sys_permission  where id = 1;

测试步骤四:第一个事务提交事务,第二个事务再查询数据

-- 第一个事务执行、并提交事务 执行SQL如下
START TRANSACTION;  -- 启动事务机制
update sys_user set nickname ='许友运' where username ='youyun.xu';
update sys_permission set name='许友运' where id = 1;
COMMIT; -- 提交事务

执行日志如下:
[SQL]START TRANSACTION;
受影响的行: 0
时间: 0.072s

[SQL]  -- 启动事务机制
update sys_user set nickname ='许友运' where username ='youyun.xu';
受影响的行: 0
时间: 0.078s

[SQL]
update sys_permission set name='许友运' where id = 1;
受影响的行: 0
时间: 0.078s

[SQL]
COMMIT;
受影响的行: 0
时间: 0.074s

[SQL] -- 提交事务

受影响的行: 0
时间: 0.077s

测试步骤四:第二个事务再查询数据

-- 第二个事务再次查询数据(看到了第一个事务修改后的数据)
START TRANSACTION;
select * from sys_user where username ='youyun.xu';
select * from sys_permission  where id = 1;

通过这个例子相信你能快速帮你加深理解,让你对数据库的隔离性有一个更深层次的理解,它会帮助你在实际业务系统开发中解决你所遇到的一些“奇怪的问题”

二、事务的隔离级别

  • 数据库产品默认隔离级别

数据库类型 默认隔离级别
MySQL 可重复读
SQL Server 读已提交
Oracle 读已提交
  • 隔离级别引发的问题

隔离级别 脏读(Dirity Read) 不可重复读(NonRepeatable) 幻读(Phantom Read)
读未提交(Read uncommitted) 可能 可能 可能
读已提交(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能
  • 修改隔离级别

1、可以通过命令行设置全局 或 会话的隔离级别。这样你就可以非常方便切换数据库事务隔离级别,这样非常有利于你进行验证。

2、数据会推荐设置默认的隔离级别,同时可以根据系统业务特性修改数据,具体怎么选请根据实际情况选择

修改命令:SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

-- 设置全局隔离级别
set global transaction isolation level REPEATABLE READ;

-- 设置会话隔离级别 
set session transaction isolation level REPEATABLE READ;

-- 通过配置文件设置隔离级别 
[mysqld]
transaction-isolation = REPEATABLE-READ
transaction-isolation = READ-COMMITTED
transaction-isolation = READ-UNCOMMITTED
transaction-isolation = SERIALIZABLE

-- 查看隔离级别
show variables like '%iso%';

  • 说一说MySQL可重复读

事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容,就出现了我们的幻读问题。

测试步骤一:开启事务1、开始事务2

set session transaction isolation level REPEATABLE READ;
START TRANSACTION;  -- 启动事务机制1
set session transaction isolation level REPEATABLE READ;
START TRANSACTION;  -- 启动事务机制2

测试步骤二:事务2在事务1修改提交事务之前查询一次数据

select * from sys_user where username ='youyun.xu';

测试步骤三:事务1修改数据并提交事务

update sys_user set nickname ='许友运Repeatable read' where username ='youyun.xu';
COMMIT; -- 提交事务

测试步骤四:事务2在事务1修改提交事务之后查询一次数据(查询到的依然是修改前的数据

测试步骤五:提交事务2后再查询一次数据库库(原始数据确实被修改,是不是产生幻觉了

select * from sys_user where username ='youyun.xu';
commit; -- 提交事务、再查询一次
select * from sys_user where username ='youyun.xu';

通过我整理的这个例子你能很快的理解MySQL的可重复读产生的"幻读",同时你可以参照这个例子进行验证,也可以切换数据库隔离进行各种场景的验证。在这里我也整理完成的操作步骤,希望对你有帮助。

三、思考题

1、MySQL 数据库产品在设计时采用了CAP理论思想,设计者是如何取舍的、什么原因呢?

猜你喜欢

转载自blog.csdn.net/weixin_37854829/article/details/113446847
今日推荐