四种隔离级别的比较
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交/不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
- 隔离级别高低比较:可串行化>可重复读>读已提交>读未提交
- 隔离级别对性能的影响大小比较:可串行化>可重复读>读已提交>读未提交
隔离级别越高,所需要消耗的MySQL性能越大(如事务并发严重性),为了平衡二者,一般建议设置的隔离级别为可重复读。
事务隔离机制的实现基于锁机制和并发调度。
其中,并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是读已提交(不可重复读)内容,但是你要知道的是 InnoDB 存储引擎默认使用可重复读并不会有任何性能损失。InnoDB 存储引擎在分布式事务的情况下一般会用到可串行化隔离级别。
注意:Oracle 默认采用的读已提交隔离级别。
数据库的多版本并发控制(MVCC)原理
Mysql 默认采用的可重复读隔离级别。每条记录在更新的时候都会同时记录一条回滚操作(回滚操作日志undo log)。同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。即通过回滚(rollback操作),可以回到前一个状态的值。
假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。
当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如下图所示,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。
对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。
同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。
提问:回滚操作日志(undo log)什么时候删除?
MySQL会判断当没有事务需要用到这些回滚日志的时候,回滚日志会被删除。
提问:那什么时候不需要了?
当系统里没有比这个回滚日志更早的read-view的时候。
设置隔离级别
通过set命令
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level; 复制代码
其中,level有4种值:REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
。
关键词:GLOBAL
,只对执行完该语句之后产生的会话起作用,当前已经存在的会话无效。
关键词:SESSION
,对当前会话的所有后续的事务有效,该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务,如果在事务之间执行,则对后续的事务有效。
无关键词:只对当前会话中下一个即将开启的事务有效,下一个事务执行完后,后续事务将恢复到之前的隔离级别,该语句不能在已经开启的事务中间执行,会报错的。
通过服务启动项命令
可以修改启动参数transaction-isolation
的值。比方说,我们在启动服务器时指定了--transaction-isolation=READ UNCOMMITTED
,那么事务的默认隔离级别就从原来的REPEATABLE READ变成了READ UNCOMMITTED。
查看当前会话隔离级别
方式一:
mysql> show variables like 'transaction_isolation'; +-----------------------+--------------+ | Variable_name | Value | +-----------------------+--------------+ | transaction_isolation | SERIALIZABLE | +-----------------------+--------------+ 复制代码
方式二:
mysql> select @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | SERIALIZABLE | +-------------------------+ 复制代码
MySQL 中的事务操作
事务分为隐式事务和显式事务。
MySQL 中事务默认是隐式事务,执行insert、update、delete操作的时候,数据库自动开启事务、提交或回滚事务。是否开启隐式事务是由变量autocommit控制的。
隐式事务
事务自动开启、提交或回滚,比如insert、update、delete语句,事务的开启、提交或回滚由mysql内部自动控制的。
查看变量autocommit是否开启了自动提交,autocommit为ON表示开启了自动提交。
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set, 1 warning (0.00 sec) 复制代码
显式事务
事务需要手动开启、提交或回滚,由开发者自己控制。有 2 种方式手动控制事务。
用 START来开始一个事务
语法:
// 开启事务 start transaction; // 执行事务操作(事务回滚/事务确认) commit|rollback; 复制代码
示例:提交事务操作。
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (2); Query OK, 1 row affected (0.00 sec) mysql> insert into test1 values (3); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) 复制代码
用 SET 来改变 MySQL 的自动提交模式
- SET AUTOCOMMIT=0 禁止自动提交
- SET AUTOCOMMIT=1 开启自动提交
语法:
// 设置不自动提交事务 set autocommit=0; // 执行事务操作 commit|rollback; 复制代码
示例:回滚事务操作。
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values(2); Query OK, 1 row affected (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec) 复制代码
总结
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为读未提交、读已提交、可重复读以及可串行化,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
- 事务隔离级别为读已提交时,写数据只会锁住相应的行。
- 事务隔离级别为串行化时,读写数据都会锁住整张表。
InnoDB 存储引擎默认使用可重复读隔离级别。在分布式事务的情况下,InnoDB 存储引擎一般会用到可串行化隔离级别。