共计 2836 个字符,预计需要花费 8 分钟才能阅读完成。
导读 | 实际上我看到一些国产数据库现在也在考虑使用多个 WAL WRITER 提升高并发 WAL 写入的性能,从而更为充分的利用 SSD 等现代硬件。不过 WAL 写入对于延时十分敏感,算法写不好,就容易引发更为严重的闩锁串行问题。 |
这些年 Oracle 发展的太快,我从 12C 之后就比较少参与运维工作,顶多帮着客户看看 AWR 报告,所以多 Oracle 12C 以后的很多细节实际上了解不多。搞了二十多年 Oracle,从 5.1 用到 11.2,Oracle 10G 出来的时候,我就说这应该是我学习的最后一个版本的 Oracle 了。没想到没搂住,11G 又搞了 10 年。12C 后因为不怎么做一线运维了,所以就没怎么关注了。
前几天群里朋友在讨论 PG WAL 写入存在性能问题的时候。群里有个朋友就问,难道 PG 这么土,不支持多个 WALWRITER 并发写吗?我当时想都没想就说,Oracle 也不支持啊,早期 Oracle 支持过 LGWR SLAVER,不过因为 BUG 太多,没什么人用,到 12C 以后,好像就没有 SLAVER 这码子事儿了。当时那个朋友就蒙圈了,Oracle 咋能不支持多个 LGWR 并发写呢?事后我问了问同事,他们说好像你记错了,12C 之后 Oracle 所有的 SLAVER 都被统一改成 WORKER 了。在 12C 里 LGWR worker 是自动开启的。
昨天正好有点空,我找了一些关于 12C LGWR worker 的资料看了看。在公司的测试环境上也找了一套 19.15 的环境检查了一下。发现还真如同事所说,12C 开始,Oracle 已经自动开启 LGWR 并发写了。在 12C 里增加了 LGnn 进程,用于实际写入 REDO 数据,LGWR 完全不管写 Redo Log 文件的事情,只负责发布一些和 REDO 落盘的消息了。
目前我看的关于 LGWR worker 的资料不多,从一些资料和我对 LGWR 的理解,LGWR worker 应该是和 Oracle Redo Strand 有关的。Oracle 的 LGWR worker 都是分配到 GROUP 的,GROUP 的数量如果是和 Redo public Strand 相关,那么每个 group 就之间就不需要通过锁机制来同步写入工作。LGWR 也不需要在多个 worker 之间做协同,而仅仅需要做和消息公告相关的共组了,这种机制应该是最为高效的。如果多个 worker 之间写 REDO 文件还需要闩锁来做串行化,那么效率肯定是不会好的。
Redo Strand 从 Oracle 11 开始就已经被用来加速 REDO 性能了,Strand 的目的是为了提高并发写入 Redo Log buffer 和 Redo Log 文件时候的性能,减少因为串行化闩锁等待导致的 REDO 性能问题。Oracle 会根据 CPU_COUNT 的值,自动的调整 Redo srand 的数量。
Oracle 会根据 CPU_COUNT/16 来设定 Strand 的数量,在 LOG BUFFER 中会按照 Strand 数量划分为 N 个子池,写入 REDO 数据的时候,可以并发的写入不同的 STRAND,这样可以减少高并发 LOG BUFFER 写入的性能。为了确保这一机制起作用,在 Redo Log 文件中,也是按照 Strand 的方式分配 Redo Log 文件,这种模式可以让 Redo Log 文件的写入也可以高速并发。Redo Strand 为 12C 的 LGWR worker 称为默认开启的功能打下了一个良好的基础。
我这个环境的 CPU_COUNT 是 16,而每个实例的 Redo Strand 最小值是 2,因此启动实例的时候也启动了 2 个 LGWR worker,这说明数据库实例有两个 LGWR worker group,当系统空闲,没有什么需要写入的 REDO 数据的时候,LGnn 都在等待空闲等待事件 LGWR worker group idle,而 lgwr 进程在等待 rdbms ipc message。
通过 strace 看 lgwr,也只是在做一些信号量方面的操作。我们再来看看空闲时的 LGnn。
也是在相同的信号量上休眠。
可以看出 LGWR 的等待事件发生了变化,而 LGnn 的等待事件也和以前的 LGWR 十分类似。从等待事件上看,当一个 worker 完成工作后,会处于 Ordering 等待,等待获取另外的写任务。在具体实现算法上,还并没有和我想象的一样不需要调度。我们再来 TRACE 一下 LGWR。
可以看出 LGWR 还是十分频繁的在操作那个信号量,这很可能是 LGWR 在积极参与日志写的调度协调。
从 worker 的行为上也看到了和 LGWR 之间的互动。这说明 Oracle 并发日志写还是需要多进程之间的同步行为,不是完全自主的无阻塞的。因此在某些场景下,可能会导致当 WORKER 数量过多时,引发 Log file parallel write 的等待时间过长,从而引起 LOG FILE SYNC 的增加,影响数据库的性能。
当年 Oracle 的 REDO STRAND 成为默认开启的时候,也出现过类似的问题,因为 STRANDS 数量时和 CPU_COUNT 相关的。十多年前在 Oracle 11g 上就有人发现了当 CPU 数量很多的时候,log file sync 会莫名其妙的变坏。
当时的建议时通过_log_parallelism_max 参数来减少 Strand 的数量,解决过多的 Strand 带来的性能问题。对于 LGWR worker 机制,Oracle 也提供了一个类似的参数来进行控制,这个参数就是“_max_log_write_parallelism”。
在 Oracle 12C 或者以后版本中,也经常会出现因为 LGWR worker 导致的性能问题。Oracle 可以通过“_use_single_log_writer”参数来进行调整。默认情况下这个参数的值时 ADAPTIVE,这意味着 Oracle 会自己根据工作负载来选择工作模式。如果遇到这方面的性能问题的时候,可以将这个参数设置为 TRUE,强制使用单个 LGWR,也就是恢复以前的工作模式。如果你发现你的数据库从 11G 升级到 12C 之后,log file sync 变坏了,从而导致了一些性能问题,你可以考虑调整这个参数。
数据库的应用场景十分复杂,我们享受某些应用场景受益于一个新技术的时候,难免会引发一些新的问题,某些场景可能和新技术的适应性不够好。另外加上一些新技术刚刚开始使用时,对某些特殊场景的算法不够优化,也会引发一些问题。我想,随着今后 Oracle 数据库版本的迭代,LGWR worker 的机制也会越来越成熟。
实际上我看到一些国产数据库现在也在考虑使用多个 WAL WRITER 提升高并发 WAL 写入的性能,从而更为充分的利用 SSD 等现代硬件。不过 WAL 写入对于延时十分敏感,算法写不好,就容易引发更为严重的闩锁串行问题。Oracle 的 Redo Strand 与 LGWR worker 相结合的机制应该是目前最值得借鉴的方法了。如果不针对 WAL BUFFER 做 Strand 分区,那么多个 WAL WRITER 的并发控制的成本会更高。