事务的四个基本要素(ACID)
原子性(Atomicity):事务开始后所有操作要么全执行,要么全不执行,不会出现执行部分操作的情况。
一致性(Consistency):事务开始前和结束后,数据完整性没有被破坏。比如说A给B转账,不会出现A扣了钱,B却没收到钱的情况。
隔离性(Isolation):同一时间,同一条数据不能被两个事务同时操作,不同的事务之间相互没有干扰。比如A正从一张银行卡取钱,B不能向这个银行卡转钱。
持久性(Durability):事务完成后,数据被保持到数据库,不能再回滚。
事务的并发问题
脏读:事务A读取了事务B更新的数据,然后事务B执行了回滚操作,那么A读取到的数据就是脏数据。
不可重复读:事务A多次读取同一数据,在此过程中事务B对此数据做了更新操作并提交,导致事务A多次读取的结果不一致。
幻读:事务A更新某一区间的数据,在此过程中事务B新增了一条数据并已提交,导致事务A提交后发现有一条数据没有被更新。
注意点:区分不可重复读和幻读,不可重复读侧重于修改,而幻读侧重于新增和删除。解决不可重复读的问题只需要锁住满足条件的行,解决幻读需要锁表(Mysql可重复读隔离级别使用间隙锁)。
事务的隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复度(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
Mysql默认事务隔离级别为“可重复度(repeatable-read)”
具体分析四个隔离级别
以下通过例子进行分析,首先创建表,并导入数据:
create table account(
id int(8) primary key,
name varchar(10),
balance int(10)
);
insert into account(id,name,balance) values(1, 't1', 100);
insert into account(id,name,balance) values(3, 't3', 300);
insert into account(id,name,balance) values(5, 't5', 500);
insert into account(id,name,balance) values(8, 't8', 800);
读未提交(read-uncommited):
开启两个会话窗口,修改事务隔离级别为"读未提交",启动事务A和事务B,事务A修改记录并未提交,事务B可以看到修改的内容。
//设置事务隔离级别
set session transaction isolation level read uncommitted;
//查看事务的隔离级别
select @@tx_isolation;
执行顺序 | 事务A | 事务B |
1 | begin; | |
2 | begin; | |
3 | update account set balance = balance + 10 where id=1; | |
4 | select * from account where id=1; |
事务A,未提交:
事务B
读已提交(read-committed)
修改事务隔离级别为读已提交(read-committed),启动事务A和事务B,事务A修改记录不提交,事务B查询记录看不到修改内容,当提交后可以看到修改的内容。
//设置事务隔离级别
set session transaction isolation level read committed;
//查看事务的隔离级别
select @@tx_isolation;
执行顺序 | 事务A | 事务B |
1 | begin; | |
2 | begin; | |
3 | update account set balance = balance + 5 where id=1; | |
4 | select * from account where id=1; | |
5 | commit; | |
6 | select * from account where id=1; |
事务A,更新未提交:
事务A未提交时,事务B:
事务A提交更新:
事务A提交后,事务B:
可重复读(repeatable-read)
修改事务的隔离级别为可重复读,开启事务A和事务B,事务A修改后并提交,事务B无法查看到A修改的内容,但B修改会在最新的基础上修改,保证事务的一致性。
//设置事务隔离级别
set session transaction isolation level repeatable read;
//查看事务的隔离级别
select @@tx_isolation;
执行顺序 | 事务A | 事务B |
1 | begin; | |
2 | begin; | |
3 | update account set balance = balance + 10 where id=1; | |
4 | commit; | |
5 | select * from account where id=1; |
事务A修改并提交:
事务B无法查看到A修改的内容:
串行化(serializable)
这种隔离级别最严格,会锁整张表,当A事务更新记录1的时候,事务B不仅无法更新记录1,甚至无法更新其它记录。
事务A更新记录id=1:
事务B更新记录id=3失败: