在NUMA处理器绑定多实例到固定核心

7月 1st, 2011 | Posted by | Filed under 数据库

本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
网址: http://www.penglixun.com/tech/database/mysql_multi_using_numactl.html

另发在:http://www.mysqlops.com/2011/07/01/mysql_multi_using_numactl.html

关于NUMA的介绍我这里就不多说了,网上太多资料了,我在这篇文章要介绍的是如何在MySQL多实例场景下使用numactl来绑定各个实例到具体的物理节点上,避免跨节点分配内存和跨节点访问寄存器。

至于为何使用多实例,因为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也全部都是闲的~

有兴趣的话赶紧尝试一下吧~

  1. laozhao6
    3月 13th, 201317:33

    其实这种实现,在mysql服务重启的情况下是存在问题的:
    比如说主机有2个节点,
    mysql.3306服务启动,占用节点1
    mysql.4406服务启动,占用节点0

    然后mysql.3306服务重启.占用节点0,和mysql.4406服务争用同一个节点,而节点1却是空闲的.

    其实最好的实现方式是:
    启动前,查询当前已经启动的mysqld进程都绑定了哪些节点,启动时优先使用被绑定次数最少的节点
    但numactl似乎不提供接口查询某个特定的进程已经绑定了哪些节点呀!

    另外,问一下:
    numactl runs processes with a specific NUMA scheduling or memory placement policy. The policy is set for command and inherited by all of its children.
    对于mysql这种多线程实现,随后的线程继承mysqld的numa策略.
    但对于oracle这样的多进程实现来说,随后的用户进程也继承numa策略吗??(我都不确定对于oracle而言,它的d进程应该是什么呀!)

    [回复]

    P.Linux 回复:

    @laozhao6, 嗯,后来我们更新了脚本,每次启动会输出当前节点绑定在哪,也可以手动强制指定绑定哪个核心。Oracle的NUMA似乎有bug,我看Oracle DBA都没开NUMA,并且打了NUMA补丁

    [回复]