InnoDB 在设计 lock-free 的 log system 时, 除了已有的参数外, 还通过宏控制隐藏了一些参数, 如果你使用源码编译时, 打开 cmake 选项 - DENABLE_EXPERIMENT_SYSVARS=1, 就可以看到这些参数了. 本文主要简单的过一下这些隐藏的参数所代表的含义
A.
- innodb_log_write_events
- innodb_log_flush_events
两者的含义类似, 表示用来唤醒等待 log write/flush 的 event 的个数, 默认值都是 2048
比如你要等待的位置在 lsnA, 那么计算的 slot 为:
slot = (lsnA - 1) /OS_FILE_LOG_BLOCK_SIZE & (innodb_log_write/flush_events - 1)
这意味着: 如果事务的 commit log 的 end lsn 落在相同 block 里, 他们可能产生 event 的竞争
当然如果不在同一个 block 的时候, 如果调大参数, 就可以减少竞争, 但也会有无效的唤醒
唤醒操作通常由后台线程 log_write_notifier 或者 log_flush_notifier 异步来做, 但如果推进的 log write/flush 还不足一个 block 的话, 那就 log_writter/flusher
自己去唤醒了.
B.
innodb_log_recent_written_size, 默认 1MB
表示 recent_written 这个 link_buf 的大小, 其实控制了并发往 log buffer 中同时拷贝的事务日志量, 向前由新的日志加入, 后面由 log writer 通过写日志向前推进, 如果写的慢的话, 那这个 link_buf 很可能用满, 用户线程就得 spin 等待. 再慢 io 的系统上, 我们可以稍微调大这个参数
innodb_Log_recent_closed_size, 默认 2MB
表示 recent closed 这个 link_buf 的大小, 也是维护可以并发往 flush list 上插入脏页的并罚度, 如果插入脏页速度慢, 或者 lin_buf 没有及时合并推进, 就会 spin wait
简单说下 link_buf, 这本质上是一个数组, 但使用无锁的使用方式来维护 lsn 的推进, 比如获得一个 lsn 开始和结束, 那就
通过设置 buf[start_lsn] = end_lsn 的类似方式来维护 lsn 链, 基于 lsn 是连续值的事实, 最终必然不会出现空洞, 所以在演化的过程中, 可以从尾部
推进连续的 lsn, 头部插入新的值.
如果新插入的值超过了尾部, 表示 buf 满了, 就需要 spin wait 了
C.
- innodb_log_wait_for_write_spin_delay,
- innodb_log_wait_for_write_timeout
从 8.0 版本开始用户线程不再自己去写 redo, 而是等待后台线程去写, 这两个变量控制了 spin 以及 condition wait 的 timeout 时间, 当 spin 一段时间还没推进到某个想要的 lsn 点时, 就会进入 condition wait
另外两个变量
- innodb_log_wait_for_flush_spin_delay
- innodb_log_wait_for_flush_timeout
含义类似, 但是是等待 log flush 到某个指定 lsn
注意在实际计算过程中, 最大 spin 次数, 会考虑到 CPU 利用率, 以及另外两个参数:
- innodb_log_spin_cpu_abs_lwm
- innodb_log_spin_cpu_pct_hwm
如果是等待 flush 操作的话, 还收到参数 innodb_log_wait_for_flush_spin_hwm 限制, 该参数控制了等待 flush 的时间上限, 如果平均等待 flush 的时间超过了这个上限的话, 就没必要去 spin, 而是直接进入 condition wait
关于 spin 次数的计算方式在函数 log_max_spins_when_waiting_in_user_thread 中 ":
函数的参数即为配置项 innodb_log_wait_for_write_spin_delay 或 innodb_log_wait_for_flush_spin_delay 值
- static inline uint64_t log_max_spins_when_waiting_in_user_thread(
- uint64_t min_non_zero_value) {
- uint64_t max_spins;
- /* Get current CPU usage. */
- const double CPU = srv_cpu_usage.utime_pct;
- /* Get high-watermark - when CPU usage is higher, don't spin! */
- const uint32_t hwm = srv_log_spin_cpu_pct_hwm;
- if (srv_cpu_usage.utime_abs <srv_log_spin_cpu_abs_lwm || CPU>= hwm) {
- /* Don't spin because either cpu usage is too high or it's
- almost idle so no reason to bother. */
- max_spins = 0;
- } else if (CPU>= hwm / 2) {
- /* When CPU usage is more than 50% of the hwm, use the minimum allowed
- number of spin rounds, not to increase CPU usage too much (risky). */
- max_spins = min_non_zero_value;
- } else {
- /* When CPU usage is Less than 50% of the hwm, choose maximum spin rounds
- in range [minimum, 10*minimum]. Smaller usage of CPU is, more spin rounds
- might be used. */
- const double r = 1.0 * (hwm / 2 - CPU) / (hwm / 2);
- max_spins =
- static_cast<uint64_t>(min_non_zero_value + r * min_non_zero_value * 9);
- }
- return (max_spins);
- }
D. 以下几个参数是后台线程等待任务时 spin 及 condition wait timeout 的值
log_writer 线程:
- innodb_log_writer_spin_delay,
- innodb_log_writer_timeout
log_flusher 线程:
- innodb_ log_flusher_spin_delay
- innodb_log_flusher_timeout
log_write_notifier 线程:
- innodb_ log_write_notifier_spin_delay
- innodb_log_write_notifier_timeout
log_flush_notifier 线程
- innodb_log_flush_notifier_spin_delay
- innodb_log_flush_notifier_timeout
log_closer 线程 (用于推进 recent_closed 这个 link_buf 的专用线程)
- innodb_log_closer_spin_delay
- innodb_log_closer_timeout
- E
- innodb_ log_write_max_size
表示允许一个 write 操作最大的字节数, 默认为 4kb, 这个是在推进 recent_written 这个 link buf 时计算的, 个人认为这个限制太小了, 可以适当调大这个参数.(然而 8.0 的最大写入限制还受到 innodb_log_write_ahead_size 限制, 两者得综合起来看)
- F
- innodb_log_checkpoint_every
默认 1000 毫秒 (1 秒), 表示至少每隔这么长时间 log_checkpointer 线程会去尝试做一次 checkpoint. 当然是否做 checkpoint 还受到其他因素的影响, 具体见函数 log_should_checkpoint:
- a) more than 1s elapsed since last checkpoint
- b) checkpoint age is greater than max_checkpoint_age_async
- c) it was requested to have greater checkpoint_lsn,
- and oldest_lsn allows to satisfy the request
G. 参考:
MySQL8.0.16 源代码
来源: https://yq.aliyun.com/articles/703648