事务、锁、MVCC
一、事务
事务就是一个完整的业务逻辑单元,可以保证多个操作(DML))的原子性,要么全部成功,要么全部失败。
1、事务的过程:
开启事务机制
执行DML语句,将操作记录记录到数据库的操作历当中,但是不会修改实际数据持久化到磁盘
结束事务。
提交会把修改后的数据持久化到磁盘;
回滚事务,根据历史日志将数据恢复,两种操作都会把历史操作日志清空
2、事务的特性(ACID):
原子性:事务就是最小的工作单元,不可再分
一致性:事务必须保证DML同时成功或失败回滚
隔离性:事务A和事务B的读写具有隔离性
持久性:最终数据必须持久化到磁盘,事务才算结束
二、锁
1、读锁:共享锁、S锁
加读锁的情况:
select * from table lock in share mode
此时其他线程只能读取数据,修改数据会进入阻塞状态,只能等待锁释放或者线程超时
2、写锁:排它锁、X锁:
加写锁的情况:
select * from table for update
在读已提交的隔离级别下:
1.事务A执行select * from table where b=1 for update,事务B执行select * from table where b=2 for update
发现事务B没有被阻塞可以执行,所以在读已提交的隔离级别下,for update写锁只是把查询出来的行记录加上了锁,没有查询出的结果依然可以加锁
2.insert操作可以插入数据
事务A执行select * from table where b=1 for update,事务B执行insert table (a,b) values (1,1),可以插入,这样就造成了幻读
在可重复读的隔离级别下:
1.事务A执行select * from table where a=1 for update,事务B执行select * from table where a=2 for update
发现事务B没有阻塞可以执行,所以for update写锁只是把查询出来的行记录加上了锁,没有查询出的结果依然可以加锁
2.insert操作不能插入数据
事务A执行select * from table where b=1 for update,事务B执行insert table (a,b) values
(1,1),这个时候触发了间隙锁,为了不造成幻读,必须保证符合where条件的数据不能被插入DELETE:首先锁定这一行,再去删除
UPDATE:首先锁住这一行,再去更新
INSERT:插入一条记录,会先加入“隐式锁”来保护插入的记录在本事务提交前不被别的事务访问到
3、行锁
- LOCK_REC_NOT_GAP:单个行记录上的锁
- LOCK_ORDINARY间隙锁:根据主键排序,对于行本身以及上下行的间隙添加锁,主要是解决幻读的问题
三、MVCC
多版本并发控制。
多个线程并发访问或者操作数据库的时候,有的在读有的在写,数据不一致的问题怎么办?
- 加行级锁、间隙锁。会导致效率降低
- 用MVCC
当前读、快照读:
- 当前读:每次读取的数据都是最新版本。以下几种情况会保证每次读取的是最新的数据
- insert/update/delete
- select * from table for update
- select * from table lock in share mode
- 快照读:读取的是历史记录(存在undo日志文件中的记录)
三大组件
一、隐藏字段:
- trx_id:每一个行记录都会包含一个用户不可见的字段:trx_id–>存的是最后一次创建或修改该记录的事务id
- row_id:隐藏主键
- roll_pointer:存储的是回滚指针。每次对聚簇索引修改的时候,就会把老版本写入undo日志,如果事务执行失败,需要回滚到修改之前的状态,通过roll_pointer指针,指向上一版本中的位置,通过它来获得上一个版本的记录信息
二、undolog(回滚日志)
保存的是数据的历史版本状态,一条数据可以存在多条undolog日志信息。
三、readview的生成时机
事务在进行快照读的时候产生的视图
read committed:每次进行快照读的时候都会生成readview
repeatable read:只有在第一次进行快照读的时候才会生成readview,之后的读操作都会用第一次生成的readview