MySQL主從復(fù)制通過Binlog進行數(shù)據(jù)傳輸,主庫寫入數(shù)據(jù),生成Binlog,通過dump線程將Binlog發(fā)送給從庫,從庫IO線程接收Binlog并寫入自己的relay log,SQL線程應(yīng)用relay log里的事務(wù)。本文結(jié)合MySQL源碼,分析SQL線程的主要處理過程。
MySQL源碼版本:5.7.19
原文地址:
https://mytecdb.com/blogDetail.php?id=90
1. SQL線程啟動
MySQL從庫在執(zhí)行start slave命令時,在MySQL內(nèi)部依次調(diào)用下面的函數(shù)來啟動IO線程和SQL線程。
- mysql_execute_command(),sql/sql_parse.cc
- start_slave_cmd(),sql/rpl_slave.cc
- start_slave(),sql/rpl_slave.cc
- start_slave_threads(),sql/rpl_slave.cc
- start_slave_thread(),sql/rpl_slave.cc
其中SQL線程在 start_slave_thread() 函數(shù)中被創(chuàng)建。
start_slave_threads 函數(shù)在另外一個地方也會被調(diào)用,init_slave()函數(shù),這個函數(shù)是在MySQL啟動時調(diào)用,如果沒有指定skip-slave-start,主從復(fù)制隨著MySQL啟動而自動啟動。
2. SQL線程函數(shù)
MySQL主從復(fù)制SQL線程的線程函數(shù)位于sql/rpl_slave.cc文件中,定義如下:
extern "C" void *handle_slave_sql(void *arg)
(1)創(chuàng)建工作線程
在MySQL 5.7 版本,開啟邏輯并行復(fù)制,SQL線程會創(chuàng)建多個工作線程并發(fā)進行relay log日志的應(yīng)用,源碼中創(chuàng)建工作線程的調(diào)用棧如下:
- handle_slave_sql()
- slave_start_workers()
- slave_start_single_worker()
- mysql_thread_create()
(2)進入循環(huán)
SQL線程創(chuàng)建完工作線程之后,會進入while循環(huán),直到停止復(fù)制或者SQL線程被kill。
在循環(huán)中的主要函數(shù)調(diào)用關(guān)系如下:
- handle_slave_sql(),SQL線程主函數(shù)
- exec_relay_log_event()
- apply_event_and_update_pos()
- ev->apply_event(rli);
- ev->do_apply_event()
exec_relay_log_event 函數(shù)讀取relay log中的event。
apply_event_and_update_pos函數(shù)去應(yīng)用relay log event。在這個函數(shù)里,會將thd的server_id設(shè)置成event的server_id,保證event被應(yīng)用后,生成自己的binlog時,server_id仍然是原始值。
apply_event_and_update_pos函數(shù)中會調(diào)用函數(shù)sql_delay_event(),用于處理延遲復(fù)制,比如使用了CHANGE MASTER TO MASTER_DELAY = X 這樣的語法。
ev->apply_event()函數(shù)是event自己成員函數(shù),這個函數(shù)里面會去判斷event是否可以并行應(yīng)用,如果不可以,就在當前線程(SQL線程)去處理這個event,如果可以并行應(yīng)用,則會返回上一層函數(shù)apply_event_and_update_pos,將event丟進入一個隊列,后續(xù)worker線程會去這個隊列中取出event處理。
3. 工作線程主要邏輯
工作線程的線程函數(shù)為:
extern "C" void *handle_slave_worker(void *arg)
主要調(diào)用關(guān)系如下:
- handle_slave_worker(),工作線程主函數(shù)
- slave_worker_exec_job_group()
- slave_worker_exec_event()
- ev->do_apply_event_worker(this);
- ev->do_apply_event()
- mysql_parse()
工作線程內(nèi)部主要是一個while循環(huán),調(diào)用slave_worker_exec_job_group()函數(shù)執(zhí)行分配給自己的任務(wù)。
在slave_worker_exec_job_group函數(shù)中,拿到event后,調(diào)用下面這個函數(shù)應(yīng)用event:
error= worker->slave_worker_exec_event(ev);
在slave_worker_exec_event函數(shù)中又會調(diào)用event自己的成員函數(shù)來應(yīng)用event:
ret= ev->do_apply_event_worker(this);
在do_apply_event_worker函數(shù)中,調(diào)用不同類型event的do_apply_event()成員函數(shù)。比如Query_log_event這種類型的event。
Query_log_event::do_apply_event()
do_apply_event函數(shù)中最終調(diào)用mysql_parse函數(shù)執(zhí)行SQL語句。
4. 總結(jié)
本文簡單分析了MySQL主從復(fù)制SQL線程的主要處理邏輯,MySQL主從復(fù)制是邏輯復(fù)制,從上面過程來看,SQL線程(包括工作線程)從relay log中拿到event,然后像執(zhí)行一個原始SQL一樣在從庫上重新執(zhí)行一次,相對于物理復(fù)制,這種方式效率并不高,耗費資源,并且容易產(chǎn)生復(fù)制延遲。