MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MySQL性能瓶颈探究:操作系统与硬件因素

2022-08-016.8k 阅读

硬件因素对 MySQL 性能的影响

CPU 相关因素

  1. CPU 核心数与频率
    • MySQL 作为多线程应用,在处理大量并发查询时,CPU 核心数起着关键作用。更多的核心能够同时处理多个线程任务,减少线程等待时间。例如,在一个具有 8 核心的 CPU 服务器上运行 MySQL,相比于 4 核心的服务器,理论上能够同时处理更多的查询线程。
    • 代码示例:可以通过SHOW STATUS LIKE 'Threads_connected';查看当前连接到 MySQL 的线程数,当线程数接近或超过 CPU 核心数时,可能会出现 CPU 资源竞争。
    • CPU 频率也很重要。较高的频率意味着 CPU 能够更快地执行指令,对于复杂的 SQL 查询,如包含大量聚合函数、连接操作的查询,高频率的 CPU 可以更快地完成计算。例如,查询统计一个大型电商数据库中每个品类的总销售额和平均销售额:
SELECT category, SUM(sales_amount), AVG(sales_amount)
FROM products
GROUP BY category;

在高频率 CPU 的服务器上,此查询可能会更快地得到结果。 2. CPU 缓存

  • CPU 缓存分为一级缓存(L1)、二级缓存(L2)和三级缓存(L3)。缓存的存在是为了减少 CPU 从内存中读取数据的时间。MySQL 在执行查询时,数据和指令会在 CPU 缓存中暂存。例如,如果一个经常查询的表数据能够常驻在 CPU 缓存中,那么后续对该表的查询就可以直接从缓存中获取数据,大大提高查询速度。
  • 假设我们有一个频繁查询的users表,包含用户的基本信息。当 MySQL 第一次查询该表时,数据从内存加载到 CPU 缓存。如果后续查询条件相同,就可以从缓存中快速获取数据,而不需要再次从内存读取。
  • 代码示例:可以通过一些性能测试工具,如sysbench来模拟大量查询操作,观察 CPU 缓存命中率对查询性能的影响。首先安装sysbench,然后使用以下命令模拟 OLTP 读操作:
sysbench oltp_read_only --tables=1 --table_size=100000 --threads=16 run

在不同的 CPU 缓存配置下运行此命令,对比性能指标,如每秒事务数(TPS)等。

内存相关因素

  1. 物理内存大小
    • MySQL 在运行过程中需要大量内存来缓存数据和索引。足够的物理内存可以减少磁盘 I/O 操作。例如,InnoDB 存储引擎有一个缓冲池(Buffer Pool),它会将经常访问的数据页和索引页缓存到内存中。如果物理内存足够大,缓冲池可以容纳更多的数据和索引,使得大部分查询可以直接从内存中获取数据,而不需要从磁盘读取。
    • 假设我们有一个包含 1000 万条记录的orders表,并且该表的索引大小为 5GB。如果物理内存只有 8GB,并且系统还有其他应用在运行,那么缓冲池可能无法完全容纳该表的索引,导致频繁的磁盘 I/O 操作,影响查询性能。
    • 代码示例:可以通过SHOW STATUS LIKE 'Innodb_buffer_pool_pages_data';SHOW STATUS LIKE 'Innodb_buffer_pool_pages_total';来查看缓冲池中已使用的数据页和总数据页数量,评估缓冲池的使用情况。
  2. 内存分配策略
    • MySQL 有多个内存组件,如查询缓存(虽然在 MySQL 8.0 中已弃用)、排序缓冲区、连接缓冲区等。合理分配这些内存组件的大小对性能至关重要。例如,排序缓冲区用于在查询中对数据进行排序操作。如果排序缓冲区设置过小,对于需要排序的大结果集查询,可能会导致多次磁盘 I/O 操作,因为无法一次性在内存中完成排序。
    • 以下是设置排序缓冲区大小的配置参数示例,在my.cnf文件中:
[mysqld]
sort_buffer_size = 2M

这里将排序缓冲区大小设置为 2MB。如果经常执行大结果集排序的查询,可以适当增大这个值。但也要注意,过大的内存分配可能会导致系统内存不足,影响其他组件的运行。

存储相关因素

  1. 磁盘 I/O 性能
    • MySQL 数据存储在磁盘上,磁盘 I/O 性能对其影响巨大。传统机械硬盘(HDD)的读写速度相对较慢,尤其是在随机 I/O 场景下。而固态硬盘(SSD)具有更快的读写速度,特别是在 4K 随机读写方面,优势明显。例如,在一个频繁进行插入、更新操作的数据库中,SSD 可以显著减少 I/O 等待时间,提高数据库的响应速度。
    • 假设我们有一个日志记录数据库,每秒有大量的日志数据插入。如果使用 HDD,由于其机械寻道时间长,插入操作可能会变得非常缓慢。而使用 SSD,插入操作可以更快地完成,因为 SSD 没有机械寻道的延迟。
    • 代码示例:可以使用sysbench工具的文件 I/O 测试来模拟数据库的 I/O 操作,评估不同磁盘类型的性能。例如:
sysbench fileio --file-total-size=1G prepare
sysbench fileio --file-total-size=1G --file-test-mode=rndrw run

在 HDD 和 SSD 上分别运行此命令,对比读写速度等性能指标。 2. 磁盘阵列配置

  • 磁盘阵列(RAID)可以通过不同的方式组合多个磁盘,以提高性能、数据冗余或两者兼顾。例如,RAID 0 通过条带化技术将数据分散存储在多个磁盘上,可以提高读写性能,因为可以并行从多个磁盘读取数据。但 RAID 0 没有数据冗余,如果一个磁盘出现故障,数据将丢失。
  • RAID 1 则通过镜像技术,将数据同时存储在两个磁盘上,提供数据冗余,但写入性能可能会略有下降,因为数据需要同时写入两个磁盘。
  • 在 MySQL 应用中,如果对性能要求较高且允许一定的数据丢失风险,可以选择 RAID 0。如果数据的安全性至关重要,如银行数据库,可能会选择 RAID 1 或更高级的 RAID 5、RAID 6 等。
  • 代码示例:在操作系统层面,可以通过mdadm工具来创建和管理 RAID 阵列。以下是创建一个 RAID 0 阵列的示例命令:
mdadm --create /dev/md0 --level=0 --raid-devices=2 /dev/sda1 /dev/sdb1

这里使用/dev/sda1/dev/sdb1两个分区创建了一个 RAID 0 阵列/dev/md0

操作系统因素对 MySQL 性能的影响

文件系统

  1. 文件系统类型
    • 不同的文件系统对 MySQL 性能有不同的影响。例如,EXT4 是 Linux 系统中常用的文件系统,它在性能和稳定性方面表现良好。EXT4 支持大文件和大分区,并且具有较好的日志功能,能够保证数据的一致性。在 MySQL 数据存储方面,EXT4 的元数据管理和 I/O 调度策略能够满足大部分场景的需求。
    • 另一个文件系统 XFS 也越来越受到关注,特别是在处理超大文件和高并发 I/O 场景下。XFS 具有高性能的日志记录和高效的空间分配算法,对于 MySQL 数据库这种需要频繁读写大文件的应用,XFS 可以提供更好的性能。
    • 代码示例:在 Linux 系统中,可以通过以下命令将分区格式化为 EXT4 文件系统:
mkfs.ext4 /dev/sda2

而格式化为 XFS 文件系统的命令为:

mkfs.xfs /dev/sda2

可以在不同文件系统上部署相同的 MySQL 数据库实例,并使用性能测试工具,如mysqlslap进行测试。例如:

mysqlslap --create-schema='test' --query='SELECT * FROM test_table' --concurrency=10 --iterations=5

对比在 EXT4 和 XFS 文件系统下的测试结果,评估文件系统对 MySQL 性能的影响。 2. 文件系统参数调优

  • 文件系统有一些参数可以进行调优,以适应 MySQL 的需求。例如,对于 EXT4 文件系统,noatime参数可以减少文件访问时间的更新,从而减少 I/O 操作。在/etc/fstab文件中,可以将挂载选项设置为noatime
/dev/sda2 /var/lib/mysql ext4 defaults,noatime 0 0

这里将/var/lib/mysql目录所在的分区挂载为使用noatime选项。

  • 对于 XFS 文件系统,swalloc参数可以调整文件系统的空间分配策略,提高 I/O 性能。可以通过xfs_admin命令来设置:
xfs_admin -O swalloc /dev/sda2

这些参数的调整需要根据具体的应用场景和硬件环境进行测试和优化。

内存管理

  1. 操作系统内存分配策略
    • 操作系统的内存分配策略会影响 MySQL 的性能。例如,在 Linux 系统中,kswapd守护进程负责将不常用的内存页交换到磁盘交换空间(swap)中。如果 MySQL 服务器内存紧张,kswapd可能会将 MySQL 缓冲池中的数据页交换出去,导致严重的性能下降。
    • 可以通过调整swappiness参数来控制系统将内存页交换到 swap 的倾向。swappiness的值范围是 0 - 100,默认值通常为 60。将其设置为较低的值,如 10,可以减少内存页交换到 swap 的概率,提高 MySQL 的性能。可以通过以下命令临时调整swappiness值:
echo 10 > /proc/sys/vm/swappiness

要永久生效,可以在/etc/sysctl.conf文件中添加:

vm.swappiness = 10
  1. 透明大页(Transparent Huge Pages)
    • 透明大页(THP)是 Linux 内核的一项功能,旨在提高内存管理效率。然而,对于 MySQL 来说,THP 有时会导致性能问题。THP 以 2MB 的页面大小进行内存分配和管理,而 MySQL 内部的内存管理机制可能与之不兼容。
    • 当 THP 开启时,MySQL 在申请内存时可能会遇到碎片化问题,导致额外的内存开销和性能下降。可以通过以下命令检查 THP 的状态:
cat /sys/kernel/mm/transparent_hugepage/enabled

如果显示为[always] madvise never,表示 THP 处于开启状态。可以通过以下命令关闭 THP:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

要永久关闭,可以在/etc/rc.local文件中添加上述命令。

进程调度

  1. 调度算法
    • 操作系统的进程调度算法决定了 CPU 时间如何分配给各个进程。在 Linux 系统中,默认的调度算法是 Completely Fair Scheduler(CFS)。CFS 旨在公平地分配 CPU 时间给所有进程。然而,对于 MySQL 这种对 CPU 时间敏感的应用,有时可能需要更优化的调度策略。
    • 例如,实时调度算法(如 SCHED_FIFO 和 SCHED_RR)可以给予 MySQL 更高的 CPU 优先级,使其能够更快速地执行查询任务。但使用实时调度算法需要谨慎,因为如果设置不当,可能会导致其他进程饿死。
    • 可以通过chrt命令来改变进程的调度策略和优先级。例如,要将 MySQL 进程的调度策略设置为 SCHED_RR 并将优先级设置为 50:
chrt -r -p 50 `pidof mysqld`

这里pidof mysqld获取 MySQL 服务进程的 PID。 2. CPU 亲和性

  • CPU 亲和性是指将进程绑定到特定的 CPU 核心上运行。对于 MySQL 服务器,合理设置 CPU 亲和性可以提高性能。例如,如果 MySQL 服务器运行在一个具有多个 CPU 核心的服务器上,将 MySQL 进程绑定到特定的核心组,可以减少 CPU 核心切换带来的开销。
  • 在 Linux 系统中,可以使用taskset命令来设置 CPU 亲和性。假设 MySQL 进程的 PID 为 1234,要将其绑定到 CPU 核心 0 - 3 上运行,可以使用以下命令:
taskset -p -c 0-3 1234

这样 MySQL 进程就只会在 CPU 核心 0 - 3 上运行,避免了在其他核心上运行带来的额外开销。

I/O 调度

  1. I/O 调度算法
    • 操作系统的 I/O 调度算法决定了如何将 I/O 请求发送到存储设备。在 Linux 系统中,常见的 I/O 调度算法有noopdeadlinecfq(Completely Fair Queuing)。
    • noop调度算法非常简单,它只是将 I/O 请求放入一个 FIFO 队列中,适用于 SSD 设备,因为 SSD 本身的随机读写性能很高,不需要复杂的调度算法。deadline调度算法旨在减少 I/O 响应时间,它为每个 I/O 请求设置一个截止时间,优先处理即将到期的请求,对于 MySQL 这种对 I/O 响应时间敏感的应用,deadline算法在某些情况下可以提高性能。
    • cfq调度算法试图公平地分配 I/O 带宽给各个进程。然而,对于 MySQL 这种需要大量连续 I/O 的应用,cfq可能不是最佳选择,因为它可能会将 I/O 请求分散,导致性能下降。
    • 可以通过修改/sys/block/sda/queue/scheduler文件来更改 I/O 调度算法。例如,要将 I/O 调度算法设置为deadline,可以使用以下命令:
echo deadline > /sys/block/sda/queue/scheduler

这里假设存储 MySQL 数据的磁盘设备为/dev/sda。 2. I/O 调度参数调优

  • 对于不同的 I/O 调度算法,还有一些参数可以进一步调优。例如,对于deadline调度算法,read_expirewrite_expire参数可以调整读写请求的截止时间。默认情况下,read_expire为 500 毫秒,write_expire为 5000 毫秒。
  • 如果 MySQL 读操作较多,可以适当减小read_expire的值,以提高读性能。可以通过以下命令查看和修改这些参数:
cat /sys/block/sda/queue/read_expire
echo 200 > /sys/block/sda/queue/read_expire

这里将read_expire值修改为 200 毫秒。同样,对于写操作,可以根据实际情况调整write_expire参数。

网络相关因素

  1. 网络带宽与延迟
    • 在分布式数据库环境或多服务器架构中,网络带宽和延迟对 MySQL 性能影响显著。例如,如果 MySQL 主从复制架构中,主服务器和从服务器之间的网络带宽不足,会导致主服务器上的数据更改不能及时同步到从服务器,从而影响数据的一致性和查询性能。
    • 高网络延迟也会导致查询响应时间增加。假设客户端与 MySQL 服务器之间的网络延迟较高,客户端发送的查询请求需要较长时间才能到达服务器,服务器的响应也需要较长时间返回给客户端。
    • 可以使用ping命令来测试网络延迟,例如:
ping mysql -server -ip

使用iperf工具来测试网络带宽,例如:

iperf -c mysql -server -ip

如果网络带宽不足,可以考虑升级网络设备或网络链路。对于高延迟问题,可以排查网络拓扑、路由等问题。 2. TCP/IP 参数调优

  • 操作系统的 TCP/IP 参数可以进行调优,以提高 MySQL 的网络性能。例如,在 Linux 系统中,tcp_window_scaling参数可以调整 TCP 窗口大小,提高网络传输效率。默认情况下,tcp_window_scaling是开启的。
  • 可以通过以下命令查看和修改该参数:
sysctl net.ipv4.tcp_window_scaling
sysctl -w net.ipv4.tcp_window_scaling=1

另外,tcp_timestamps参数也会影响网络性能,它用于 TCP 连接的时间戳记录。可以根据实际情况调整该参数:

sysctl net.ipv4.tcp_timestamps
sysctl -w net.ipv4.tcp_timestamps=0

这些参数的调整需要根据网络环境和 MySQL 的应用场景进行测试和优化,以达到最佳的网络性能。