一个事务会涉及大量的cpu操做和IO操做,这些操做会被打包成一个执行单元,要么同时完成,要么同时都不完成。面试
事务是一组原子性的sql命令或者说是一个独立的工做单元,若是其中任何一条sql语句由于崩溃或者其余缘由执行失败,那么该组全部的sql语句都不会执行。若是没有显示启动事务,数据库会根据autocommit的值,默认每条sql操做都会自动提交。sql
一个事务中的全部操做,要么都完成,要么都不执行,对于一个事务来讲,不可能只执行其中的一部分。数据库
数据库老是从一个一致性的状态转换到另外一个一致性的状态安全
一个事务所作的修改在最终提交以前,对其余事务是不可见的。多个事务之间的操做相互不影响。每下降一个事务的隔离级别都能提升数据库的并发,但同时不安全性就增长了。关于事务的隔离级别后续还要详细讨论。这里简单介绍一下:bash
事务一旦提交,就要更新到数据库中,不能回滚。就算服务器宕机,仍然须要在下次启动的时候自动执行事务中的sql命令,体现到数据库中。服务器
隔离界别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(Read uncommited) | 可能 | 可能 | 可能 |
已提交读(Read commited) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
串行化(Serializable) | 不可能 | 不可能 | 不可能 |
准备一个帐户表,表里三个字段,分别是主键id,帐户名和金额session
create table account(
id int(11) not null auto_increment,
customer_name varchar(255) not null,
money decimal(10,2) not null,
primary key(id),
unique uni_name using btree (customer_name)
) ENGINE = 'InnoDB' AUTO_INCREMENT=10 COMMENT = '帐户表';
复制代码
一、开启两个会话,设置隔离级别为read uncommited并发
set session transaction isolation level read uncommitted;
select @@session.tx_isolation;
复制代码
一、将会话1和会话2的隔离级别设置成读提交模式性能
set session transaction isolation level read committed;
复制代码
小结:在RC模式下,咱们发现,当另外一个事务没有提交数据修改时,当前事务时读不到修改后的数据的,这就避免了读未提交模式的脏读。但有一个问题,在当前事务中,两次select的数据不同,这就存在了不可重复读的问题。PS:RC隔离级别是Oracle数据库默认的隔离级别。ui
一、将会话1和会话2的隔离级别都设置成可重复读
set session transaction isolation level repeatable read;
复制代码
首先说结论:在RR的隔离级别下,InnoDB使用MVCC和next-key locks(间隙锁)解决幻读。MVCC解决的是普通读(快照读)的幻读,间隙锁解决的是当前读状况下的幻读。
(首先声明:在RR模式下,引擎是InnoDB,没法产生如下效果。)
一、事务1先执行,但未提交
start transaction;
update account set account=1000 where id>10;
复制代码
结果为:OK row xx表名成功影响多少行 二、事务2后执行,而且提交
start transaction;
insert into account(customer_name, money) values('李云龙',10000);
commit;
复制代码
三、事务1再select一下,结果集为:
select * from account;
复制代码
··· 李云龙,10000
这时,事务1蒙了,不是已经将id>10的全部人的金额都更新成1000了吗,怎么多出一我的金额是10000?这是已提交事务2对事务1产生的影响,这个影响就叫作幻读。
不可重复读:屡次读取一条记录,发现该记录中某些列值被修改过。
幻读:只要是说屡次读取一个范围内的记录(包括直接查询全部记录结果或者作聚合统计),发现结果不一致(通常指的是记录增多,记录的减小应该也算是幻读)。
当前读
所谓当前读,指的是加锁的select(S锁或者X锁),update或者delete语句。在RR的事务隔离级别下,数据库会使用间隙锁来锁住本条记录以及索引区间。例如:
select * from account where id>10 for update;锁住的就是id=10的记录以及id>10的这个区间范围,避免范围间插入记录,以免产生幻影行记录。
普通读
由于普通读是不会加锁的读,因此解决幻读的手段是MVCC。MVCC会给每行元祖加一些辅助字段,记录建立版本号和删除版本号。在每个事务启动的时候,都有一个惟一的递增的版本号。每开启一个新事务,事务的版本号就会递增。MYSQL默认的隔离级别(可重复读Repeatable Read)下,增删改查就变成下面这样:
id | name | createversion | deleteversion |
---|---|---|---|
1 | 马云 | 1 |
若是我把name更新成刘强东,事务id=2,那么记录就变成
id | name | createversion | deleteversion |
---|---|---|---|
1 | 马云 | 1 | 2 |
2 | 刘强东 | 2 |
=> 能够看出,原来的元祖的deleteversion为更新的这个事务的id,而且新增一条
若是删除的话,假设事务id=3,删除id=2的记录
id | name | createversion | deleteversion |
---|---|---|---|
1 | 马云 | 1 | 2 |
2 | 刘强东 | 2 | 3 |
关键点来了,若是我如今读取的话,须要同时知足两个条件:
一、读取建立版本小于或等于当前事务的版本号,这意味着数据在这个事务以前被建立
二、删除版本为空或者大于当前事务版本号的记录。这意味着删除操做在这个事务以后发生
因此当事务1插入一条记录,事物2更新这条记录并删除这条记录以后,事务1再次查询这条记录,只有id=1的记录知足这两个条件,因此结果为:(避免了幻读)
id | name | createversion | deleteversion |
---|---|---|---|
1 | 马云 | 1 | 2 |
全部事务串行执行,是最高的隔离级别,性能最差
这篇文章就到这了,总的来讲数据库事务的四个特性ACID和四种隔离级别(RU,RC,RR,串行化)以及会产生的问题(脏读、不可重复读、幻读)是面试的高频问点,什么东西都须要积累,而后反复的温习,这样才能记忆和理解的更深入。