如果你改变不了环境,那你可以先改变你自己。如果现在的你成为不了“某某某”、那你可以先成为你自己。
应用层开发的业务系统架构90%以上都是业务流,数据流(入库、加工,计算、处理)的整合,打通并连接一个个信息孤岛,而数据库就是关键核心的一环。随在架构演进中后期还会涉及分库分表,跨库的分布式事务,分布式事务我们后期会准备一个章节来进行谈。
这个在面试中也是经常被问到一个高频问题,相信事务的四大特性大多数开发者都听过一耳朵,但能深刻理解的并不多。应用层的开发者几乎每天都在跟各种数据库产品打交道。在业务逻辑代码中会经常涉及到事务的管理,有很多场景中会遇到事务四大特性、事务隔离级别,事务传播属性等问题,那么在业务系统开发中事务就是一个绕不开的话题。
原子性(Atomicity) 事务是一个或者多个SQL语句组成的整体,要么全部执行成功,要么全都执行失败 ,事务执行之后,不允许停留在中间某个状态。
-- 当前事务要么全部执行成功,要么全都执行失败,不会出现中间状态
START TRANSACTION; -- 启动事务机制
update sys_user set nickname ='许友运' where username ='youyun.xu';
update sys_permission set name='许友运' where id = 1;
COMMIT; -- 提交事务
测试步骤一:在验证之前先查询修改前数据库表中的原始数据
测试步骤二:第一个事务执行了修改数据的脚本,但先不要提交事务
-- 第一个事务执行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%';
事务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理论思想,设计者是如何取舍的、什么原因呢?