MySQL在Linux操作系统层面的优化

理论部分

Swap调整

swap是干嘛的?

在 Linux 操作系统下,Swap 的作用类似 Windows 下的"虚拟内存"。当物理内存不足时,拿出部分硬盘空间当作内存使用,此部分硬盘空间称之为 Swap,从而解决内存空间不足的情况。

Swap 意思是交换,顾名思义,当某进程向 OS 请求内存时,OS 若是发现内存不足,就会把内存中暂时不用的数据交换出去,放在 Swap 中,这个过程称之为 "Swap out";当某个进程又需要这些数据且 OS 发现还有空闲的物理内存空间时,又会把 Swap 中的数据拿回到物理内存中,这个过程称为 "Swap in"。

当然,Swap 的大小是有上限的,一旦 Swap 的空间也被使用完,操作系统就会触发 OOM(Out Of Memory ) 机制,把消耗内存最多的进程 kill 掉以释放内存。

数据库系统为什么嫌弃 Swap?

显然,Swap 机制的初衷是为了缓解物理内存用尽而直接粗暴 OOM 机制的尴尬。但坦白讲,几乎所有的数据库对 Swap 都不怎么待见,无论MySQL、Oracle、MongoDB抑或HBase,为什么?这主要和以下两方面有关:

  1. 数据库系统一般对响应延迟比较敏感,如果使用 Swap 代替物理内存,数据库服务性必然不可接收。对于响应延迟极其敏感的系统来讲,延迟太大和服务不可用没有任何区别。比服务不可用更严重的是,Swap 场景下进程就是不死,这就意味着系统一直不可用......再想想如果不使用 Swap 直接 OOM,是不是更好的选择,这样很多高可用系统直接会进行主从切换,用户基本无感知。
  2. 另外,对于诸如 HBase 这类分布式系统来说,其实并不担心某个节点宕掉,而恰恰担心某个节点夯住。一个节点宕掉,最多就是小部分请求短暂不可用,重试即可恢复。但是一个节点夯住会将所有分布式请求都夯住,服务器资源被占用不放,导致整个集群请求阻塞,甚至集群被拖垮。

Swap 的工作机制

既然数据库们对 Swap 这么不待见,那是不是就要使用 "swapoff  [设备]" 命令关闭系统交换区呢?非也,大家可以想想,关闭 Swap 以为着什么?实际生产环境没有一个系统会如此激进,要知道这个世界永远不是非0即1的,大家都会或多或少选择走在中间,不过有些偏向0,有些偏向1而已。很显然,在swap这个问题上,数据库必然选择偏向尽量少用。HBase官方文档的几点要求实际上就是落实这个方针:尽可能降低swap影响。知己知彼才能百战不殆,要降低swap影响就必须弄清楚Linux内存回收是怎么工作的,这样才能不遗漏任何可能的疑点。

先来看看 Swap 是如何触发的?

简单来说,Linux 操作系统会在两种情况下触发内存回收:一种是在内存分配时,发现没有足够空闲内存时会立即触发内存回收;一种是开启了一个守护进程(kswapd进程)周期性对系统内存进行检查,在可用内存降低到特定阈值之后,就会主动触发内存回收。第一种场景没什么可说,来重点聊聊第二种场景,如下图所示:

kswapd 进程定义了三个三个内存阈值(watermark,也称水位),分别是:页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high),剩余内存则使用 pages_free 表示。

kswapd 进程会定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。

  • 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存
  • 剩余内存落在页最小阈值和页低阈值之间,说明内存压力很大,剩余内存不多了。这时 kswapd 进程会执行内存回收,直到剩余内存大于页高阈值为止
  • 剩余内存落在页低阈值和页高阈值之间,说明内存有一定的压力,但是还可以满足新的内存请求
  • 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力

我们可以看到,一旦剩余内存小于页低阈值,就会触发内存回收。这个页低阈值,其实可以通过内核选项 "/proc/sys/vm/min_free_kbytes"来间接设置。min_free_kbytes 设置了页最小阈值,而其它两个阈值,都是根据页最小阈值计算生成的,计算方法如下:

pages_low = pages_min*5/4
pages_high = pages_min*3/2

官方文档中要求 min_free_kbytes 不能小于1G(在大内存系统中设置8G),就是不要轻易触发直接回收。

至此基本了解了 Linux 的内存回收触发机制以及我们关注的参数 min_free_kbytes。接下来简单看看 Linux 内存回收都回收了些什么。Linux 内存回收对象主要分为两种:

  1. 文件缓存。这个容易理解,为了避免文件数据每次都要从硬盘读取,系统会将热点数据存储在内存中,从而提高性能。如果仅仅将文件读出来,内存回收只需要释放着部分内存即可,再次读取改文件数据直接从硬盘中读取即可。那如果不仅将文件数据读进了内存中,而且还对这心数据进行了修改(脏页),回收内存就需要将这部分数据刷新到磁盘,然后再释放内存空间
  2. 匿名内存。这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如典型的堆、栈数据等。这部分内存在回收的时候不能直接释放或者写回类似问及那的媒介中,所以才搞出来 swap 这个机制,将这类内存换出到硬盘中,需要的时候再加载出来。(ps:堆内存由应用程序自己来分配和管理。除非程序退出(进程退出了 内存肯定就被系统回收了),这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数 free() 来释放它们。如果应用程序没有正确释放堆内存,就会造成内存泄漏。)

具体 Linux 使用什么算法来确认哪些文件缓存或者匿名内存需要被会收掉,这里并不关心。但是有个问题 需要我们思考:既然有两类内存可以被回收,那么在这两类内存都可以被回收的情况下,Linux 是如何决定到底回收哪类内存呢?还是两者都回收?这里就迁出了我们关心的第二个参数 "swappiness",这个参数用来定义内核使用 swap 的积极程度,值越高,内核就会积极的使用 swap;值越低,就会降低对 swap 的使用积极性。该值的取值范围在0~100,默认是60。这个swappiness到底是怎么实现的呢?具体原理很复杂,简单来讲,swappiness通过控制内存回收时,回收的匿名页更多一些还是回收的文件缓存更多一些来达到这个效果。swappiness等于100,表示匿名内存和文件缓存将用同样的优先级进行回收,默认60表示文件缓存会优先被回收掉,至于为什么文件缓存要被优先回收掉,大家不妨想想(回收文件缓存通常情况下不会引起IO操作,对系统性能影响较小)。对于数据库来讲,swap是尽量需要避免的,所以需要将其设置为0。此处需要注意,设置为0并不代表不执行swap哦!

至此,我们从Linux内存回收触发机制、Linux内存回收对象一直聊到 swap,将参数 min_free_kbytes 以及 swappiness 进行了解释。接下来看看另一个与 swap 有关系的参数 "zone_reclaim_mode",文档说了设置这个参数为0可以关闭NUMA的zone reclaim,这又是怎么回事?提起NUMA,数据库们又都不高兴了,很多DBA都曾经被坑惨过。那这里简单说明三个小问题:NUMA是什么?NUMA和swap有什么关系?zone_reclaim_mode的具体意义?

NUMA(Non-Uniform Memory Access)是相对UMA来说的,两者都是CPU的设计架构,早期CPU设计为UMA结构,如下图(图片来自网络)所示:

为了缓解多核CPU读取同一块内存所遇到的通道瓶颈问题,芯片工程师又设计了NUMA结构,如下图(图片来自网络)所示:

这种架构可以很好解决UMA的问题,即不同CPU有专属内存区,为了实现CPU之间的”内存隔离”,还需要软件层面两点支持:

  1. 内存分配需要在请求线程当前所处CPU的专属内存区域进行分配。如果分配到其他CPU专属内存区,势必隔离性会受到一定影响,并且跨越总线的内存访问性能必然会有一定程度降低
  2. 另外,一旦 local内存(专属内存)不够用,优先淘汰local内存中的内存页,而不是去查看远程内存区是否会有空闲内存借用

这样实现,隔离性确实好了。但是问题也来了:NUMA 这种特性可能会导致CPU内存使用不均衡,部分CPU专属内存不够使用,频繁需要回收,进而可能发生大量 swap,系统响应延迟会严重抖动。而与此同时其他部分CPU专属内存可能都很空闲。这就会产生一种怪现象:使用free命令查看当前系统还有部分空闲物理内存,系统却不断发生 swap,导致某些应用性能急剧下降。

案例:有时候会遇到这样一个问题,明明 OS 还有大量的空闲内存,可是却发生了 swap 的情况。

1、现场排查

从上图能看出来什么呢?有几个关键的信息:

  • 系统负载不算高,最近的平均负载是6.8
  • CPU负载也不算高,有大量的空间,idle为98.4%
  • 内存主要分配给 mysqld 进程,占用了80.2%
  • 尽管物理内存有256G,空间的也将近39G,但是却发生了 swap,并且把 Swap 都耗尽了。

2、再执行 "free -gt" 查看内存、Swap消耗的情况,如下图:

一般来说,如果发现内存统计结果中,cachedused 相差特别大的话,基本可确定系统发生了内存泄漏。响应的处理方法有:

  • 治标办法:择机重启进程,彻底释放内存归还给 OS
  • 治本办法:找到代码中 导致内存泄漏的代码,修复之
  • 治本办法:升级程序版本,通常新版本会解决旧版本存在的问题,推荐此方案

若是升级程序的版本后,依然存在内存泄漏的问题,比如依然出现上面说的:物理内存还有不少空闲,但把swap都耗尽了。这时就要考虑是否是因为没有关闭 NUMA 引起的。(在运行数据库进程的服务器上,强烈建议关闭 NUMA)

3、查看一下 NUMA 的状况

从上面两张图可见,NUMA 问题导致了其中一个 CPU 的可分配内存远小于另一个(1.8G vs 38G),那么这个 CPU 上如果要申请大内存时,显然是不够的,所以发生 swap。

所以,对于小内存应用来讲,NUMA所 带来的这种问题并不突出,相反,local内存所带来的性能提升相当可观。但是对于数据库这类内存大户来说,NUMA 默认策略所带来的稳定性隐患是不可接受的。因此数据库们都强烈要求对 NUMA 的默认策略进行改进,有两个方面可以进行改进:

  1. 将内存分配策略由默认的亲和模式改为interleave模式,即会将内存page打散分配到不同的CPU zone中。通过这种方式解决内存可能分布不均的问题,一定程度上缓解上述案例中的诡异问题。对于MongoDB来说,在启动的时候就会提示使用interleave内存分配策略
  2. 改进内存回收策略:此处终于请出今天的第三个主角参数 zone_reclaim_mode,这个参数定义了 NUMA 架构下不同的内存回收策略,可以取值0/1/3/4,其中0表示在 local内存不够用的情况下可以去其他的内存区域分配内存;1表示在 local内存不够用的情况下本地先回收再分配;3表示本地回收尽可能先回收文件缓存对象;4表示本地回收优先使用 swap 回收匿名内存。可见,HBase 推荐配置 zone_reclaim_mode=0 一定程度上降低了swap发生的概率

 

I/O调度策略

IO调度策略一般有btrfs cfq,noop, deadline三种。

IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走。这恰恰就是现实生活中的电梯模型。所以IO调度器也被叫做电梯(elevator),而相应的算法也就被叫做电梯算法.而Linux中IO调度的电梯算法有好几种:as(Anticipatory)、cfq(Complete Fairness Queueing)、deadline、和noop(NoOperation)。具体调度原理请参考此篇博文:传送门

操作部分

Swap 调整

1、关闭numa

i、在操作系统中关闭
可以直接在/etc/grub.conf的kernel行最后添加numa=off,如下所示:

  kernel /vmlinuz-2.6.32-220.el6.x86_64 ro root=/dev/mapper/VolGroup-root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=VolGroup/root rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb crashkernel=auto rd_LVM_LV=VolGroup/swap rhgb crashkernel=auto quiet KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM  numa=off

#此种方式关闭numa需要重启机器才能生效,在实际生产中不推荐使用。


ii、在MySQL 5.7.9+ 版本以后,可以直接在MySQL配置文件/etc/my.cnf中配置"innodb_numa_interleave"

[root@db01 ~] # vim /etc/my.cnf
      ...
  innodb_numa_interleave=on
    
#这样只需要重启MySQL服务即可生效,推荐使用此方法
#当设置innodb_numa_interleave=on的时候,相当于mysqld进程的numa内存分配策略设置为MPOL_INTERLEAVE,而一旦Innodb buffer pool分配完毕,则策略重新设置回MPOL_DEFAULT。

 2、设置 swappiness 的值

i、查询当前swappiness的值
[root@db01 ~] # cat /proc/sys/vm/swappiness
    60

ii、查询当前swap使用情况
[root@db01 ~] # free -m
              total        used        free      shared  buff/cache   available
Mem:            974         289         303           7         381         508
Swap:          1535           7        1528

iii、清空swap
[root@db01 ~] # swapoff -a
[root@db01 ~] # free -m
              total        used        free      shared  buff/cache   available
Mem:            974         289         303           7         381         508
Swap:          1535           0        1535
[root@db01 ~] # swapon -a     #开启swap,不能完全关闭swap

iv、重新设置swappiness的值
(1)[root@db01 ~] # echo 0 >/proc/sys/vm/swappiness  #临时修改
(2)[root@db01 ~] # vim /etc/sysctl.conf       #永久修改
            ... 
        vm.swappiness=0
     [root@db01 ~] # sysctl -p      #使改设置永久生效

tip:
1、可通过"vmstat -an 1"命令来查看swap的变化
2、在CentOS 7中最好不要把swappiness设置为0,会有问题,设置成5~10即可

3、设置 zone_reclaim_mode 的值

例如:
[root@db01 ~] # echo 0 > /proc/sys/vm/zone_reclaim_mode

I/O调度策略

CentOS 7版本下:

CentOS 7版本默认的I/O调度策略为:deadline

[root@db01 ~] # cat /sys/block/sda/queue/scheduler  #查看默认的I/O调度策略

CentOS 6版本下:

i、临时修改I/O调度策略
[root@db01 ~] # echo deadline >/sys/block/sda/queue/scheduler   

ii、永久修改I/O调度策略
[root@db01 ~] # vim /boot/grub/grub.conf   #在内核文件中调整
       ...
   kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet
       ...

 

 

参考文章:

http://www.voidcn.com/article/p-ykacjcoj-gc.html

https://blog.csdn.net/jack_shuai/article/details/107908696

https://www.jianshu.com/p/ecd43c987711

https://blog.csdn.net/AXW2013/article/details/79659055

https://blog.csdn.net/xx5595480/article/details/100317293

https://blog.51cto.com/linzhijian/2061506

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值