MySQL 开发

[MySQL5.6] 最近对group commit的小优化

最近花了一些时间在做MySQL Group Commit的优化,关于Group commit的原理,这里不再赘述,有兴趣的可以翻阅我之前的博客http://mysqllover.com/?p=581,这里简单描述下两点优化,主要基于MySQL5.6.16 1.优化binlog_order_commits=0并且sync_binlog>0时的性能  我们知道当binlog_order_commits关闭时,表示我们能接受binlog commit和innodb commit的顺序不同(这不会带来数据不一致,但可能会影响到热备份),关闭该选项可以带来一定程度的性能提升。 本优化也是基于该前提,假定sync_binlog =1000, 那么在第1000组事务进入sync stage时,需要去做binlog sync,我们知道fsync操作是非常慢且耗时的操作,而第1001组事务,显然无需去做sync,如果我们允许innodb/binlog commit失序,就可以让第1001组事务跳过sync stage,直接进入innodb commit detail见http://bugs.mysql.com/bug.php?id=73018,附加补丁 2.延迟写redo直到group commit时来提升性能 我们知道MySQL使用Binlog,Innodb XA的方式来进行crash recovery,所有记录在binlog中的事务我们都期望能够commit掉; 这意味着,在写binlog之前,需要确保事务的prepare状态被写到redo中,这样才能从crash中恢复. 原生的逻辑中,各个事务各自做innodb prepare, 并写redo log; 只有到了commit阶段,进入ordered_commit,才进入组提交; 我们主要集中在group commit的第一阶段:flush stage。 在该阶段,leader线程从队列中pop 线程加入queue,并依次flush thread cache到binlog文件. 修改后的流程: 1. 在Innodb prepare阶段不再write/sync redo log,而是直接返回 2.在group commit的flush stage阶段,修改成如下逻辑 a) 收集组提交队列 b) 调用ha_flush_logs 做一次redo write/sync c) 将队列中thd的所有binlog cache写到binlog文件中 detail见http://bugs.mysql.com/bug.php?id=73202, 附加补丁 […]


近期对semisync的一些优化

最近对同机房semisync性能优化告一段落,性能基本能达到预期;目前优化的版本基于MySQL5.6.14,基本优化思路是让备库尽快的发送ACK。主要的优化点简单总结下: a).备库在接受到需要ack的事件时,先ack,再去写relay log。对于不需要ack的事件只写IO Cache 原始行为为每接受到一个事件就写一次relay log,在同机房场景下,可能存在网络IO已经完成,备库还没写完数据到磁盘 b).备库在需要ack时才去刷master info文件 原始行为是每接受一个事件写一次master_info文件 a)和b)以牺牲备库日志的持久化来提升semisync场景下的主库性能,当备库挂了时,只需要从一个安全的位点重拉binlog即可; 使用sysbench测试的结果:TPS 13000(原生semisync) => 24000 (改进后semisync) 无semisync的TPS约为28000 上述测试同时基于以下的改进 c).主库上在事务commit之前等待备库ack(AFTER_SYNC),在等待期间其他事务无法看到等待线程所做的修改,如果主库挂了,切换到备库后,应用连接只需要检查数据是否更新成功即可 (Port  From  MySQL5.7.2) 原始行为为在事务commit之后(AFTER_COMMIT),每个应用连接等待备库ack,这种情况下在主备切换后,应用连接将无法判断其事务是否成功提交(数据可能被修改多次) AFTER_SYNC比AFTER_COMMIT更能保证数据的一致性,并且有一定的性能提升,因为在AFTER_SYNC发生在innodb group commit之前,由Leader线程来等待,只需要唤醒leader,而AFTER_COMMIT在大thread running下,可能需要唤醒大量应用连接,开销更大 d).mysql_binlog_send函数不再需要持有Lock_log锁来读取binlog事件,而是维持一个文件末尾的偏移量;(Port From MySQL5.7.2, Facebook mysql5.6有类似实现) 原始行为中,写binlog和读binlog都需要Lock_log这个大锁来保护,这会导致锁竞争,如果有多个备库dump线程,这种竞争会影响到TPS e).在rotate时,需要生成下一个binlog文件,在设定文件名后缀时,避免扫描整个文件夹,改为维护一个递增数字 其他一些小的修改点不一一列出; semisync接下来潜在的优化点: a).大量dump线程对主库性能的影响需要纳入考虑(例如bug#70342) b).如何保证主备库的强一致;例如,主库宕机,事务已写入本地Binlog,但还没发送到备库;主备切换后,如果应用链接在备库重试事务,则主备一致;如果应用抛弃该事务,或走其他逻辑,那么挂掉重启后的主库可能比备库多执行了事务(利用ADHA回滚机制?) c).在跨机房场景下,网络称为瓶颈时,如何解决(并发网络IO?) d).内建semisync,而不是通过plugin的形式,是否可能带来性能提升? e).主库semisync 的大锁对性能的影响? 原创文章,转载请注明: 转载自Simple Life 本文链接地址: 近期对semisync的一些优化 Post Footer automatically generated by wp-posturl plugin for wordpress.


备库由于表无主键导致延迟

由于ROW模式的复制已经广泛使用,但对于没有主键的表而言,如果发生大更新,在备库上会表现出极大的延迟,因为在binlog中产生的大量行记录将无法根据主键快速查找,最差的情况,需要对每条修改的记录进行全表扫描。 5.6已经解决了这个问题,可以只扫描一次表;5.5最新的版本只是在错误日志里输出了一些信息。 Port 5.6的实现不太现实,因为改动太大。因此我做了些小改动,对于无主键表上的DELETE/UPDATE,转换为STATEMENT模式的binlog记录。 以下是一个改动非常简单的patch,基于Percona5.5.18 Index: /PS5518/branches/PS-r3633-nopk-logstmt/sql/sys_vars.cc =================================================================== — /PS5518/branches/PS-r3633-nopk-logstmt/sql/sys_vars.cc (revision 3639) +++ /PS5518/branches/PS-r3633-nopk-logstmt/sql/sys_vars.cc (revision 3641) @@ -396,6 +396,13 @@ CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(binlog_direct_check)); +static Sys_var_mybool Sys_binlog_use_stmt_for_non_pk( + “binlog_use_stmt_for_non_pk”, + “if a table doesn’t have primary key ,then log the changes (SQLCOM_DELETE” + “and SQLCOM_UPDATE) using STATEMENT.”, + SESSION_VAR(binlog_use_stmt_for_non_pk), + CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + static […]


限制MySQL Binlog的传输速率

最近一台核心库备库完成恢复后打开slave,导致主库传送binlog,瞬间占满网络,触发故障。 为了做一些限制, 给mysql在发送binlog的函数(mysql_binlog_send)里每隔一段时间sleep一次, 增加了两个参数: master_send_count  每读master_send_count次事件,sleep一次 master_send_sleep   每次sleep的时间为master_send_sleep ,单位为毫秒ms 以下统计数据,每1秒统计一次。 1.不受限制 set global master_send_count = 0; set global master_send_sleep = 0; 2.  set global master_send_count = 10; set global master_send_sleep = 100; 3. set global master_send_count = 10; set global master_send_sleep = 200; 4. set global master_send_count = 1; set global master_send_sleep = 15; 5. […]