【holm】MySQL事务隔离级别以及并发情况下出现的常见问题
总览
MySQL事务隔离级别
- read uncommitted 读取未提交的数据
- read committed 读取提交的数据
- repeatable read 可重复读
- serializable 串行化
常见问题
- 更新丢失————事务A的操作被事务B覆盖
- 脏读————事务A读取了事务B已经修改但尚未提交的数据,若事务B回滚,则事务A读取到的数据为脏数据
- 不可重复读————事务A执行操作时两次读取数据不一致
- 幻读————再解决不可重复读问题的基础上,可能无法根据查询到的值进行修改表数据。例如事务A无法插入一条自己事务内查询不到但事务B已经插入的相同数据,原因是事务B在事务A开始之后进行的插入...
MySQL相关操作
- MySQL之后隔离级别的关键字为
transaction_isolation
- MySQL8之前隔离级别的关键为
tx_isolation
- 隔离级别的作用范围有
GLOBAL
和SESSION
查看隔离级别
show [GLOBAL|SESSION] VARIABLES like 'transaction_isolation'
SElECT @@transation_isolation
设置隔离级别
SET [SESSION|GLOBAL] transaction_isolation=[0|1|2|3]
read uncommitted
- 此隔离级别中,所有事务都可以看到其他未提交事务的执行结果。(很少使用,舍弃了所有安全性的同时并没有明显性能提升)
问题:出现脏数据
read committed
- 此隔离级别中,一个事务只能看见已提交事务所做的改变。
问题:不可重复读
repeatable read
- MySQL默认的事务隔离级别
- 此隔离级别可以确保同一事务内相同的查询语句执行结果一致
问题:幻读
实例:
-- 事务一
BEGIN;
SELECT * FROM table00;
/*
查询结果:
|id|name|
|-|-|
|1|wu|
*/
-- 此时系统遇到并发,首先执行完事务二
INSERT INTO table00 VALUES(2,"we"); -- 执行失败
SELECT * FROM table00;
/*
查询结果不变:(可重复读但出现幻觉——明明不存在却无法插入)
|id|name|
|-|-|
|1|wu|
*/
COMMIT;
-- 事务二
BEGIN;
SELECT * FROM table00;
/*
查询结果:
|id|name|
|-|-|
|1|wu|
*/
INSERT INTO table00 VALUES(2,"we"); -- 插入
SELECT * FROM table00;
/*
查询结果: (成功插入)
|id|name|
|-|-|
|1|wu|
|2|we|
*/
COMMIT;
避免幻读
- 乐观锁为理论基础的MVCC(多版本并发控制)
- 间隙锁
serializable
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突。换言之,它会在每条select语句后自动加上lock in share mode,为每个查询操作施加一个共享锁。在这个级别,可能导致大量的锁等待现象。该隔离级别主要用于InnoDB存储引擎的分布式事务。
隔离级别的选择(注意这部分是纸上谈兵,算是学习到现在的个人见解和猜测吧)
- 并发量很少的情况下可以考虑选择串行化级别,一般来说面向用户尽可能不用此隔离级别。
- 接着就是使用用户体验较好的乐观锁
- 在高并发情况下增加消息队列对大量请求进行处理
参考资料
- mysql doc
- 数据库应用设计课程资料