Mysql——基本日志常识
日志分类
普通日志
二进制日志(bin log)、错误日志、查询日志、慢查询日志
事务日志
redo log——重做日志
,来记录已成功提交事务的修改信息undo log——回滚日志
,主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作
,需要将之前的操作都记录下来,然后在发生错误时才可以回滚
事务日志——数据结构
其实除了我们在数据库中定义的列之外,每一行中还包含了几个隐藏列:
**row_id**:行记录的唯一标志 row_id是行记录的唯一标志,也没有申明唯一索引的情况MySQL才会添加这个隐藏列
**transaction_id**:事务ID 事务中产生了增删改,则会分配一个事务id来区分不同行
**roll_pointer**:回滚指针 指向该记录对应的undo log;一个有undo log组成的链表,这个undo log链表其实就是这条记录的版本链
事务日志——持久化
持久化时机
insert undo log是指在insert 操作中产生的undo log
update undo log记录的是对delete 和update操作产生的undo log
持久化过程
Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)
先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool
持久化调优
innodb_flush_log_at_trx_commit:0 定时缓存、即可调用fsync()写入到log file on disk,会丢失一秒数据
innodb_flush_log_at_trx_commit:1 即可缓存、即可刷新,不会丢失 每次提交都会将log buffer中的日志写入os buffer并调用fsync()函数刷到log file on disk
innodb_flush_log_at_trx_commit:2 即可缓存、定时调用fsync()写入到log file on disk,会丢失一秒数据
Mysql——并发读隔离原理(1)——事务隔离级别
Read uncommitted(读未提交)
在这个隔离级别下,事务的修改即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据,这也被称之为脏读。这个级别会带来很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但是却会带来很多问题,除非真的有非常必要的理由,在实际应用中一般很少使用。
Read committed(读提交)
大多数数据系统的默认隔离级别都是REDA COMMITED(MySql不是),REDA COMMITED满足前面提到的隔离性的简单定义:一个事务开始时,只能看到已经提交的事务所做的修改。换句话说,一个事物从开始直到提交前,所做的修改对其他事务不可见。这个级别有时候也叫做不可重复读,因为执行两次相同的查询可能会得到不同的结果。
Repeatable read(可重复读取)
REPEATABLE READ解决了脏读以及不可重复度的问题。该级别保证了同一个事务多次读取同样记录的结果是一致的。但是理论上,可重复度还是无法解决另外一个幻读的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,就会产生幻行。
不可重复读跟幻读的区别在于,「前者是数据发生了变化,后者是数据的行数发生了变化」。
Serializable(可序化)
SERIALIZABLE是最高的隔离级别,它通过强制事务串行执行,避免前面说的幻读。简单来说SERIALIZABLE会在读取的每一行数据上都加锁,所以可能会导致大量的超时和锁争用的问题。实际应用中也很少使用这个隔离级别,只有在非常需要确保数据一致性而且可以接受没有并发的情况下,才考虑此级别。
Mysql——并发读隔离原理(2)——并发读分类
当前读指令
【激活方式】
select lock in share mode(共享锁),
select for update ;
update, insert ,delete(排他锁)【特殊运行】
这些操作都是一种当前读,为什么叫当前读?
就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读指令
【激活方式】
事务中直接select,第二次再select就会快照读,激活方式就是普通查询不加for update和读锁,【特殊运行】
即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本,不会并发读写阻塞
Mysql——并发读隔离原理(3)——快照读原理(MVCC多版本并发控制)
继续顺延mysql中的并发读中的快照读原理的实现
MVCC主要解决的问题就是
(1)RR模型中事务中读取的变量只重复读第一次的,保证事务的正常,不能一会儿是这个值,一会是另外一个值
(2)解决当前读中的阻塞问题
步骤(1)生成快照(数据库级别)
快照生成方式
RC隔离级别下,每一次select都会生成并获取最新的Read View;此时遍历数据只过滤读未提交的数据
RR隔离级别下,第一次select生成一次Read View,事务执行的比照基础一直是这个Read View
快照数据结构
整体信息:m_ids,数据库中正在执行事务的id集合
整体信息:m_up_limit_id:数据库中正在执行事务的id集合的最小值,
整体信息:m_low_limit_id :数据库中系统id的下一个id数字
单行信息:m_creator_trx_id:当前事务的id,又叫快照的id
步骤(2)遍历数据(数据行级别)
遍历数据行伪代码
每次遍历数据行的变量是row,row的tx_version_id判断本行数据对该事务是否可见
for(;;){
if(changes_visible(row.tx_version_id,tableName)){
return row;
}
row=row.next
}
可见性检查逻辑
主要是利用事务id的递增特性,来区分数据行的事务版本对应和事务操作的先后顺序
注意,RR模式下,每次select时快照仍然一致,但是数据的链表可能发生了变化
【每遍历一次】
数据的事务id=(快照生成时)创建该快照的事务id(当前事务)————自己在操作自己的数据————数据行可见
数据的事务id<(快照生成时)正在执行的事务id集合最小(不存在trIds)————该数据在快照生成前就插入提交了————数据可见
数据的事务id>(快照生成时)当时系统分配将要分配的事务id————该数据在快照生成后才插入————数据不可见
数据的事务id $(快照生成时)正在执行的事务id集合所有值————该数据在快照时事务还在运行(正运行)——————数据不可见
数据的事务id!$(快照生成时)正在执行的事务id集合所有值————该数据在快照时事务已经停止(已提交)——————数据可见
【不可见逻辑】
根据roll_pointer继续沿着链表遍历,判断可见性直到找到一个可见的就将数据返回
Mysql——事务回滚管理(待更新)