从09年12月份MySQL5.5的第一个Release,再到去年12月的第一个GA,MySQL5.5平均一月一个小版本号的速度在默默更新。该版本最重要的特性目前仍是Semi-sync Replication,之前做了一个概述,这里将继续介绍如何安装、配置Semi-sync,后续还将继续介绍其性能和一些基本的代码实现。
Semi-sync最早是由Google实现的一个补丁,代码主要由Mark Callaghan、Wei Li(@Google)等人贡献。Google原本是将需求提给Hekki的,但是后来等不及就自己实现了……。(现在Mark Callaghan已跳到Facebook,除了Google,他曾经还在Informix、Oracle工作过)
在5.5之后由MySQL Replication Team按照Plugin的方式将代码移植过来,并将Semi-sync独立成MySQL的一个插件,主要代码移植者是Zhenxing He(参考)。
在两台主机上安装好MySQL5.5,编译好的插件在目录CMAKE_INSTALL_PREFIX/lib/plugin下(默认是/usr/local/mysql/lib/plugin)。例如这里编译是指定CMAKE_INSTALL_PREFIX为/home/mysql/mysql,则有:
/home/mysql/mysql/lib/plugin
$ls -a semisync_*
semisync_master.so semisync_slave.so
在主库初次启动时,执行如下语句加载semisync_master插件:
备库上则加载semisync_slave插件:
在初次加载插件后,MySQL会将该插件记录到系统表mysql.plugin中,下次启动时系统则会自动加载该插件了,无需再次执行上面的命令。
另外,主备库的配置文件my.cnf还需要新增如下记录来打开semi-sync。主库上,新增如下参数:
…
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000
…
备库上新增:
…
rpl_semi_sync_slave_enabled=1
…
那么主备在启动后,且slave线程开始dump主库的日志后,Semi-sync Replication就会开启,上面的配置(rpl_semi_sync_master_timeout=1000)表示主库在某次事务中,如果等待时间超过1000毫秒,那么则降级为普通模式,不再等待备库。如果主库再次探测到,备库恢复了,则会自动再次回到Semi-sync状态。
Semi-sync的配置参数不多,按照上面配置就可以了,其他少数几个参数默认即可。
主库上的其他参数:
mysql> show variables like "%rpl_semi%"; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 1000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+
备库上有:
mysql> show variables like "%rpl_semi%"; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_wait_no_slave | ON | | rpl_semi_sync_slave_enabled | ON | | rpl_semi_sync_slave_trace_level | 32 | +------------------------------------+-------+
rpl_semi_sync_master_trace_level和rpl_semi_sync_slave_trace_level是可以组合(求或)的参数,可以是下面值的组合:
1 = general level (for example, time function failures)
16 = detail level (more verbose information)
32 = net wait level (more information about network waits)
64 = function level (information about function entry and exit)
备库上很简单,只有一个状态值,查看备库是否开启了Semi-sync功能:
show status like "%rpl_semi%"; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+
主库需要关注的状态值则比较多
mysql> show status like "%rpl_semi%"; +--------------------------------------------+---------------+ | Variable_name | Value | +--------------------------------------------+---------------+ | Rpl_semi_sync_master_clients | 1 |# 有多少个Semi-sync的备库 | Rpl_semi_sync_master_net_avg_wait_time | 732 |# 事务提交后,等待备库响应的平均时间 | Rpl_semi_sync_master_net_wait_time | 1042785285 |# 等待网络响应的总次数 | Rpl_semi_sync_master_net_waits | 1423323 |# 总的网络等待时间 | Rpl_semi_sync_master_no_times | 0 |# 一共有几次从Semi-sync跌回普通状态 | Rpl_semi_sync_master_no_tx | 0 |# 备库未及时响应的事务数 | Rpl_semi_sync_master_status | ON |# 主库上Semi-sync是否正常开启 | Rpl_semi_sync_master_timefunc_failures | 0 |# 时间函数未正常工作的次数 | Rpl_semi_sync_master_tx_avg_wait_time | 29059 |# 开启Semi-sync,事务返回需要等待的平均时间 | Rpl_semi_sync_master_tx_wait_time | 8164966680929 |# 事务等待备库响应的总时间 | Rpl_semi_sync_master_tx_waits | 280974820 |# 事务等待备库响应的总次数 | Rpl_semi_sync_master_wait_pos_backtraverse | 5499398 |# 改变当前等待最小二进制日志的次数 | Rpl_semi_sync_master_wait_sessions | 230 |# 当前有几个线程在等备库响应 | Rpl_semi_sync_master_yes_tx | 16801244 |# Semi-sync模式下,成功的事务数 +--------------------------------------------+---------------+
上面比较重要的状态值有:
Rpl_semi_sync_master_tx_avg_wait_time:事务因开启Semi_sync,平均需要额外等待的时间
Rpl_semi_sync_master_net_avg_wait_time:事务进入等待队列后,到网络平均等待时间
依据上面两个状态值可以知道,Semi-sync的网络消耗有多大,给某个事务带来的额外的消耗有多大。
Rpl_semi_sync_master_status 则表示当前Semi-sync是否正常工作。
从Rpl_semi_sync_master_no_times变量,可以知道一段时间内,Semi-sync是否有超时失败过,该计数器则记录了这样的失败次数。
比较难理解参数有:
Rpl_semi_sync_master_wait_pos_backtraverse:这个稍微难理解一点,不过该参数并不重要。
花了一些时间弄明白这个变量,继续证实了这个变量确实不重要。
在semisync_master中,维护了这样的两个变量wait_file_name_和wait_file_pos_,当主库上多个事务都在等待备库的响应时,这两个变量记录了所有等待中,最小的那一个Binlog位置。如果这时,一个新的事务加入等待,并且该事务需要等待的Binlog比wait_file_name_和wait_file_pos_还小的话,则更新这两个值,并将Rpl_semi_sync_master_wait_pos_backtraverse值自增一次。
上面的解释参考如下代码段(semisync_master.cc):
int cmp = ActiveTranx::compare(trx_wait_binlog_name, trx_wait_binlog_pos, wait_file_name_, wait_file_pos_); if (cmp <= 0) { /* This thd has a lower position, let's update the minimum info. */ strcpy(wait_file_name_, trx_wait_binlog_name); wait_file_pos_ = trx_wait_binlog_pos; rpl_semi_sync_master_wait_pos_backtraverse++; if (trace_level_ & kTraceDetail) sql_print_information("%s: move back wait position (%s, %lu),", kWho, wait_file_name_, (unsigned long)wait_file_pos_); }
累了,听首歌,伸个懒腰吧:)
参考文献:
1. ReplicationFeatures/SemiSyncReplication
Leave a Reply