MySQL事务总结
事务四大特征
- 原子性
- 定义: 从操作层面说明,多个数据操作要么全部成功,要么全部失败
- 实现原理: 使用undo log日志保证,undo log记录数据的历史版本,可以根据历史版本进行回滚
- 持久性
- 定义: 数据一旦提交,数据就会永久保存下来,即使服务器宕机也不会影响数据
- 实现原理: redo log记录新数据的备份,在数据提交前会先将数据持久化到redo log,服务器即使宕机,也可以通过redo log进行数据恢复
- 隔离性
- 定义: 事务执行过程中不受其他事务的影响
- 实现原理: 使用mvcc实现
- 一致性
- 定义: 从数据层面说明,事务提交前后,数据的总体效果保持一致
- 实现原理: 其他三个原理共同保证数据一致性
数据读取问题(脏读,不可重复读,幻读)
- 脏读:读取到还未提交的数据
- 不可重复读:同一个事务里面,前后多次获取同一条数据,结果不一致
- 幻读:由于其他事务插入数据导致,本事务更新到了其他事务新增的数据
事务隔离级别
- 读未提交:回存在脏读,不可重复读,以及幻读
- 读已提交:解决了脏读,但是还是会出现不可重复读和幻读,使用mvcc实现
- 可重复读:解决了脏读和不可重复读,还是存在幻读,使用mvcc实现
- 序列化:解决了脏读,不可重复读和幻读,一般不会使用,串行化执行效率太低
锁机制
- 锁分类
- 行锁:存储引擎为innodb,锁住一行数据,锁实现更复杂,开销大,加锁速度慢,粒度小,并发高
- 表级锁:存储引擎为myisam,锁住整张表,加锁速度快,开销小,并发低
- 页级锁:存储引擎BDB,锁住一页数据
- 行级锁分类
- 共享锁
- 特点:加共享锁的数据,允许读数据,不允许写数据
- 使用:```select … from table_name lock in share mode;``
- 排它锁
- 特点:添加排它锁的数据,既不允许读数据也不允许写数据
- 使用:```select … from table_name for update;``;mysql中update,insert,delete这些写操作默认是添加排它锁的
- 记录锁
- 特点:单个行记录添加的锁,记录锁锁住的是索引记录,如果没有指定将隐式的使用主键来进行锁定
- 间隙锁
- 特点:锁住的是记录中的间隙,即范围查找的记录
- 使用:```select * from table_name where id between 1 and 10 for update;``
- 临建锁:记录锁+间隙锁
- 共享锁
mvcc:多版本并发控制器
- undo log链
- MySQL数据表有一些隐藏字段,例如事务id,回滚指针
- 当一条数据新增或者更新时,会生成一条新的undo log记录,新的表数据通过回滚指针指向历史记录
- readView
- 存放数据:生成该readView时,当前正在活跃的事务id集合,以及接下来将要分配的事务id
- 数据是否可见的规则
- 当前事务id小于最小活跃事务id,说明生成该readView时该事务已经提交,所以数据可见
- 当前事务id大于等于接下来将要分配的事务id,说明生成readView时,还没有该事务,所以数据不可见
- 当前事务id位于两者之间时,判断事务id是否在活跃事务列表里,如果在说明生成readView时,该事务正在活跃,事务不可见;如果不在活跃列表说明生成readView时
该事务已经提交,数据可见
- 可重复读和读已提交的readView生成区别:生成时机不同,读已提交每次读取数据时都会重新生成readView,可重复读只有在第一次读取数据时会生成readView。
buffer poll
- mysql 数据更新的整体流程
- 将数据所在数据页的所有数据加载到buffer pool里面
- 将数据旧值写入undo log日志便于回滚
- 更新内存数据
- 将新数据写入redo log buffer里面
- 准备提交事务,将redoLog数据写入磁盘
- 准备提交事务,binlog日志写入磁盘
- 写commit命令到redo日志文件里,事务提交完成,该操作是为了保证redolog数据和binlog数据一致
- 通过io线程随机将buffer pool数据刷到磁盘
- MySQL内存淘汰算法(LRU)
- 在原有算法上面的优化点:新增老年代和新生代概念,老年代数据优先于新生代数据淘汰
- 解决预热失效问题:新访问数据存在于老年代头部,数据被再次访问之后才会放入新生代
- 解决缓冲池污染问题:页被访问并且在老年代停留超过指定阈值的数据,才能进入新生代,解决批量访问数据,缓冲池被污染的问题
- 假设T=老年代停留时间阈值
- 插入老年代头部的数据,及时被立即访问也不会直接放入新生代,只有短时间内访问超过指定次数的数据才能够放入新生代,优先淘汰那些短时间内只访问一次的数据
- 只有满足,被访问,并且在老年代停留时间大于T的数据,才会被放入新生代