MySQL事务的隔离性演示

文章目录

  • 命令
  • 事务隔离级别演示
    • 数据准备
    • 读未提交 READ UNCOMMITTED
    • 读已提交 READ COMMITTED
    • 可重复读 REPEATABLE READ
      • 测试与分析不可重复读
      • 幻读演示
  • 参考

命令

查看MySQL当前的隔离级别

/*方式一*/
select @@tx_isolation;

/*方式二*/
show variables like 'tx_isolation';

MySQL事务的隔离性演示_第1张图片

设置MySQL的隔离级别

/* read uncommitted/read committed/repeatable read/serializable */
set session transaction isolation level read uncommitted

开启事务 commit

提交事务 start transaction

回滚事务 rollback

设置事务自动提交是否开启
关闭事务自动提交:set autocommit=0;
注:值0和OFF都是一样的,当然,1也就表示ON。
查看是否开启自动提交功能:show variables like 'autocommit';
MySQL事务的隔离性演示_第2张图片

事务隔离级别演示

问题 描述 要求
脏读(Dirty Reads) B事务读到了A个事务未提交的数据。 B事务要读取A事务已经提交的数据。
不可重复读(Non-Repeatable Reads) 一个事务中两次读取的数据不一致。 一个事务中多次读取的数据是一致的。
虚/幻读(Phantom Reads) 一个事务中两次读取到的数据的条数不一致。 一个事务多次读取的数据的条数是一致的。
隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
READ UNCOMMITTED Yes Yes Yes No
READ COMMITTED No Yes Yes No
REPEATABLE READ No No Yes No
SERIALIZABLE No No No Yes

数据准备

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `balance` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'zhangsan', '1000');
INSERT INTO `account` VALUES ('2', 'lisi', '1000');

读未提交 READ UNCOMMITTED

读未提交,脏读、不可重复读、幻读都会出现。
脏读:一个事务读取到了另一个事务未提交的数据。

脏读演示
(1)打开一个客户端A,并设置当前会话事务隔离级别为read uncommitted(未提交读),开启事务,更新account表id=1的余额:

set session transaction isolation level read uncommitted;
select @@tx_isolation;
update account set balance = balance - 100 where id = 1;

MySQL事务的隔离性演示_第3张图片

(2)打开一个客户端B,并设置当前会话事务隔离级别为read uncommitted(未提交读),开启事务,查询account表id为1的余额。
MySQL事务的隔离性演示_第4张图片
(3)客户端A事务回滚
在这里插入图片描述
(4)客户端B读取到了脏数据,脏读出现
MySQL事务的隔离性演示_第5张图片

读已提交 READ COMMITTED

读已提交,可以解决脏读,但是不可重复读、幻读仍然会出现
不可重复读:一个事务中两次读取到的数据不一致。

不可重复读演示

(1)开启一个会话A,设置事务隔离级别为读已提交,开启事务,查询id=1的余额。
MySQL事务的隔离性演示_第6张图片
(2)开启一个会话B,设置事务隔离级别为读已提交,开启事务,修改id=1的余额,并提交事务。
如果想测试脏读是否解决,可以在更新余额之后,事务提交之前,在会话A中查询一次余额。
MySQL事务的隔离性演示_第7张图片
(3)在会话A的事务中,再次查询id=1的余额。不可重复读 问题出现
MySQL事务的隔离性演示_第8张图片

可重复读 REPEATABLE READ

可重复读,可以解决脏读、不可重复读,幻读仍然会出现
读写sql操作同一数据可以并发执行,但是多个写的sql语句操作同一条数据会阻塞

测试与分析不可重复读

(1)开启一个会话A,设置事务隔离级别为repeatable read,开启事务,查询id=1的余额。
MySQL事务的隔离性演示_第9张图片
(2)开启一个会话B,设置事务隔离级别为repeatable read,开启事务,更新id=1的余额,并提交。
MySQL事务的隔离性演示_第10张图片
(3)在会话A中再次查询id=1的余额。不可重复读问题解决
MySQL事务的隔离性演示_第11张图片
(4)在会话A中对id=1的账户减去100元,并没有变成900元,而是变成了800元。
可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)
MySQL事务的隔离性演示_第12张图片

幻读演示

数据准备

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50724
Source Host           : localhost:3306
Source Database       : tx

Target Server Type    : MYSQL
Target Server Version : 50724
File Encoding         : 65001

Date: 2019-05-01 16:14:42
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for tbl_tx
-- ----------------------------
DROP TABLE IF EXISTS `tbl_tx`;
CREATE TABLE `tbl_tx` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tbl_tx
-- ----------------------------
INSERT INTO `tbl_tx` VALUES ('1', 'zhangsan1');
INSERT INTO `tbl_tx` VALUES ('2', 'zhangsan2');

(1)开启一个会话A,设置事务隔离级别为repeatable read,开启事务,查询tbl_tx表。
MySQL事务的隔离性演示_第13张图片
(2)开启一个会话B,设置事务隔离级别为repeatable read,开启事务,插入一条数据,并提交。
MySQL事务的隔离性演示_第14张图片
(3)在会话A的事务中再次查询仍然是2条记录,但是当更新所有行之后会有3条数据受影响,再查询会发现出现3条数据。
MySQL事务的隔离性演示_第15张图片

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。

本身,可重复读和提交读是矛盾的。在同一个事务里,如果保证了可重复读,就会看不到其他事务的提交,违背了提交读;如果保证了提交读,就会导致前后两次读到的结果不一致,违背了可重复读。

可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据。


http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

If you want to see the “freshest” state of the database, you should use either the READ COMMITTED isolation level or a locking read:
SELECT * FROM t_bitfly LOCK IN SHARE MODE;


结论:MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是next-key locks。

结论:mysql 的重复读解决了幻读的现象,但是需要 加上 select for update/lock in share mode 变成当前读避免幻读,普通读select存在幻读

参考

《高性能MySQL》
MySQL的四种事务隔离级别
MySQL加锁过程详解(3)

你可能感兴趣的:(面试,MySQL)