嵌入式技术
本篇文章着眼于 Linux 页面大小对数据库性能的影响,以及如何优化数据库 Kubernetes 节点。
大多数流行的数据库都受益于 Linux 大页面。
Kubernetes 最初旨在大规模编排容器的生命周期,用于轻量级、无状态应用程序,如 Ngnix、Java 和 Node.js。对于这个用例,Linux 4K 页面是正确的选择。
最近,通过添加Statefulsets、Persistent Volumes和大页面等功能,Kubernetes 得到了增强,以支持大型、有状态、持久性数据库。
下图显示了使用 Linux 大页面对数据库性能的影响有多大。
上图显示,对于相同的数据库,相同的数据,相同的工作负载,使用Linux 2MB页面而不是4K页面时吞吐量可以提高8倍。该图还显示,随着并发水平的提高,大页面的好处也随之增加。
文章的其余部分介绍了一些背景概念,并着眼于影响数据库工作负载的 Linux 页面大小的因素。
Linux 页面大小
所有现代多用户操作系统都使用虚拟内存来使不同的进程能够使用内存而不必担心底层细节。Linux x86 64 的系统使用分页进行虚拟内存管理。
Linux x8664 支持以下页面大小:
4K
2MB
1GB
页大小是可用于虚拟内存管理的连续数据的最小单位。
页面的大小是一种权衡。4K 页面最大限度地减少了小内存分配的内存浪费。对于大内存分配,使用 2MB 或 1GB 页面总共需要更少的页面,而且速度会明显更快,因为将虚拟内存转换为物理内存地址会产生相关成本。
TLB 缓存命中和未命中
Linux 上任何进程的每次内存访问(例如,无论是 Nginx、Node.js 还是 MySQL)都需要从虚拟内存转换为物理内存。由于这是一个常规的操作,所有 CPU 都有某种形式的转换后备缓冲区[TLB],它充当最近转换的内存地址的缓存。
所有从虚拟内存到物理内存的转换首先查看 TLB 中是否已经存在映射。如果映射已经存在,则称为 TLB 缓存命中。TLB 缓存命中非常快,并且发生在硬件中。当 TLB 缓存中不存在从虚拟内存到物理内存的转换时,称为 TLB 缓存未命中。TLB 缓存未命中需要通过页面遍历在 Linux 内核页表中的软件中解决映射。尽管页面遍历是高效的 C 代码,但它比通过 TLB 缓存在硬件中进行映射要慢得多。
为什么 TLB 缓存未命中对数据库很重要
所有数据库最终都需要访问内存中的数据进行读取或写入。所有这些数据库读取或写入都需要至少进行一次 TLB 查找。TLB 缓存未命中会显著减慢数据库的读写速度:
数据库越大,访问的不同页面越多,需要的 TLB 查找就越多。这实际上是数据库工作集大小。
并发越大,单位时间需要的 TLB 查找越多
如果您有具有可变长度数据类型(例如字符串、JSON、CLOB 或 BLOB)的行/记录,那么这些行/记录的宽度很容易超过 4KB。当 Linux 页面大小为 4KB 时,访问宽度为 20KB 的单行/记录通常需要至少五次 TLB 查找。如果使用 2MB 或 1GB Linux 页面,访问相同的 20KB 行/记录通常只需要一次 TLB 查找。所以一般来说,数据库行(rows)/记录(record)越宽,大页面与 4K Linux 页面的优势就越大。
挑战在于 CPU 具有少量 TLB 缓存条目:
Intel Ice Lake
4K 页面的 L1 TLB 中64个条目,2MB 页面的 32 个条目,1G 页面的 8 个条目
4K + 2MB 页面的 L2 TLB 中有1024个条目
4K + 1GB 页面的 L2 TLB 中有1024个条目
AMD EPYC Zen 3
4K + 2MB 页面 + 1GB 页面的 L1 TLB 中的64 个条目
4K 和 2MB 页面的 L2 TLB 中的512 个条目
由于 L1 CPU 缓存通常只有大约 64 个 TLB 4K 条目,而最新的 Intel 和 AMD CPU 上的 L2 缓存则有 512 到 1024 个 4K 条目,如果您的数据库具有宽行/记录并访问许多不同的行/记录,那么它几乎总是会得到 TLB 缓存未命中。
如果您使用 2MB 页面,那么您不太可能遇到 TLB 缓存未命中,因为您有效地使 TLB 缓存更大:
AMD EPYC Zen 3 CPU 比 L1 和 L2 CPU 缓存大 512 倍
英特尔 Ice Lake CPU 的 L1 CPU 缓存大 256 倍,L2 CPU 缓存大 512 倍
减少 TLB 缓存未命中的数量可以对数据库性能产生显著的积极影响。
基准
Linux 并不关心你的数据库是 MySQL、PostgreSQL 还是 Oracle。Linux 并不关心您的应用程序是用 Node.js、Java、Go、Rust 还是 C 编写的。Linux 性能取决于诸如工作负载每单位时间发生多少 TLB 缓存未命中等指标。
以下基准测试着眼于几种配置:
窄行(Narrow)/记录 [128 字节],访问 1 亿条不同的行/记录的概率均匀
整个行/记录应适合单个 4KB Linux 页面
中等行(Medium)/记录 [8 KB],平均访问 1 亿条不同的行/记录的概率
行/记录应至少适合两个 4KB Linux 页面
更宽的行(Wider)/记录 [16 KB],甚至有可能访问 1 亿条不同的行/记录
行/记录应至少适合四个 4KB Linux 页面
16 KB 不是很宽,但结果很显著
要最小化变量的数量:
只执行数据库读取,数据库中的所有 1 亿行都可以轻松放入 DRAM 并且数据库被“预热”
数据库客户端使用 IPC 而不是 TCP 套接字来访问数据库
这种配置意味着没有磁盘 IO 或网络处理,因此工作负载会在 CPU 和/或内存访问上出现瓶颈。
128 字节行/记录的4K Linux 页面
上图显示,在 AMD EPYC 7J1C3 @ 2.55 GHz 处理器上使用 4K Linux 页面和 128 个数据库连接,在单台 Linux 机器上每秒可以执行超过 350 万次数据库读取。
128 字节行/记录的4K 与 2MB 页面
上图显示,对于相同的硬件、相同的数据库、相同的表、相同的数据、相同的查询,2 MB 的大页面可以实现比使用 4K Linux 页面时多出 8 倍的吞吐量。
对于窄行/记录,吞吐量提高 8 倍是一个显著的结果。
8 KB行/记录的4K 与 2MB 页面
对于 8KB 宽的数据库行/记录,2MB 页面可以提供比 4K 页面多 8 倍的吞吐量。
对于中等宽度的行/记录,吞吐量提高 8 倍是一个重要的结果。
16 KB行/记录的4K 与 2MB 页面
上图显示,对于相同的硬件、相同的数据库、相同的表、相同的数据、相同的查询,2 MB 的大页面可以实现比使用 4K Linux 页面时多出 5 倍的吞吐量。
对于更宽的行/记录,吞吐量提高 5 倍是一个重要的结果。
2MB 和 1GB Linux 页面怎么样
很容易看出 2MB Linux 页面与 4K 页面的优势,例如提高 8 倍。您是否还希望看到 2MB 和 1GB Linux 页面之间的显著差异?
由于所有测试的行宽都可以放入 2MB 页面,唯一的变量是 2MB 与 1GB Linux 页面的 TLB 缓存未命中率,用于 1 亿不同的行/记录。
对于所有经过测试的行宽 [128 字节、8KB 和 16KB],1GB Linux 页面的吞吐量比 2MB Linux 页面高 1% 到 21%。
虽然高达 21% 的吞吐量改进不如 8 倍令人印象深刻,但它仍然存在一些差异。
也许行/记录宽于 2MB 的测试会显示显著差异?
Kubernetes 节点专业化
在 Kubernetes 的早期,工作负载往往用于小型、无状态的“基于 Web”的应用程序,例如负载均衡器、Web 服务器、代理和各种应用程序服务器。对于这个用例,使用 Linux 4K 页面是一个合适的选择。
最近,更专业的工作负载正在 Kubernetes 集群中运行,这些集群具有不同的硬件和/或软件要求。例如,机器学习工作负载可以在通用 x86 64位 CPU 上运行,但在具有 GPU 或 ASIC 的 Kubernetes 节点上运行速度往往要快得多。此外,某些 Kubernetes 节点可能专门用于具有快速本地存储、更多 RAM 或可能运行 ARM 64 CPU。
因此,并非所有 Kubernetes 节点都具有完全相同的 CPU、RAM、存储等,一些节点可以使用守护程序集或节点标签来定义和公开这些节点的特定功能。使用POD 标签[使用选择器来匹配节点标签],允许 Kubernetes 调度程序在最合适的节点上自动运行 POD。
上图显示了具有四种类型的专用节点的 Kubernetes 集群。
你可以做些什么来优化 Kubernetes 上的数据库性能
通常不在您控制范围内的事情:
数据库行/记录的宽度
您的数据库中有多少行/记录
您的数据库工作集大小
数据库中数据访问的并发性和频率
CPU 的 TLB 缓存大小
在范围内可以控制你的Kubernetes集群的事情:
Linux 内核在 Linux x8664 Kubernetes 节点上使用 4KB、2MB 还是 1GB Linux 页面
您配置了多少 Linux 大页面 [2MB 或 1GB]
Kubernetes 应用程序的内存和大页面资源 的请求和限制
数据库被认为是 Kubernetes 中的一个应用程序
您可以选择为要在其上运行数据库工作负载的一组机器配置具有 2MB 或 1GB 大页面的 Kubernetes 节点 [即 Linux 主机]。
在 Linux上配置大页面的方式与Kubernetes 无关。您必须在 Linux 内核中配置大页面,因为您无法在 Kubernetes 或容器级别执行此操作。通常你想关闭透明大页面,因为它们通常不会提高数据库性能,只会浪费内存。
在 Linux x8664 上配置 2MB 页面对于任何 Linux 发行版都相当简单,通常无需更改启动时间参数即可完成。
配置 1GB Linux 页面的步骤因发行版而略有不同,并且需要启动时间参数。我能够在最近的 Intel Xeon 和 AMD CPU 上配置 1 GB Linux 页面,用于:
红帽企业 Linux 7.9 和 8.4
Oracle Linux 7.9 和 8.4
CentOS 7 和 8
Ubuntu 18.04 和 20.04
SuSE 12 和 SuSE 15
您应该为 Kubernetes 上的数据库配置多少大页面
这个问题是特定于数据库的。这取决于您的 Kubernetes 节点有多少 RAM、您希望在该节点上运行多少其他 [非数据库] POD、这些 POD 需要多少 RAM,以及最终您的数据库通过使用更多内存而受益多少。
总结
大多数流行的数据库在 Linux x86 64 上看到了大页面的性能优势
Kubernetes 支持 4KB、2MB 和 1GB 的 Linux 页面
由于历史原因,大多数 Kubernetes 集群使用 4KB Linux 页面
许多 Kubernetes 集群根据工作负载优化一些节点 [例如机器学习、快速本地存储、通用无状态 Web 应用程序等]
考虑添加另一类经过优化以提高数据库性能的节点。
使用 2MB 或 1GB 页面在某些 Kubernetes 节点上配置 Linux 内核以优化它们的数据库性能
根据您的数据库为这些机器选择适当数量的大页面和 4K Linux 页面
编辑:黄飞
全部0条评论
快来发表一下你的评论吧 !