NUMA 问题曾经一直是困扰DBA的一个大问题,MySQL在x86系统下存在严重的 “swap insanity” 问题。在NUMA架构越来越普遍的今天,这个问题越来越严重。
就是当你把主机大部分内存分配给InnoDB时,你会发现明明操作系统还有很多内存,但是却有很多内存被交换到了SWAP分区。
有两个方式可以解决这个问题:
在Linux Kernel启动参数中加上numa=off(这样也会影响到其他进程使用NUMA);
在mysqld_safe脚本中加上“numactl –interleave all”来启动mysqld。
当然如果跑多实例,我也用过直接绑定mysqld进程到某个numa节点的方式,不过这要求每个实例分配的内存不超过每个NUMA节点管理的内存。
关于多实列
至于为何使用多实例,因为MySQL对于多处理机和大内存的利用效率不佳,采用多实例可以很大程度提高MySQL对资源的利用,详情可以看Percona的白皮书中对多实例的测试:Scaling MySQL With Virident Flash Drives and Multiple Instances of Percona Server .
numactl这个程序的用法可以参照man手册:http://linux.die.net/man/8/numactl
基本用法是“numactl [option] 程序路径”,例如我希望用numactl启动mysqld则是numactl [option] usr/local/mysql/bin/mysqld。曾经我误以为numactl是控制某一个程序名,汗……亲手做过才明白是程序路径。
我只介绍几个重要参数
–interleave=all 这是使用交叉分配模式启动一个程序,也就是说程序可以随意跨节点用其他节点的内存,传说中这是效率最高的关闭NUMA特性的方法,只是传说。
–cpunodebind=node 这是把程序绑定在指定的node节点上运行,即使另一个物理节点是idle的,也不会去使用。
–localalloc 严格控制只在节点内分配内存,禁止分配其他节点下的内存到当前节点运行的程序。
我们启动MySQL希望的参数是 numactl –cpunodebind=node –localalloc mysqld_path
为了运维方便,我不可能每次mysql启动都这么执行,我依然希望通过/etc/init.d/mysql和mysqld_multi来管理mysql启动和关闭,于是我采用自定义启动脚本的方式。
首先编写自定义启动脚本如下:
#!/bin/sh
# Program Path
NUMACTL=`which numactl`
MYSQLD=/usr/alibaba/mysql/libexec/mysqld
PS=`which ps`
GREP=`which grep`
CUT=`which cut`
WC=`which wc`
EXPR=`which expr`
# Variables
CPU_BIND=(`$NUMACTL --show | $GREP nodebind | $CUT -d: -f2 `) # CPU bins list
CPU_BIND_NUM=${#CPU_BIND[@]} # How many CPU binds
MYSQLD_NUM=`$PS aux | $GREP mysqld | $GREP -v grep | $GREP '\' | $WC -l`
MYSQLD_NUM=`$EXPR $MYSQLD_NUM + 1`
BIND_NO=`$EXPR $MYSQLD_NUM % $CPU_BIND_NUM ` # Calc Which CPU to Bind
# echo CMD
echo "$NUMACTL --cpunodebind=$BIND_NO --localalloc $MYSQLD" > tmp/mysqld.$MYSQLD_NUM
# use exec to avoid having an extra shell around.
exec $NUMACTL --cpubind=$BIND_NO --localalloc $MYSQLD "$@"
复制
方法是查看当前有多少个mysqld进程已经存在,并且通过numactl –show判断有多少个物理节点,从而判断当前的进程应该分配给哪个节点,例如有2个物理节点,没有mysqld进程,则分配当前进程到0节点,再启动一个实例,当前已经有1个mysqld进程,则分配到1节点,再启动一个实例到0节点……依次循环。
然后在my.cnf文件中配置使用我们自己的脚本启动:
[mysqld_safe]......ledir=/usr/local/mysql/bin/# 放自定义脚本的目录mysqld=mysqld_using_numactl # 自定义脚本的名称复制
然后再用/etc/init.d/mysql或mysqld_multi启动mysqld进程就可以实现绑定了。
你可以先启动一个实例,然后在MySQL里做一些消耗CPU的操作,可以观察到只有一个物理节点上的core有活动,哪怕这个节点的core全是100%的利用率,另一个节点的core也全部都是闲的~
代码层面解决NUMA问题
如果在代码层面彻底解决NUMA问题,那么我们需要解决两个问题:
全局内存应该采用interleave的分配方式分散在不同的numa node上;
线程内存应该采用local的分配方式分配在线程运行的numa node上
Linux 提供了 set_mempolicy() 函数可以用来设置进程的内存分配策略,其中默认的MPOL_DEFAULT策略就是在当前运行的节点上分配内存,而MPOL_INTERLEAVE策略则是跨所有节点来分配内存
#include <numaif.h>
long set_mempolicy(int mode, const unsigned long *nodemask,
unsigned long maxnode);
复制
因此对于MySQL Server和InnoDB引擎都需要做修改:
在mysqld_main()入口设置 set_mempolicy(MPOL_INTERLEAVE, NULL, 0) 启用全局分配方式;
在MySQL启动完成之后设置set_mempolicy(MPOL_DEFAULT, NULL, 0) 启用本地分配方式;
在InnoDB入口时设置 set_mempolicy(MPOL_INTERLEAVE, NULL, 0) 启用全局分配方式;
在Buffer Pool分配完成时设置 set_mempolicy(MPOL_DEFAULT, NULL, 0) 启用本地分配方式。
MySQL 5.6.27, 5.7.9 发布之后,将会增加一个 innodb_numa_interleave 参数来控制这个策略。innodb_numa_interleave 如果打开,那么将会按上面的策略来设置内存分配方式,如果关闭或者主机不支持NUMA,那么还是按原来的方式分配。
安装
备份和恢复
主从复制
运维优化
SQL优化
mysql大量的waiting for table level lock怎么办
using filesort VS using temporary
MYSQL开发
MYSQL分区维护
MGR集群
dba+开源工具:MySQL 8.0 MGR高可用VIP切换脚本
源码阅读
压测试工具
MYSQL 安全