一、大致现象
如图,启动操作和某些查询均改变redolog和lsn。
二、堆栈情况及分析
1. 启动时更新max_trx_id
2. 启动时更新临时表空间元数据
3. 表空间变更,即便没有变更也要提交
4. collation和charset
更新collation,从charset重新生成,无论是否有更新
更新charset对collation的引用,无论是否有更新
提交collation和charset的更新
5. 验证resource_group,如果不通过则禁用,重新设置用户默认组和系统默认组
疑问:为何用户默认组和系统默认组即便没有修改也要更新?
默认组在启动时无视数据字典中的配置,通过硬编码的方式直接更新数据字典,覆盖原配置,不检查是否有修改。
6. Flush prepared gtid
PREPARE_GTID机制即在事务中两次写GTID到undo log中,第一次是在prepare时,第二次是在commit时,但没找到注释里描述的只在commit时写GTID的老版本,在8.0.30中反倒是只在XA prepare时才会写GTID,在8.0.18中是在本地prepare时写GTID,18和30的版本均会在本地提交时写GTID,在更早的5.7版本中根本不会写入GTID到undo log中,因此注释中所述不再在写COMMIT GTID前等待PREPARE GTID flush暂时还无法理解。Clone_persist_gtid::flush_gtids将新生成的GTID从内存中写入到mysql.gtid_executed表,这一般是一种周期性的写入,但在启动时,会一次性将启动过程中积累的GTID写入到mysql.gtid_executed表。这里写入redolog的并不是server层的GTID,而是trx->no,是从serialisation_list写入最大的trx->no到系统头,然后数据库启动初始化回滚段的时候会用到。注意到trx->no的更新与是否积累了GTID无关,只要serialisation_list被更新就会触发,在每次周期性写入中都要做,并且有相关事件机制,因此如果只读查询触发了serialisation_list的更新就会导致redolog的写入。
7. 重新安装performance_schema.innodb_redo_log_files表,先drop再add
疑问:为何要清空数据,为何必须使用drop-add方式?
该表存储在内存中,每次查询都会清空数据并重新生成数据,因此没必要在启动时清空数据,也没必要使用drop-add方式。这里的考虑或许是以防该表元数据被人为修改过?
删除performance_schema.innodb_redo_log_files表
删除performance_schema.innodb_redo_log_files表的提交操作
重新创建performance_schema.innodb_redo_log_files表
重新创建performance_schema.innodb_redo_log_files表的提交操作
8. checkpoint线程中元数据刷脏
疑问:为何元数据刷脏要写redolog?如果之前不是在redolog中,那是在哪里?
这里的刷脏并不是数据字典回写脏页的含义,DDL中对数据字典的修改一般在用户线程完成,和DDL log一起记在redolog中,这些数据回写脏页显然是不须要再次写redolog的。这里的刷脏是指将DDL中涉及到的元数据信息持久化到mysql.tables, mysql.columns, mysql.indexes, mysql.tablespaces, mysql.innodb_ddl_log等几个表中。在启动中,由于要重新创建performance_schema.innodb_redo_log_files表,会修改mysql.tables和mysql.columns。
9. purge线程组
purge worker线程,删辅助索引节点
purge worker线程,从聚簇索引中删除
purge coordinator线程,LOB的完整purge由于worker线程的多线程协作原因,被延迟到批处理之后
purge coordinator线程,truncate undo log