什么是事务
事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
事务的4大特性:
- 原子性(Atomicity)
- 一致性(Consistent)
- 隔离性(lsolation)
- 持久性(Durable)
事务并发带来的问题
脏读
一个事务读取到了另一个事务未提交的数据。
不可重复读
一个事务读取到了另一个事务已修改的数据。
幻读
一个事务读取到了另一个事务插入后的数据,导致总记录数不一致。
事务隔离级别
事务并发的三大问题都是数据库读一致性的问题,必须由数据库提供一定的事务隔离机制来解决。
SQL92标准
- Read Uncommitted(未提交读):未解决任务并发问题,事务未提交的数据对其他事务可见。
- Read Committed(已提交读):解决脏读问题,只能看到已提交的事务所做的修改,会出现不可重复读、幻读。
- Repeatable Read(可重复读):解决不可重复读问题,同一个事务多次读取同样的数据结果一定是一致的,会出现幻读。
- Serializable(串行):解决所有问题。
InnoDB对事务隔离级别的支持程度
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读(Read Uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read Committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable Read) | 不可能 | 不可能 | 在InnoDB中不可能 |
串行(Serializable) | 不可能 | 不可能 | 不可能 |
InnoDB事务隔离级别的实现
RR | RC | |
---|---|---|
普通的select | mvcc | mvcc |
加锁的select和更新 select … in share mode select … for update insert update delete | Record Lock Gap Lock Next-key Lock | Record Lock |
事务隔离级别区别
- RR的间隙锁会导致锁定范围的扩大;
- 条件列未使用索引时,RR锁表,RC锁行;
- RC的“半一致性”读可以增加update操作的并发性。
事务隔离级别解决方案
第一种
在读取数据前,对其加锁,阻止其他事务对数据进行修改(LBCC)Lock Based Concurrency Control。
第二种
生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取(MVCC)Multi Version Concurrency Control。
MVCC
多版本并发控制(Multiversion Concurrency Control),它是事务隔离级别的无锁化实现方式,用于提高事务的并发效率。
隐藏列
InnoDB会为每张表生成3个隐藏列:
- ROW_ID:6byte,如果当前表中没有主键,会默认生成一个主键ID;
- DB_TRX_ID:6byte,最近修改(修改/插入)的事务ID,删除也被视为修改;
- DB_ROLL_PTR:7byte,存储当前记录之前版本的存储指针,用于回滚。
undolog
每次操作的历史记录都会存储在undolog文件中
ReadView
ReadView在事务中每次查询都会生成一个ReadView(重复读事务隔离级别只会在首次查询时创建),ReadView中有存储当前事务ID、未提交事务ID、未开始事务ID等数据,通过这些信息就可以在undolog中定位到具体的版本。
锁
在MySQL中InnoDB支持表锁与行锁,MyISAM只支持表锁。
锁的类型如下:
- 共享锁(行锁):Shared Locks
- 排它锁(行锁):Exclusive Locks
- 意向共享锁(表锁):Intention Shared Locks
- 意向排它锁(表锁):Intention Exclusive Locks
共享锁(行锁)
又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是不能修改。
使用方式:
select * from employee where emp_id = 20003 LOCk IN SHARE MODE;
COMMIT/ROLLBACK;
排他锁(行锁)
又称为写锁,简称X锁,排他锁不能与其他锁并存,如一个事务获取了一条数据行的排他锁,其他事务就不能再获取该行的锁(共享锁、排他锁),只有获取了排他锁的事务可以对数据进行读取和修改操作。
使用方式:
delete/update/insert语句默认会加上X锁。
select * from employee where emp_id = 20003 for update;
COMMIT/ROLLBACK;
意向共享锁(表锁)
意向锁是由数据引擎自己维护的,用户无法手动操作意向锁。
意向锁仅仅只是给表加上一个标识,表明该表已经有行锁了,如果此时有事务需要对该表进行表锁,通过该标识判断即可,而不需要全表扫描判断该表是否有加行锁。
简称IS锁,表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先获取该表的IS锁。
意向排他锁(表锁)
简称IX锁,表示事务准备给数据行加入排他锁,说明事务在给一个数据行加排他锁前必须先获取该表的IX锁。
锁的算法
记录锁
使用唯一/主键索引等值查询时,精准匹配到一条记录。
select * from tab where id = 4 for update //锁住的就是id=4所在的记录行
间隙锁
查询的记录不存在时,锁住一块区间。
// 锁住(4,7)
select * from tab where id > 4 and id < 7 for update;
select * from tab where id = 6 for update;
// 锁住(10,+∞)
select * from tab where id > 15 for update;
临键锁
范围查询,包含记录和区间
// 锁住(4,7](7,10]
select * from tab where id > 5 and id < 9 for update;