-- 创建user表
CREATE TABLE `user` (
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '姓名',
`age` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',
UNIQUE KEY `unique:name` (`name`) COMMENT '唯一索引:name'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
-- 插入一条记录
INSERT INTO `user` (`name`, `age`) VALUES ('张三', '17');
脏读:事务T1对数据进行了修改,但还未提交,此时事务T2读取了这条数据,那么事务T2读取到的就是脏数据,依据脏数据所做的操作可能是不正确的。
举个例子,事务T1将张三年龄改为18岁,在提交前事务T2读取了张三的记录,并根据年龄判断张三是否成年,很明显事务T2将会做出错误判断。
幻读:一个事务前后两次使用完全相同的WHERE条件查询记录数,但两次的查询结果却不相同。
幻读通常发生在高并发下的INSERT、DELETE操作。举个例子,假设user表只有一条记录,事务T1查询总记录数为1,此时事务2插入(或删除)了一条记录并提交,事务T1再查询总记录数为2(若是删除则为0),这样事务T1前后两次的查询结果就出现了不一致。
不可重复读:一个事务前后两次使用完全相同的WHERE条件查询记录,但两次的查询结果却不相同。
不可重复读通常发生在高并发下的UPDATE操作。举个例子,事务T1查询所有age=17的用户,查出了张三的记录,此时事务T2将张三年龄改为18岁并提交,事务T1再查询所有age=17的用户,就查不到任何记录了。
幻读和不可重复读非常容易混淆,因为从结果上来看它们都是前后两次的查询结果不相同,但是从加锁的角度来看两者有很大区别,解决幻读需要加表锁(因为侧重于INSERT、DELETE操作),而解决不可重复读只需要加行锁(因为侧重于UPDATE操作)。
MySQL有四种事务隔离级别,每个级别可以在不同程度上规避脏读、幻读、不可重复读:
读取未提交:READ UNCOMMITTED
读取已提交:READ COMMITTED
可重复读:REPEATABLE READ
串行化:SERIALIZABLE
mysql> SHOW VARIABLES LIKE 'transaction_isolation'; -- 查询当前事务隔离级别
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
mysql>
说明:
1、如果没有手动修改过事务隔离级别,那么默认使用REPEATABLE-READ(可重复读)。
2、使用REPEATABLE-READ时系统会自动帮我们规避掉脏读和不可重复读,如果使用InnoDB存储引擎还会帮我们把幻读也规避掉,所以通常不需要修改事务隔离级别。
修改事务隔离级别可以针对当前会话(SESSION),也可以针对全局(GLOBAL),格式如下:
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE];
示例:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;