隔离级别是为事务服务的。
数据库事务的4个特性:
只能从事务本身的角度来保证结果的永久性,但若不是数据库本身发生故障,而是一些外部的原因,如RAID卡损坏、自然灾害等原因导致数据库发生问题,那么所有提交的数据可能都会丢失。因此持久性保证事务系统的高可靠性(High Reliability),而不是高可用性(High Availability)。对于高可用性的实现,事务本身并不能保证,需要一些系统共同配合来完成。
查看db的隔离级别:
SELECT @@tx_isolation;
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
通过执行SET TRANSACTION ISOLATION LEVEL
命令来设置隔离级别,新设置的隔离级别将在下一个事务开始时生效。
隔离级别有四种:
这四个级别逐步加重。到最后的Serializable,事务和事务之间彻底什么都看不到,而且每次事务执行还要把其他事务全部卡起,全部串行。
面试题:大多数数据库的默认隔离级别是RC?MySQL InnoDB的默认隔离级别是什么?或为什么是RR?而不是RC?RR和RC区别是什么?
在MySQL5.0之前,binlog只支持statement。执行命令show variables like 'binlog_format';
查看当前DB的binlog格式。
statement会导致主备不一致?
statement只是记录SQL,不能保证保证主从复制不出问题。statement记录的是SQL语句原文,而row格式(MySQL 5.1才引入)记录的是执行的逻辑过程。statement占用空间少,但是会出现主备不一致;row占用空间多,不会出现主备不一致问题。
MySQL 5.5.5版本后默认的隔离级别为RR?
所以,MySQL默认RR级别是历史原因。现在有row类型的binlog,当然在不严格要求不准出现不可重复读的情况下,选用RC+row!
InnoDB默认隔离级别为RR,但实际情况是使用RC 和 RR隔离级别的都不少。
insert into t select ... from s where
语句在s表上的锁也是不一样的;一个事务在执行期间,彻底排他。比如a事务执行SELECT * FROM order
,此时a事务会把order
表的所有数据全部锁定,而不允许任何其他事务进行insert或update。这个级别强调的是对数据的范围(range)的排他锁定。只会锁定查询范围内的数据。如SELECT * FROM order WHERE status='CLOSED'
,只会锁定状态为CLOSED
的数据。
在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为SERIALIZABLE。
不同的隔离级别有不同的问题:
RR隔离级别配合GAP间隙锁已经避免幻读
不可重复读的重点是修改,同一个select,两次查询得到的某条数据不一致。
幻读的重点在于新增或删除,同一个select,两次查询得到的记录数不一样。
从结果上来看,两者都是为多次读取的结果不一致。但如果你从实现的角度来看, 它们的区别就比较大:
对于前者,在RC下只需要锁住满足条件的记录,就可以避免被其它事务修改,也就是select for update, select in share mode;
RR隔离下使用MVCC实现可重复读;
对于后者,要锁住满足条件的记录及所有这些记录之间的gap,也就是需要gap lock。
第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。另,一个事务先后读取一个范围的记录,但两次读取的纪录数不同,称之为幻读(两次执行同一条 select 语句会出现不同的结果,第二次读会增加一数据行,并没有说这两次执行是在同一个事务中)
幻读和不可重复读很像,但幻读侧重点在于新增和删除,而不可重复读侧重点在于更改,共同之处都是一个事务中两次查询得到的数据结果不一致。
总结:
事务隔离级别 | 脏读Dirty Read | 不可重复读NonRepeatable read | 幻读Phantom |
---|---|---|---|
RU | Y | Y | Y |
RC | - | Y | Y |
RR | - | - | Y |
SERIALIZABLE | - | - | - |
Y表示次隔离级别存在对应的问题。
非锁定读,Multi-Version Concurrency Control,多版本并发控制。基于对并发性能的考虑,MySQL的大多数事务型存储引擎都实现多版本并发控制,可简单认为MVCC是行级锁的一个变种,但是它在很多情况下避免加锁操作,降低开销。
InnoDB的MVCC是通过在每行记录后添加两个隐藏列来实现的,创建时间、过期时间(或删除时间),这两个时间在实际存储时,存储的是系统版本号。每开始一个新事务,系统版本号都将递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。MVCC只能在Read Committed和Repeatable Read隔离级别下正常工作。
MVCC能够实现读写不阻塞!
两种实现方式
纵览各种数据库的MVCC实现,主要有两种实现方式。
一次封锁or两段锁?
因为有大量的并发访问,为了预防死锁,一般应用中推荐使用一次封锁法,就是在方法的开始阶段,已经预先知道会用到哪些数据,然后全部锁住,在方法运行之后,再全部解锁。这种方式可以有效的避免循环死锁,但在数据库中却不适用,因为在事务开始阶段,数据库并不知道会用到哪些数据。
数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)
加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁(共享锁,其它事务可以继续加共享锁,但不能加排它锁),在进行写操作之前要申请并获得X锁(排它锁,其它事务不能再获得任何锁)。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
解锁阶段:当事务释放一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。
这种方式虽然无法避免死锁,但是两段锁协议可以保证事务的并发调度是串行化(串行化很重要,尤其是在数据恢复和备份的时候)的。
为何有些公司会把RR更改为RC??
可重复读和幻读的区别?
可序列化,如何实现的?
MVCC能否解决幻读
MySQL-RR隔离与RC隔离
Innodb中的事务隔离级别和锁的关系
MySQL为什么默认隔离级别为可重复读?
MySQL中隔离级别RC与RR区别