mysql-事务管理
什么是事务
事务是逻辑上的一组操作,要么全执行,要么全不执行
事务的特性(ACID)
- 原子性。事务最小的执行单位,不允许分割。事务的原子性确保动作要么全部执行,要么全部不执行
- 持久性。一个事务被提交后,它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有影响
- 隔离性。并发访问数据库时,一个用户的事务不应该被其他事务所影响,各并发事务之间数据库是独立的
- 一致性。执行事务的前后,数据保持一致
事务隔离级别
分类
- 读未提交(Read Uncommited),简称RU
- 最低的隔离级别,允许读取尚未提交的数据变更,可能造成脏读、不可重复读、幻读
- 读已提交(Read Commited),简称RC
- 允许读取并发事务已经提交的数据,可以避免脏读,但是可能造成不可重复、幻读
- 可重复度(Repeatable Read),简称RR
- 对同一字段多次读取的结果都是一致的,除非本身事务修改,可以避免脏读和不可重复读,但是可能造成幻读
- 存储引擎默认的事务隔离级别是可重复读,默认采用的是一致性非锁定读,也就是快照读
- 串行化(Serializable)
- 最高的隔离级别,完全服从ACID的隔离级别,所以的事务依次执行(串行执行),可以避免脏读、不可重复读、幻读
事务常见问题
- 脏写/修改丢失(Lost of modify)
- 两个事务没提交的状况下,都修改统一条数据,结果一个事务回滚了,把另外一个事务修改的值也撤销了,所谓脏写就是两个事务没提交状态下修改同一个值
- 脏读(Dirty read)
- 脏读是指事务读取到其他事务未提交的数据
- 不可重复读(non-repeatable read)
- 不可重复读是指在同一次事务中前后查询不一致的问题。同样的查询语句,两次的执行结果却不一致(同一次事务中前后查询不一致)
- 幻读(Phantom read)
- 幻读是一次事务中前后数据量发生变化,用户产生不可预料的问题
不可重复读与幻读的比较
- 幻读和不可重复读都是读取了另一条已经提交的事务,不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(区间查询)
- 不可重复读的重点是修改,幻读的重点在于插入或者删除
- 但如果从控制的角度来看, 两者的区别就比较大不可重复读只需要锁住满足条件的记录幻读要锁住满足条件及其相近的记录
事务相关命令
-
查询数据库隔离级别
select @@tx_isolation; SELECT @@transaction_isolation;(MySQL 8.0) -
显示的开启一个事务
START TRANSACTION | BEGIN -
提交事务,使得对数据库做的所有修改成为永久性
COMMIT -
回滚到结束用户的事务,并撤销正在进行的所有未提交的修改
ROLLBACK
MVCC
概念
- MVCC(Multi-Version Concurrency Control)
- 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,提高了数据库并发读写的性能 同时还可以解决脏读,幻读,不可重复读等事务隔离问题
- 在并发读写数据库时,可以做到在读操作(快照读)时不用阻塞写操作,写操作也不用阻塞读操作
- 是用于解决多事务并发操作数据与一致性读实现
多事务并发操作数据分析
概念
- 基于Undo log进行实现,可以用来做事务的回滚操作,保证事务的原子性
- 同时可以用来构建数据修改之前的版本,支持多版本读
隐藏字段
- DB_ROW_ID(6Byte)
- 隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
- DB_TRX_ID(6Byte)
- 最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务ID
- DB_ROLL_PTR(7Byte)
- 回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
- 通过链表形式组织
- 当存在多个事务进行并发操作数据时,不同事务对同一行的更新操作产生多个版本,通过回滚指针将这些版本链接成一条Undo Log
- DELETED_BIT(1Byte)。删除标记位
- 删除操作都只是设置一下老记录的DELETED_BIT,并不真正将过时的记录删除
- 为了节省磁盘空间,InnoDB有专门的purge线程来清理DELETED_BIT为true的记录
一致性读实现
主要通过ReadView实现一致性读,对应于事务的各个隔离级别
Read View机制
- 是事务进行快照读操作的时候生产的读视图(Read View)
- 通过可见性算法进行分析(通过当前事务与活跃事务进行比对),最终生成的读视图。如果相等,读取的就是最新数据;不相等,就是通过回滚指针遍历undo log链表,查找符合条件的记录的数据
当前读与快照读
- 当前读
- 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
- 像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读
- 串行级别下的快照读会退化成当前读
- 快照读
- 不加锁的select操作就是快照读,即不加锁的非阻塞读
- 快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本