大家好,我是【JavaDog程序狗】
今天借着黑神话悟空的热度,跟大家分享一下数据库隔离级别,也是面试必备的八股文
最近狗哥这当面试官的频率越来越多,面试者的水平也参差不齐...
关于数据库隔离级别的问题也是五花八门,有的小伙伴说这完全是八股文,没有实际作用
但狗哥作为搬砖编码小王子,认为基础内容必须掌握,不只为了应付面试,而是提升自己的必经道路
遂狗哥总结数据库隔离级别全攻略,与小伙伴一起分享这个知识点
隔离级别 | 脏读 | 幻读 | 不可重复度 |
---|---|---|---|
读未提交 | 会 | 会 | 会 |
读已提交 | 不会 | 会 | 会 |
可重复读 | 不会 | 会 | 不会 |
串行化 | 不会 | 不会 | 不会 |
🎯名词解释
定义:一个事务能够读取到另一个事务尚未提交的数据。
🌰现实例子:想象一下你在银行排队等待办理业务,而你的朋友正在柜台办理转账。你的朋友还没有完成转账操作,也就是说这笔钱还没有真正从他的账户转到你的账户。但是,你却提前看到了自己的账户余额增加了这笔钱。如果这时你的朋友决定取消转账,那么你看到的余额增加就是“脏”的,因为你实际上并没有收到这笔钱。
定义:幻读是指在一个事务内多次执行相同的查询语句,第二次或以后的查询返回了第一次查询时不存在的额外记录。
🌰现实例子:想象你在一个图书馆查找书籍,第一次查询时找到了10本书关于编程的书。你离开图书馆去喝了杯咖啡,回来后再次查询,发现现在有12本关于编程的书,因为在这段时间里有人捐赠了两本新书。这就像是在同一个事务中两次查询相同条件下的图书数量,但得到的数量不同。
定义:不可重复读是指在一个事务内多次执行相同的查询语句,第二次或以后的查询返回的结果与第一次查询的结果不同。
🌰现实例子:假设你在网上购物,第一次查看某商品的价格是100元。然后你去干别的事情,比如接了个电话。当你再次查看同一件商品的价格时,发现它变成了90元,因为在这段时间里商家做了促销活动降低了价格。这就像是在一个事务中两次查询同一商品的价格,但得到了不同的结果。
******
具体的 SQL 命令如下:
-- 修改配置文件中的设置
# 在 my.cnf 或 my.ini 文件中添加如下设置
[mysqld]
transaction_isolation = <isolation_level>
-- 设置当前会话的隔离级别为 REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 或者简写形式
SET SESSION tx_isolation='REPEATABLE-READ';
-- 在事务开始之前设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
其中 <isolation_level> 是你想要设置的隔离级别,可以是以下之一:
READ UNCOMMITTED:最低的隔离级别,允许脏读。
READ COMMITTED:允许不可重复读。
REPEATABLE READ:MySQL 默认的隔离级别,防止不可重复读。
SERIALIZABLE:最高的隔离级别,完全防止脏读、不可重复读和幻读。
🌰示例
假设你想要设置当前会话的隔离级别为 READ COMMITTED,你可以这样操作:
-- 查看当前会话的隔离级别
SELECT @@SESSION.transaction_isolation;
-- 设置当前会话的隔离级别为 READ COMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 再次查看确认是否设置成功
SELECT @@SESSION.transaction_isolation;
💥注意:这些设置只会影响当前会话或当前事务,并且在事务结束后,隔离级别会恢复到之前的设置。如果你希望永久地更改隔离级别,你需要修改配置文件并重启 MySQL 服务。
******
🍓前置准备:
新建一个产品表,包含主键(id),价格(price),并插入一条数据,用作实际测试
-- 创建表
CREATE TABLE `demo`.`Untitled` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`price` decimal(10, 2) NULL COMMENT '价格',
PRIMARY KEY (`id`)
);
-- 测试数据
INSERT INTO `demo`.`product` (`id`, `price`) VALUES (1, 10.00);
INSERT INTO `demo`.`product` (`id`, `price`) VALUES (2, 10.00);
INSERT INTO `demo`.`product` (`id`, `price`) VALUES (3, 10.00);
INSERT INTO `demo`.`product` (`id`, `price`) VALUES (4, 10.00);
允许一个事务读取另一个事务尚未提交的数据。这种隔离级别提供了最高的并发性,但也可能导致一些问题,如脏读(Dirty Read)和不可重复读(Non-repeatable Read)
事务1设置事务隔离级别为读未提交,开启事务,修改产品表id=1的price为99,注意这里并未提交事务。
-- 设置事务隔离级别为读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 开始事务
START TRANSACTION;
-- 更新表中id=1的产品价格
UPDATE product SET price=99 WHERE id=1;
-- 确认更新成功,读未提交事务1查询
SELECT * FROM product WHERE id=1;
事务2设置事务隔离级别为读未提交,去查看产品表id=1的price,结果是99,也就是读到了事务1中还未提交事务的值
-- 设置事务隔离级别为读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 开始事务
START TRANSACTION;
-- 读未提交事务2查询
SELECT * FROM product WHERE id=1;
读已提交它确保每个事务只能看到已经提交的更改,从而避免了脏读,但可能会发生不可重复读和幻读
事务1设置事务隔离级别为读已提交,开启事务,修改产品表id=2的price为99,注意这里并未提交事务。
-- 设置事务隔离级别为读已提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 更新表中id=2的数据
UPDATE product SET price=99 WHERE id=2;
-- 确认上面的UPDATE语句执行成功
SELECT * FROM product WHERE id=2;
事务2设置事务隔离级别为读已提交,**去查看产品表id=2的price,结果是10,也就事务1更新前的值
-- 设置事务隔离级别为读已提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 开始事务
START TRANSACTION;
-- 读未提交事务2查询
SELECT * FROM product WHERE id=2;
将刚才上一步price更新为99的事务进行提交commit
-- 提交事务
COMMIT;
事务1已提交,**去查看产品表id=2的price,结果是99,也就事务1更新后的值
-- 读未提交事务2查询
SELECT * FROM product WHERE id=2;
可重复读确保在同一个事务内多次读取相同的数据时结果是一致的,即使在这期间有其他事务进行了更新。
事务1设置事务隔离级别为可重复读,开启事务,修改产品表id=3的price为99,注意这里并未提交事务。
-- 设置事务隔离级别为可重复读
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 更新表中id=3的数据
UPDATE product SET price=99 WHERE id=3;
-- 确认上面的UPDATE语句执行成功
SELECT * FROM product WHERE id=3;
事务2设置事务隔离级别为可重复读,**去查看产品表id=2的price,结果是10,也就事务1更新前的值
-- 设置事务隔离级别为可重复读
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 开始事务
START TRANSACTION;
-- 读未提交事务3查询
SELECT * FROM product WHERE id=3;
将刚才上一步price更新为99的事务进行提交commit
-- 提交事务
COMMIT;
事务1已提交,**去查看产品表id=3的price,结果还是10,也就事务1提交后也是重复读取提交前的10
-- 可重复读事务3查询
SELECT * FROM product WHERE id=3;
串行化确保事务以串行的方式执行,完全避免了并发问题,但可能会降低系统的并发性能。
事务1设置事务隔离级别为串行化,开启事务,修改产品表id=1的price为99,注意这里并未提交事务。
-- 设置事务隔离级别为串行化
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 开始事务
START TRANSACTION;
-- 更新表中id=4的产品价格
UPDATE product SET price=99 WHERE id=4;
-- 确认更新成功,读未提交事务4查询
SELECT * FROM product WHERE id=1;
事务2设置事务隔离级别为串行化,去查看产品表id=4的price,结果一直阻塞中等待
-- 设置事务隔离级别为读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 开始事务
START TRANSACTION;
-- 读未提交事务2查询
SELECT * FROM product WHERE id=1;
本文通过生动的例子和实际操作,全面解读了MySQL中的四种事务隔离级别:读未提交、读已提交、可重复读和串行化。
首先介绍了每种隔离级别的特点及其可能产生的问题,例如脏读、不可重复读和幻读。
接着详细说明了如何在MySQL中设置不同的隔离级别,包括全局设置和会话级别的设置方法。
最后通过一系列实操案例,直观展示了不同隔离级别下事务间交互的具体表现,帮助读者理解各种隔离级别的实际应用场景及优缺点。
通过本文的学习,开发者可以更好地掌握事务隔离级别的选择与应用,从而提高数据库应用程序的一致性和性能。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。