21-5-04_innodb内幕

6.恢复

innodb启动时不管上次是否正常关闭都会尝试进行回复操作

redo日志是物理日志,记录的是页的变化,比逻辑日志如binlog快很多

innodb顺序读取并行应用redo日志

checkpoint表示刷新到磁盘的lsn,在恢复过程中从checkpoint开始的日志部分,例如在checkoint的lsn为1 0000时宕机,恢复仅在1 0000 ~ 1 3000范围内的日志

21-5-04_innodb内幕

幂等性:操作一次与重复多次操作的结果是一致的

redo日志是幂等的,记录的是页的改变,binlog不是幂等的,记录的是sql语句等mysql上层的操作

7.2.2 undo

1.概念

innodb在数据库修改过程中会产生undo日志,来用于回滚操作

undo日志放在undo段中(undo segment),位于共享表空间里面

undo不是将数据库物理地恢复到执行语句或事务之前的情况,只是将数据库逻辑地恢复到原来的样子,修改被逻辑地取消了,但数据结构和页本身在回滚后可能大不相同

在并发中,数十上千事务进行操作,一个事务在修改一个页的几条记录,另外事务在修改页的其他记录,不可能将页回滚到事务开始的样子

回滚中进行的是相反操作

insert <-> delete

update <-> update

同时MVVC也是通过undo实现的,当一行记录被其他事务锁定时,当前事务可以通过Undo读取之前的版本信息来实现非锁定读

undo也会产生undo log

2.undo存储管理

采用段管理,回滚段rollback segment,每个记录了1024个undo log segment,在每个undo log segment进行undo页的申请

共享表空间偏移量为 5 的页记录了所有rollback segment header所在的页,FIL_PAGE_TYPE_SYS

innodb现版本支持128个rollback segment,对在线事务的限制提高到128 * 1024

某些参数可以对rollback segment进行设置

  • innodb_undo_directory

rollback segment所在的路径,即可以设置在共享表空间外的地方,默认为‘.‘,表示innodb的目录

  • innodb_rollback_segments

设置rollback segment的个数,默认128

  • innodb_undo_tablespaces

设置构成rollback segment文件的数量,使其较为评价分布在多个文件

事务在undo log segment分配页写入undo log过程同样需要写入重做日志

事务提交时:

  • 将undo log放入列表,以供purge操作
  • 判断undo log所在页是否可以重用,可以则分配给下一个事务

不能立即删除undo log及其所在的页,因为其他事务需要通过undo log实现MVVC来得到之前的版本,将undo log放入一个链表,又purge线程来判断

为事务单独分配一个单独undo页会浪费空间,对于OLTP(查询居多)类型,因为事务提交后不能立即释放页。

innodb对undo页进行重用:

事务提交时将其放入链表中,然后判断undo页空间是否小于3 / 4,是则可以被重用,之后的undo日志记录在当前页的undo日志后

3.undo log格式

undo lod分为

  • insert undo log

insert操作产生的log,仅对本事务可见,在事务提交后可以直接删除,不需要purge操作

21-5-04_innodb内幕

*表示对存储的字段进行了压缩

next:2字节,下一个undo log的位置

start:2字节,本undo log开始的位置

type_cmpl:1字节,undo类型,insert un log值为11

undo_no:事务的id

table_id:undo log对应的表对象

所有主键的列和值,进行回滚时定位到具体记录,删除

  • update undo log

delete和update产生的log,可能需要提供MVVC机制,不能在提交时删除,提交时放入undo log链表,等待purge线程删除

21-5-04_innodb内幕

next、start、undo_no、table_id和之前的insert undo log

type_cmpl的可能的值有:

  • 12 TRX_UNDO_UPD_EXIST_REC

不更新主键,即不用删除记录,即non-delete-mark

  • 13 TRX_UNDO_UPD_DEL_REC

将delete记录标记为not delete

  • 14 TRX_UNDO_UPD_DEL_MARK_REC

将记录标为delete

记录update_vector信息,表示update操作改变的列,每个修改列的信息都要记录在undo log中,甚至可能记录对索引列做的修改

delete操作并不直接删除记录,而是将记录标记为已删除,即delete flag设置为1,最终由purge操作完成

更新操作涉及主键时,将原主键记录标记为已删除,产生trx_undo_del_mark_rec的undo log,之后插入新记录,产生rex_undo_insert_rec的undo log

7.2.3 Purge

delete和update可能并不直接删除原有数据,被purge延时删除,来支持MVVC

purge来判断是否可以删除该记录,当该行记录不被其他任何事物引用时则可以进行真正的delete操作

页中允许多个事务的undo log存在,但不代表着事务在全局过程中的顺序,innodb中有一个history列表,根据事务提交的顺序,将undo log进行连接

21-5-04_innodb内幕

history按照事务提交的顺序将undo log组织,先提交的总在尾端

Purge执行时,先从history找到第一个需要被清理的记录,设为trx1,清理后会在页中继续寻找,发现了trx3、trx5,但trx5被其他事务使用,于是再回到histroy列表寻找,找到了trx2,再在页中寻找,清理了trx6,trx4

先在histroy list中寻找,再在undo page寻找,减少了大量的随机读取操作,提高效率

innodb_purge_batch_size设置了每次purge需要清理的数量

mysql> show variables like ‘innodb_purge_batch_size‘;
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_purge_batch_size | 300   |
+-------------------------+-------+
1 row in set, 1 warning (0.01 sec)

分配过大可能导致cpu和io集中undo log处理

当innodb压力大时不能高效进行purge操作,history的长度越来越长,innodb_max_purge_lag来控制长度,大于时则延缓DML操作,默认为0,不进行限制

mysql> show variables like ‘innodb_max_purge_lag‘;
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| innodb_max_purge_lag | 0     |
+----------------------+-------+
1 row in set, 1 warning (0.00 sec)

当数值大于0时,延缓算法为:

delay = ((length(history_list) - innodb_max_purge_log) * 10) - 5

单位毫秒,对象是行,在每次purge操作后会重新计算

当delay值大于innodb_max_purge_lag_delay时则设置为其值,避免purge缓慢导致sql线程无限制等待

7.2.4 group commit

非只读事务在提交时要进行fsync操作,来保证redo日志都写入磁盘,当数据库宕机时,可以通过redo日志恢复

fsync的性能有限的,于是出现了group commit功能,即一个fsync可以刷新多个事务的日志写入磁盘

innodb提交事务:

  • 修改内存事务的信息,将日志写入redo日志缓冲
  • fsync将日志从日志缓冲写入磁盘

第二步相比第一步速度慢,所以当有事务在进行第二步时其他事务可以进行第一步,之后再进行第二步,于是就是将多个事务的redo日志通过一次fsync写入多个事务的redo日志,对写入或更新频繁的事务,group commit效果明显

当binlog开启时group commit会失效,且运行环境会为了replication复制情况开启binlog

原因是为了保证存储引擎层的事务和binlog一致,使用了两阶段事务:

  • (1)事务提交时innodb进行prepare操作
  • (2)mysql数据库上层写入binlog日志
  • (3)innodb将日志写入redo日志文件
    • (a)修改内存事务的信息,将日志写入redo日志缓冲
    • (b)fsync将日志从日志缓冲写入磁盘

当mysql数据库上层写入binlog日志后,事务就提交了,即使innodb将日志写入redo日志文件发生了宕机

且每个步骤都要进行fsync操作来保证上下层的一致性

(2)由sync_binlog控制,(3)由innodb_flush_log_at_trx_commit控制

mysql> show variables like ‘sync_binlog‘;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog   | 1     |
+---------------+-------+
1 row in set, 1 warning (0.01 sec)

mysql> show variables like ‘innodb_flush_log_at_trx_commit‘;
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
+--------------------------------+-------+
1 row in set, 1 warning (0.00 sec)

21-5-04_innodb内幕

mysql使用了 pre_commit_mutex锁来保证binlog写入顺序和innodb事务提交顺序一致,锁开启后,(3)(a)不可以在其他事务执行(b)时进行,因此group commit失效

binlog写入顺序和innodb事务提交顺序一致是为了保证可以建立副本replication

21-5-04_innodb内幕

事务T1-T3原本是按顺序进行的,但恢复时T3进行了提交了,默认前面事务完成了提交,于是T1未提交的数据则丢失了

21-5-04_innodb内幕

加锁来保证事务执行顺序

改进:

Binary Log Group COmmit(BLGC)

将事务分成几个步骤来提交:

21-5-04_innodb内幕

数据库上层提交时按顺序放入队列中,第一个事务称leader,其他为follower,leader控制其他,步骤如下:

  • flush阶段:将binlog写入内存
  • sync阶段:将binlog写入到磁盘,队列中多个事务经过一个fsync操作写入(BLGC)
  • commit阶段:leader根据顺序调用innodb事务的提交,于是使用group commit

当一组事务在commit时,其他事务可以进行flush,使group commit生效

binlog_max_flush_queue_time控制flush阶段等待时间,即前一组事务提交,后一组事务不立即进入sync阶段,而是等待一段时间,使group commit更多事务,但相应事务响应时间变长,默认0,推荐0,除非存在大量连接不断地写入或更新

7.3 事务控制语句

  • start transaction | begin

显式开启事务

  • commit

提交事务

  • rollback (work)

回滚事务

  • savepoint idntifier

设置保存点,可以有多个

  • release savepoint identifier

删除保存点

  • rollback to identifier

回滚到某个标记点

  • set transaction

设置事务隔离级别

存储过程开启事务只能使用 start transaction

completion_type控制commit work 与commit 是否等价

为0表示等价

为1 commit work 表示 commit and chain,链事务,自动开启相同隔离几倍的事务

为2 表示 commit and release,断开和数据库的连接

在使用rollback to identifier回到保存点之后事务还没有提交,需要提交或者回滚会开始前

7.4 隐式提交sql语句

某些语句会隐式提交事务

  • DDL语句:

21-5-04_innodb内幕

  • 隐式修改mysql架构:

create | drop | rename user、grant、revoke、set password

  • 管理语句:

analyze table、cache index、check table、load index into cache、optimize table、repair table

truncate table是DDL,与删除同效,但不能被回滚

7.5 对事务操作的统计

每秒请求数:Question Per Second,QPS

每秒事务处理能力:Transacion Per Second,TPS

TPS:(com_commit + com_rollback) / time,事务显示提交

mysql> show global status like ‘com_commit‘;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_commit    | 0     |
+---------------+-------+
1 row in set (0.00 sec)

mysql> show global status like ‘com_rollback‘;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_rollback  | 0     |
+---------------+-------+
1 row in set (0.00 sec)

7.6 事务的隔离级别

  • read uncommitted读未提交
  • read committed读已提交
  • repeatable read可重复读
  • serializable

设置当前会话或者全局事务隔离级别

set [global | session] transaction isolation level {
read uncommitted |
read committed |
repeatable read | 
serializable
}

在配置文件中设置

transaction-isolation = read-committed

查看当前会话级别

mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

mysql> SELECT @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+
1 row in set (0.00 sec)

serialiable隔离级别下,innodb对每个select语句自动加上lock in share mode,每个读加一个共享锁,对一致性非锁定读不支持了,主要用于分布式事务

read committed隔离级别下除了唯一性的约束检查及外键约束需要gap lock外不会使用此锁算法

21-5-04_innodb内幕

上一篇:MyBatis+MySQL8.0实现数据持久化


下一篇:SQL 导入EXCEL文件报错 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序