读取操作:
更新操作:
- 由堆栈可以看出,sql 线程首先将数据从磁盘加载到内存,然后调用引擎层的接口执行相应的操作,当iops 及 buffer pool 较小时,读磁盘需要较多的时间,容易造成主备延迟问题;
- 当系统重启后,需要对系统进行预热,提高 buffer pool 的命中率,因此需要提供有效的方法来对系统进行预热;
我们需要找到一种将数据加载到内存的方法,但又不对数据进行修改,需要满足以下的条件:
- 对于重启的mysqld实例,应该将启动之前所用的数据页加载到内存中;
- 加载操作对数据本身不进行修改,类似于select 语句。
因此,我们可以在mysqld启动时启动额外的线程对 relay log 进行特殊处理,以达到数据加载的目的。
当启动后,如果发现延迟且 buffer pool 命中率较低时,可以启用 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 出去,我们需要对两种情况进行分别处理:
- relay fetch thread 不能领先 sql thread 过多,如果领先过多的 relay log files,当 buffer pool 较小时,新加载进来的数据页会将老的数据页从内存中 evict 出去,对 sql thread 的命中率会有直接的影响;
- 当 sql thread 领先 relay fetch thread 时,此时 relay fetch thread 不需要将已执行完的 relay log 加载到内存,继续加载不仅会有命中率的问题,同时会造成 CPU 不必要的资源浪费。
- 如果 relay fetch thread 领先, 则当 relay fetch thread 读完一个文件后要等待 sql thread,直到 sql thread 应用完此relay log 再继续加载;
relay fetch thread 执行过程的伪码如下:
- 由于 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进行特殊处理;