MySQL主從復制通過Binlog進行數(shù)據(jù)傳輸和措,主庫寫入數(shù)據(jù),生成Binlog蜕煌,通過dump線程將Binlog發(fā)送給從庫派阱,從庫IO線程接收Binlog并寫入自己的relay log,SQL線程應用relay log里的事務斜纪。本文結合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靡狞,主從復制隨著MySQL啟動而自動啟動。
2. SQL線程函數(shù)
MySQL主從復制SQL線程的線程函數(shù)位于sql/rpl_slave.cc文件中隔嫡,定義如下:
extern "C" void *handle_slave_sql(void *arg)
(1)創(chuàng)建工作線程
在MySQL 5.7 版本甸怕,開啟邏輯并行復制,SQL線程會創(chuàng)建多個工作線程并發(fā)進行relay log日志的應用腮恩,源碼中創(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),直到停止復制或者SQL線程被kill秸滴。
在循環(huán)中的主要函數(shù)調(diào)用關系如下:
- 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ù)去應用relay log event荡含。在這個函數(shù)里咒唆,會將thd的server_id設置成event的server_id,保證event被應用后释液,生成自己的binlog時全释,server_id仍然是原始值。
apply_event_and_update_pos函數(shù)中會調(diào)用函數(shù)sql_delay_event()误债,用于處理延遲復制浸船,比如使用了CHANGE MASTER TO MASTER_DELAY = X 這樣的語法。
ev->apply_event()函數(shù)是event自己成員函數(shù)寝蹈,這個函數(shù)里面會去判斷event是否可以并行應用李命,如果不可以,就在當前線程(SQL線程)去處理這個event箫老,如果可以并行應用封字,則會返回上一層函數(shù)apply_event_and_update_pos,將event丟進入一個隊列槽惫,后續(xù)worker線程會去這個隊列中取出event處理周叮。
3. 工作線程主要邏輯
工作線程的線程函數(shù)為:
extern "C" void *handle_slave_worker(void *arg)
主要調(diào)用關系如下:
- 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í)行分配給自己的任務仿耽。
在slave_worker_exec_job_group函數(shù)中,拿到event后各薇,調(diào)用下面這個函數(shù)應用event:
error= worker->slave_worker_exec_event(ev);
在slave_worker_exec_event函數(shù)中又會調(diào)用event自己的成員函數(shù)來應用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. 總結
本文簡單分析了MySQL主從復制SQL線程的主要處理邏輯棕叫,MySQL主從復制是邏輯復制,從上面過程來看奕删,SQL線程(包括工作線程)從relay log中拿到event俺泣,然后像執(zhí)行一個原始SQL一樣在從庫上重新執(zhí)行一次,相對于物理復制完残,這種方式效率并不高伏钠,耗費資源,并且容易產(chǎn)生復制延遲谨设。