MySQL事务
概述
事务是一组具有原子性的SQL语句,或是一个独立的工作单元
特性
一致性,原子性,隔离性,持久性 ---- ACID
查询与设置隔离级别
select @@tx_isolation; //查询当前的隔离级别
set session transaction isolation level read uncommitted/read committed/repeatable read/serializable //设置隔离级别
set autocommit = on/off; //事务的自动提交
事务启动方式
- begin/start transaction:这种启动方式并不会是在一开始就建立一致性视图,视图的建立是在第一个select语句查询的时候建立的
- start transaction with consistent snapshot : 执行这个语句的时候,一致性视图就建立了。
隔离性与隔离级别
当数据库上有多个事务同时执行的时候,就有可能出现脏读(dirty read)、不可重复读、幻读的问题,隔离级别就是用来解决这个问题
- 脏读:事务A读取到事务B还未提交的数据。(出现的隔离级别读未提交)
- 不可重复读:事务A读到事务B修改后并提交的数据,可能会出现事务A在事务B修改提交之前的结果,与事务B提交之后读到的数据不同。(小明有3600元,正准备付款买单(事务A)的时候,小明的老婆将钱全部转走了(update),这时候付款sql读取到金额就变成0了)。(主要是指更新操作,总的记录条数没变化)
- 幻读:幻读并不是指两次读取获取的结果集不同,幻读侧重于某一次的select操作得到的结果集,无法支撑后续的操作。如select查询某记录不存在,准备插入,但insert的时候又报错,提示已经存在,这就是出现了幻读。(总的记录数发生变化)
级别名称 | 脏读 | 不可重复读 | 幻读 | 值 |
---|---|---|---|---|
读未提交 | 是 | 是 | 是 | 0 |
读提交 | 否 | 是 | 是 | 1 |
可重复读 | 否 | 否 | 是 | 2 |
串行化 | 否 | 否 | 否 | 3 |
示例
脏读:
A客户端设置read uncommitted
事务A | 事务B |
---|---|
begin | begin |
insert into s_users values(2,‘demo‘,2000); | |
select * from s_users where id = 2; (能查询到id=2的数据) | |
rollback; | |
select * from s_users where id = 2; (为空) | |
select * from s_users where id = 2; (为空) |
可重复读
将客户端A的隔离级别设置为read committed
事务A | 事务B |
---|---|
begin | beigin |
update s_users set money=0 where id = 1; | |
select * from s_users where id = 1; (查询到money:1000) | |
commit; | |
select * from s_users where id = 1; (查询到money:0) |
幻读
将客户端A的隔离级别设置为 repeatable read
事务A | 事务B | 事务C |
---|---|---|
start transaction with consistent snapshot | ||
start transaction with consistent snapshot | ||
update s_users set money = money + 800 where id = 1 (money=1800) | ||
update s_users set money = money + 200 where id = 1 (money=2000) | ||
select * from s_users where id =1 (1000) | ||
commit | ||
select * from s_users where id =1 (1000) | ||
commit |
解决方案
- RR级别下解决幻读,通过行锁来实现(X锁),当查询出来记录存在的时候就加X锁,如果不存在的时候就加gap锁(间隙锁),如
select * from s_users for update;
来保证其它的事务无法来调用。在innodb中锁的都是索引,而非记录本身。