数据库内核月报 - 2015 / 09-MySQL · 备库优化 · relay fetch 备库优化

业务背景

MySQL 主备通过 binlog 实现数据同步的功能,主库将生成的 binlog 通过 binlog send 线程发送到备库,备库通过应用这些 binlog 来更新数据,实现主备数据一致,其应用 binlog 的读取操作与更新操作的堆栈分别如下。

读取操作:

#0  row_search_for_mysql
#1  0x0000000000c200c2 in ha_innobase::index_read
#2  0x0000000000c21c57 in ha_innobase::rnd_pos
#3  0x000000000090c5d3 in handler::rnd_pos_by_record
#4  0x0000000000a574c3 in Rows_log_event::find_row
#5  0x0000000000a589da in Delete_rows_log_event::do_exec_row
#6  0x0000000000a50dcc in Rows_log_event::do_apply_event
#7  0x00000000005d0bb8 in Log_event::apply_event
#8  0x00000000005b9782 in apply_event_and_update_pos
...

更新操作:

#0  row_update_for_mysql
#1  0x0000000000c1f466 in ha_innobase::delete_row
#2  0x000000000090b64a in handler::ha_delete_row
#3  0x0000000000a58a4b in Delete_rows_log_event::do_exec_row
#4  0x0000000000a50dcc in Rows_log_event::do_apply_event
#5  0x00000000005d0bb8 in Log_event::apply_event
#6  0x00000000005b9782 in apply_event_and_update_pos
...
  • 由堆栈可以看出,sql 线程首先将数据从磁盘加载到内存,然后调用引擎层的接口执行相应的操作,当iops 及 buffer pool 较小时,读磁盘需要较多的时间,容易造成主备延迟问题;
  • 当系统重启后,需要对系统进行预热,提高 buffer pool 的命中率,因此需要提供有效的方法来对系统进行预热;

综上,我们需要一种可以在 DML 操作之前将数据从磁盘加载到内存的功能,以实现数据库的快速操作。

解决方法

我们需要找到一种将数据加载到内存的方法,但又不对数据进行修改,需要满足以下的条件:

  • 在库上更新的数据应该在备库操作之前被加载到内存中;
  • 对于重启的mysqld实例,应该将启动之前所用的数据页加载到内存中;
  • 加载操作对数据本身不进行修改,类似于select 语句。

因此,我们可以在mysqld启动时启动额外的线程对 relay log 进行特殊处理,以达到数据加载的目的。

设计思路 & 使用方法

RDS MySQL 利用 relay log 来解决上述两个问题,当系统启动后,可以在后台开启一个独立于SQL thread之外的线程将 relay log 相关的数据从磁盘加载到内存中,从而使备库在查找数据的时候直接利用buffer pool,而不需要从磁盘中进行加载,同理,使用这种方法也可以解决系统预热的问题。

当启动后,如果发现延迟且 buffer pool 命中率较低时,可以启用 relay fetch thread, 具体语法为:

启动 relay_fetch_thread: start slave relay_fetch_thread;
停止 relay_fetch_thread: stop slave relay_fetch_thread;

relay fetch thread 读取relay log, 并将要执行的数据从磁盘上加载到内存中,所以只能对包含数据部分的 log_event 进行操作,对 Query_log_event,Write_rows_log_event 是无法进行预读的,前者是因为Query_log_event 只是SQL语句,不包含具体的数据信息;后者则是event中没有的数据,所以不需要进行加载,另外为了防止 buffer pool 中读取的 page 被 evict 出去,我们需要对两种情况进行分别处理:

  1. relay fetch thread 不能领先 sql thread 过多,如果领先过多的 relay log files,当 buffer pool 较小时,新加载进来的数据页会将老的数据页从内存中 evict 出去,对 sql thread 的命中率会有直接的影响;
  2. 当 sql thread 领先 relay fetch thread 时,此时 relay fetch thread 不需要将已执行完的 relay log 加载到内存,继续加载不仅会有命中率的问题,同时会造成 CPU 不必要的资源浪费。

因此,relay fetch thread 与 sql thread 应该相差的距离不太远,我们的策略是 relay fetch thread 与 sql thread 应该在同一个 relay log 上,具体策略如下:

  1. 如果 relay fetch thread 领先, 则当 relay fetch thread 读完一个文件后要等待 sql thread,直到 sql thread 应用完此relay log 再继续加载;
  2. 如果 sql thread 领先,则会通知 relay fetch thread 跳过当前执行的文件并用 sql thread 的位点来初始化自己将要执行的起点;

relay fetch thread 执行过程的伪码如下:

handle_slave_relay_fetch
{
   init_thd_and_rli();
   while (!relay_fetch_killed(eli))
   {
       ev= Log_event::read_log_event(&rli->relay_log_buf, 0, rli->relay_log.description_event_for_relay_fetch);
       if (ev == NULL)
       {
          deal with situations like hot_log, relay log purged, eof of relay log etc.
       }
       else
       {
             switch(ev->get_type_code())
             {
                case QUERY_EVENT:
                   deal with begin, commit
                   break;

                case XID_EVENT:
                   deal with xid(commit)
                   break;

               case TABLE_MAP_EVENT:
                   init table info for rows log event
                   break;

               case UPDATE_ROWS_EVENT:
               case DELETE_ROWS_EVENT:
                  find_row();
                  break;

               case FORMAT_DESCRIPTION_EVENT:
                  init description_event_for_relay_fetch for reading binlog event;
               default:
                  break;
             }
             delete ev;
       }
   }
}

实现过程中注意的细节

  • 由于 relay fetch thread 在加载数据的过程中会对记录进行加锁,所以在遇到begin, commit 的事件时,需要释放在读取过程中获取的所有锁资源,否则有可能会引起 sql 线程锁超时错误;
  • 由于 relay fetch thread 的位点是使用 sql thread 的位点进行初始化的,所以需要处理 relay log 不是完整事务的情况;
  • 释放 relay fetch thread 在执行过程中使用到的内存,否则会有内存问题;
  • 在 relay fetch thread 执行的过程中需要特别注意 log_lock、run_lock 等锁问题,以避免备库的死锁;
  • 需要对 relay log 的purge进行特殊处理;
  • 如果是系统预热的功能,则需要对 relay fetch thread 与 sql thread 的领先策略进行调整。
时间: 2024-12-24 08:58:56

数据库内核月报 - 2015 / 09-MySQL · 备库优化 · relay fetch 备库优化的相关文章

阿里数据库内核月报:2015年09月

# 01 MySQL · 引擎特性 · InnoDB Adaptive hash index介绍 # 02 PgSQL · 特性分析 · clog异步提交一致性.原子操作与fsync # 03 MySQL · 捉虫动态 · BUG 几例 # 04 PgSQL · 答疑解惑 · 诡异的函数返回值 # 05 MySQL · 捉虫动态 · 建表过程中crash造成重建表失败 # 06 PgSQL · 特性分析 · 谈谈checkpoint的调度 # 07 MySQL · 特性分析 · 5.6 并行复制

阿里数据库内核月报合辑

阿里数据库内核月报:2017年05月 阿里数据库内核月报:2017年04月 阿里数据库内核月报:2017年03月 阿里数据库内核月报:2017年02月 阿里数据库内核月报:2017年01月 阿里数据库内核月报:2016年12月 阿里数据库内核月报:2016年11月 阿里数据库内核月报:2016年10月 阿里数据库内核月报:2016年09月 阿里数据库内核月报:2016年08月 阿里数据库内核月报:2016年07月 阿里数据库内核月报:2016年06月 阿里数据库内核月报:2016年05月 阿里数

数据库内核月报 - 2015 / 10-PgSQL · 特性分析 · PG主备流复制机制

PostgreSQL在9.0之后引入了主备流复制机制,通过流复制,备库不断的从主库同步相应的数据,并在备库apply每个WAL record,这里的流复制每次传输单位是WAL日志的record.而PostgreSQL9.0之前提供的方法是主库写完一个WAL日志文件后,才把WAL日志文件传送到备库,这样的方式导致主备延迟特别大.同时PostgreSQL9.0之后提供了Hot Standby,备库在应用WAL record的同时也能够提供只读服务,大大提升了用户体验. 主备总体结构 PG主备流复制的

MySQL内核月报 2015.01-MySQL · 优化改进· 复制性能改进过程

前言 与oracle 不同,mysql 的主库与备库的同步是通过 binlog 实现的,而redo日志只做为mysql 实例的crash recovery使用.mysql在4.x 的时候放弃redo 的同步策略而引入 binlog的同步,一个重要原因是为了兼容其它非事务存储引擎,否则主备同步是没有办法进行的. redo 日志同步属于物理同步方法,简单直接,将修改的物理部分传送到备库执行,主备共用一致的 LSN,只要保证 LSN 相同即可,同一时刻,只能主库或备库一方接受写请求: binlog的同

数据库内核月报 - 2015 / 08-MySQL · 社区动态 · MySQL5.6.26 Release Note解读

最近上游发布了MySQL 5.6.26版本,从Release Note来看,MySQL 5.6版本已经相当成熟,fix的bug数越来越少了.本文主要分析releae note上fix的相关bug,去除performance scheama.mac及windows平台.企业版.package相关内容.从本期开始,我们会在新版本发布时,在当月的月报上为大家做详细的版本Release Note分析. InnoDB storage engine 问题描述 在类Unix平台上,当innodb_flush_

阿里数据库内核月报:2015年08月

# 01 MySQL · 社区动态 · InnoDB Page Compression # 02 PgSQL · 答疑解惑 · RDS中的PostgreSQL备库延迟原因分析 # 03 MySQL · 社区动态 · MySQL5.6.26 Release Note解读 # 04 PgSQL · 捉虫动态 · 执行大SQL语句提示无效的内存申请大小 # 05 MySQL · 社区动态 · MariaDB InnoDB表空间碎片整理 # 06 PgSQL · 答疑解惑 · 归档进程cp命令的core

阿里数据库内核月报:2015年11月

# 01 MySQL · 社区见闻 · OOW 2015 总结 MySQL 篇 # 02 MySQL · 特性分析 · Statement Digest # 03 PgSQL · 答疑解惑 · PostgreSQL 用户组权限管理 # 04 MySQL · 特性分析 · MDL 实现分析 # 05 PgSQL · 特性分析 · full page write 机制 # 06 MySQL · 捉虫动态 · MySQL 外键异常分析 # 07 MySQL · 答疑解惑 · MySQL 优化器 ran

阿里数据库内核月报:2015年06月

# 01 MySQL · 引擎特性 · InnoDB 崩溃恢复过程 # 02 MySQL · 捉虫动态 · 唯一键约束失效 # 03 MySQL · 捉虫动态 · ALTER IGNORE TABLE导致主备不一致 # 04 MySQL · 答疑解惑 · MySQL Sort 分页 # 05 MySQL · 答疑解惑 · binlog event 中的 error code # 06 PgSQL · 功能分析 · Listen/Notify 功能 # 07 MySQL · 捉虫动态 · 任性的 

阿里数据库内核月报:2015年04月

# 01 MySQL · 引擎特性 · InnoDB undo log 漫游 # 02 TokuDB · 产品新闻 · RDS TokuDB小手册 # 03 TokuDB · 特性分析 · 行锁(row-lock)与区间锁(range-lock) # 04 PgSQL · 社区动态 · 说一说PgSQL 9.4.1中的那些安全补丁 # 05 MySQL · 捉虫动态 · 连接断开导致XA事务丢失 # 06 MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题 #