文章https://segmentfault.com/a/11... 中介紹了存儲應該考慮的方向。本文詳細介紹其中的mysq,主要是INNODB。總體架構,啓動流程,一條語句的執行過程帶你快速深刻mysql源碼
。再從性能(緩存,數據結構
),功能(ACID實現,索引
)如何實現介紹了mysql中核心點。第二部分爲分佈式,介紹原生mysql的同步
過程。第三部分是proxy,由於proxy多數會自研,只介紹proxy
應該包含的功能。mysql
mysql爲單進程多線程,由於元數據用Innodb保存,啓動後除了mysql的處理鏈接請求/超時等還會啓動Innodb的全部線程。
主流程:linux
主函數在sql/amin.cc中 調用Mysqld.cc中mysqld_main 1. 首先載入日誌,信號註冊,plugin_register (mysql是插件式存儲引擎設計,innodb,myisam等都是插件,在這裏註冊),核心爲mysqld_socket_acceptor->connection_event_loop(); 2. 監聽處理循環poll。 process_new_connection處理handler有三種:線程池方式只用於商業,一個線程處理全部請求,一個鏈接一個線程(大多數選擇Per_thread_connection_handler)。 3. 若thread_cache中有空閒直接獲取,不然建立新的用戶線程。進入用戶線程的handle_connection 3.1 mysql網絡通訊一共有這幾層:`THD` | Protocol | NET | VIO | SOCKET,protocol對數據的協議格式化,NET封裝了net buf讀寫刷到網絡的操做,VIO是對全部鏈接類型網絡操做的一層封裝(TCP/IP, Socket, Name Pipe, SSL, SHARED MEMORY),handle_connection初始化THD(線程),開始do_command (關於THD,有個很好的圖:http://mysql.taobao.org/monthly/2016/07/04/) 3.2.do_command=>dispatch_comand=>mysql_parse=》【檢查query_cache有緩存直接返回不然=》】parse_sql=》mysql_execute_cmd判斷insert等調用mysql_insert,每條記錄調用write_record,這個是各個引擎的基類,根據操做表類型調用引擎層的函數=》寫binlog日誌=》提交/回滾。注意你們可能都覺得是有query_cache的。可是從8.0開啓廢棄了query_cache。第二正會講一下 4. 除了用戶線程和主線程,在啓動時,還建立了timer_notify線程。因爲爲了解決DDL沒法作到atomic等,從MySQL8.0開始取消了FRM文件及其餘server層的元數據文件(frm, par, trn, trg, isl,db.opt),全部的元數據都用InnoDB引擎進行存儲, 另一些諸如權限表之類的系統表也改用InnoDB引擎。所以在加載這些表時,建立了innodb用到的一系列線程。
總體流程圖以下:git
必須有這些步驟的緣由:
[1]爲了快,全部數據先寫入內存,再刷髒
[2]爲了防止數據頁寫過程當中崩潰數據的持久性=》先寫redo保證重啓後能夠恢復。日誌寫不成功不操做,日誌是順序寫,內容少,能夠同步等。(最好是物理重作)。
[3]異常回滾=》物理回滾反解複雜,須要一個邏輯日誌。
基於undo log又實現了MVCC
unlog等也要保證操做持久化原子化。
[4]爲了刪除不每次整理頁,只標記,爲了真正刪除/undo不須要的清除=》purge
[5]flush對一個pageid屢次操做合併在一塊兒減小隨機操做=》二級索引非惟一change buff
[6]Flush過程當中一個頁部分寫成功就崩潰,沒法正確後恢復=》二次寫
[7]爲完整的主鏈路。
[8]爲異步的刷盤鏈路github
詳細步驟:算法
binlog
】,調用引擎table->file->ha_write_row(table->record[0])開啓事務
的操做,trx_start_low。全部數據頁。都走這套。包括undo等sql
name | desc |
---|---|
buf_pool_t::page_hash | page_hash用於存儲已經或正在讀入內存的page。根據<space_id, page_no>快速查找。當不在page hash時,纔會去嘗試從文件讀取 |
buf_pool_t::LRU | LRU上維持了全部從磁盤讀入的數據頁,該LRU上又在鏈表尾部開始大約3/8處將鏈表劃分爲兩部分,新讀入的page被加入到這個位置;當咱們設置了innodb_old_blocks_time,若兩次訪問page的時間超過該閥值,則將其挪動到LRU頭部;這就避免了相似一次性的全表掃描操做致使buffer pool污染 |
buf_pool_t::free | 存儲了當前空閒可分配的block |
buf_pool_t::flush_list | 存儲了被修改過的page,根據oldest_modification(即載入內存後第一次修改該page時的Redo LSN)排序 |
buf_pool_t::flush_rbt | 在崩潰恢復階段在flush list上創建的紅黑數,用於將apply redo後的page快速的插入到flush list上,以保證其有序 |
buf_pool_t::unzip_LRU | 壓縮表上解壓後的page被存儲到unzip_LRU。 buf_block_t::frame存儲解壓後的數據,buf_block_t::page->zip.data指向原始壓縮數據。 |
buf_pool_t::zip_free[BUF_BUDDY_SIZES_MAX] | 用於管理壓縮頁產生的空閒碎片page。壓縮頁佔用的內存採用buddy allocator算法進行分配。 |
page_hash查找。
LRU只是用於淘汰。一份block。指針保存在hash和lru上(全部的數據頁)
flush_list 修改過的block被加到flush_list上,
unzip_LRU 解壓的數據頁被放到unzip_LRU鏈表上。數據庫
若是沒有則表示須要從磁盤讀取。在讀盤前首先咱們須要爲即將讀入內存的數據頁分配一個空閒的block。當free list上存在空閒的block時,能夠直接從free list上摘取;若是沒有,就須要從unzip_lru 或者 lru上驅逐page。先unzip lru。再lru是否有可替換page,直接釋放,不然多是髒頁多,再線程在LRU上作髒頁刷新。後臺線程也會按期作髒頁刷新。segmentfault
一個流程對buffer的操做步驟:數組
內存整體流程緩存
見上B+樹
對非惟一二級索引頁,delete_mark,delete,insert順序插入緩衝區,合併減小隨機IO。
寫入
入口函數:btr_cur_ins_lock_and_undo a) 從chached_list或分配一個空閒slot建立undo頁 b) 順序寫undo log header和記錄 c) 在事務提交階段,加入到history list或釋放【見事務提交】 Undo log的寫入在一個單獨的mtr中,受redo log的保護,先講一個子事務mtr。Mtr是InnoDB對物理數據文件 操做的最小原子單元,保證持久性,用於管理對Page加鎖、修改、釋放、以及日誌提交到公共buffer等工做。 開啓時初始化m_impl,好比mlog用於存儲redo log記錄 提交時須要將本地產生的日誌拷貝到公共緩衝區,將修改的髒頁放到flush list上。
事務提交時undolog
1.入口函數:trx_commit_low-->trx_write_serialisation_history
2.事務提交總體流程(寫完redo就能夠提交了。)
生成事務no。若是有update類的undo日誌 。加入到purge_queue(清理垃圾),history鏈表(維護歷史版本)
子事務提交。Redo log寫到公共緩存
釋放MVCC的readview;insert的undo日誌釋放(可cache重用,不然所有釋放包括page頁)
刷日誌
3.在該函數中,須要將該事務包含的Undo都設置爲完成狀態,先設置insert undo,再設置update undo(trx_undo_set_state_at_finish),完成狀態包含三種:
若是當前的undo log只佔一個page,且佔用的header page大小使用不足其3/4時(TRX_UNDO_PAGE_REUSE_LIMIT),則狀態設置爲TRX_UNDO_CACHED,該undo對象會隨後加入到undo cache list上; 若是是Insert_undo(undo類型爲TRX_UNDO_INSERT),則狀態設置爲TRX_UNDO_TO_FREE; 若是不知足a和b,則代表該undo可能須要Purge線程去執行清理操做,狀態設置爲TRX_UNDO_TO_PURGE。
對於undate undo須要調用trx_undo_update_cleanup進行清理操做。
注意上面只清理了update_undo,insert_undo直到事務釋放記錄鎖、從讀寫事務鏈表清除、以及關閉read view後才進行,
這裏的slot,undo page ,history關係:
每一個rseg控制頁有1024個slot和history。undo page釋放後或者移到history list後,就能夠把slot清空、undo page轉爲cache不釋放則不動slot
1)首先依據當前的系統負載來肯定須要使用的Purge線程數(srv_do_purge),即若是壓力小,只用一個Purge Cooridinator線程就能夠了。若是壓力大,就多喚醒幾個線程一塊兒作清理記錄的操做。若是全局歷史鏈表在增長,或者全局歷史鏈表已經超過innodb_max_purge_lag,則認爲壓力大,須要增長處理的線程數。若是數據庫處於不活躍狀態(srv_check_activity),則減小處理的線程數。
2)若是歷史鏈表很長,超過innodb_max_purge_lag,則須要從新計算delay時間(不超過innodb_max_purge_lag_delay)。若是計算結果大於0,則在後續的DML中須要先sleep,保證不會太快產生undo(row_mysql_delay_if_needed)。
3)從全局視圖鏈表中,克隆最老的readview(快照、拿視圖爲了拿事務id.undo日誌中upadte記了事務id),全部在這個readview開啓以前提交的事務所產生的undo都被認爲是能夠清理的。克隆以後,還須要把最老視圖的建立者的id加入到view->descriptors中,由於這個事務修改產生的undo,暫時還不能刪除(read_view_purge_open)。
4)從undo segment的最小堆中(堆存放每一個段未被purge的最老的undo頁),找出最先提交事務的undolog(trx_purge_get_rseg_with_min_trx_id),若是undolog標記過delete_mark(表示有記錄刪除操做),則把先關undopage信息暫存在purge_sys_t中(trx_purge_read_undo_rec)。
5)依據purge_sys_t中的信息,讀取出相應的undo,同時把相關信息加入到任務隊列中。同時更新掃描過的指針,方便後續truncate undolog。
6)循環第4步和第5步,直到爲空,或者接下到view->low_limit_no,即最老視圖建立時已經提交的事務,或者已經解析的page數量超過innodb_purge_batch_size。(把delete和Undopage分別存放,detele給工做線程刪除)
7)把全部的任務都放入隊列後,就能夠通知全部Purge Worker線程(若是有的話)去執行記錄刪除操做了。刪除記錄的核心邏輯在函數row_purge_record_func中。有兩種狀況,一種是數據記錄被刪除了,那麼須要刪除全部的彙集索引和二級索引(row_purge_del_mark),另一種是二級索引被更新了(老是先刪除+插入新記錄),因此須要去執行清理操做。
8)在全部提交的任務都已經被執行完後,就能夠調用函數trx_purge_truncate去刪除update undo(insert undo在事務提交後就被清理了)。每一個undo segment分別清理,從本身的histrory list中取出最先的一個undo,進行truncate(trx_purge_truncate_rseg_history)。truncate中,最終會調用fseg_free_page來清理磁盤上的空間。
undo+read view 寫時併發讀
ReadView::id 建立該視圖的事務ID;
m_ids 建立ReadView時,活躍的讀寫事務ID數組,有序存儲;記錄trx_id不在m_ids中可見 m_low_limit_id 當前最大事務ID;記錄rx_id>=ReadView::m_low_limit_id,則說明該事務是建立readview以後開啓的,不可見 Rem_up_limit_id ;m_ids 集合中的最小值;記錄trx_id< m_up_limit_id該事務在建立ReadView時已經提交了,可見
二級索引回聚簇索引中。
若不可見,則經過undo構建老版本記錄。
提交 子事務提交寫入緩衝區
提交時,準備log內容,提交到公共buffer中,並將對應的髒頁加到flush list上 Step 1: mtr_t::Command::prepare_write() 1.若當前mtr的模式爲MTR_LOG_NO_REDO 或者MTR_LOG_NONE,則獲取log_sys->mutex,從函數返回 2.若當前要寫入的redo log記錄的大小超過log buffer的二分之一,則去擴大log buffer,大小約爲原來的兩倍。 3.持有log_sys->mutex 4.調用函數log_margin_checkpoint_age檢查本次寫入:若是本次產生的redo log size的兩倍超過redo log文件capacity,則打印一條錯誤信息;若本次寫入可能覆蓋檢查點,還須要去強制作一次同步*chekpoint* 5.檢查本次修改的表空間是不是上次checkpoint後第一次修改(fil_names_write_if_was_clean) 若是space->max_lsn = 0,表示自上次checkpoint後第一次修改該表空間: a. 修改space->max_lsn爲當前log_sys->lsn; b. 調用fil_names_dirty_and_write將該tablespace加入到fil_system->named_spaces鏈表上; c. 調用fil_names_write寫入一條類型爲MLOG_FILE_NAME的日誌,寫入類型、spaceid, page no(0)、文件路徑長度、以及文件路徑名(將本次的表空間和文件信息加入到一個內存鏈表上 (去除恢復中對數據字典的依賴))。 在mtr日誌末尾追加一個字節的MLOG_MULTI_REC_END類型的標記,表示這是多個日誌類型的mtr。 若是不是從上一次checkpoint後第一次修改該表,則根據mtr中log的個數,或標識日誌頭最高位爲MLOG_SINGLE_REC_FLAG,或附加一個1字節的MLOG_MULTI_REC_END日誌。 Step 2: 拷貝 若日誌不夠,log_wait_for_space_after_reserving Step 3:若是本次修改產生了髒頁,獲取log_sys->log_flush_order_mutex,隨後釋放log_sys->mutex。 Step 4. 將當前Mtr修改的髒頁加入到flush list上,髒頁上記錄的lsn爲當前mtr寫入的結束點lsn。基於上述加鎖邏輯,可以保證flush list上的髒頁老是以LSN排序。 Step 5. 釋放log_sys->log_flush_order_mutex鎖 Step 6. 釋放當前mtr持有的鎖(主要是page latch)及分配的內存,mtr完成提交。
當設置該值爲1時,每次事務提交都要作一次fsync,這是最安全的配置,即便宕機也不會丟失事務
當設置爲2時,則在事務提交時只作write操做,只保證寫到系統的page cache,所以實例crash不會丟失事務,但宕機則可能丟失事務
當設置爲0時,事務提交不會觸發redo寫操做,而是留給後臺線程每秒一次的刷盤操做,所以實例crash將最多丟失1秒鐘內的事務,寫入一條MLOG_FILE_NAME
刷髒。刷髒後調用log checkpoint把點寫入(刷髒就是內存到磁盤和redo不要緊,redo寫多了須要清除checkpoint寫入刷髒點,以前的能夠不要了),之後崩潰恢復今後點開始
1.刷髒會在如下情形被觸發
啓動和關閉時會喚醒刷髒線程 每10s後、按如下比對落後點決定是否要刷髒。 redo log可能覆蓋寫時,調用單獨線程把未提交LSN對應的點放入log的checkpoint點,只是redolog寫checkpoint點。如下參數控制checkpoint和flush刷髒點 log_sys->log_group_capacity = 15461874893 (90%) log_sys->max_modified_age_async = 12175607164 (71%) log_sys->max_modified_age_sync = 13045293390 (76%) log_sys->max_checkpoint_age_async = 13480136503 (78%) log_sys->max_checkpoint_age = 13914979615 (81%) LRU LIST在未能本身釋放時,先本身刷髒一頁,不行再 喚醒刷髒線程
2.刷髒線程
innodb_page_cleaners設置爲4,那麼就是一個協調線程(自己也是工做線程),加3個工做線程,工做方式爲生產者-消費者。工做隊列長度爲buffer pool instance的個數,使用一個全局slot數組表示。
1)buf_flush_page_cleaner_coordinator協調線程
主循環主線程以最多1s的間隔或者收到buf_flush_event事件就會觸發進行一輪的刷髒。 協調線程首先會調用pc_request()函數,這個函數的做用就是爲每一個slot表明的緩衝池實例計算要刷髒多少頁, 而後把每一個slot的state設置PAGE_CLEANER_STATE_REQUESTED, 喚醒等待的工做線程。 因爲協調線程也會和工做線程同樣作具體的刷髒操做,因此它在喚醒工做線程以後,會調用pc_flush_slot(),和其它的工做線程並行去作刷髒頁操做。 一但它作完本身的刷髒操做,就會調用pc_wait_finished()等待全部的工做線程完成刷髒操做。 完成這一輪的刷髒以後,協調線程會收集一些統計信息,好比這輪刷髒所用的時間,以及對LRU和flush_list隊列刷髒的頁數等。 而後會根據當前的負載計算應該sleep的時間、以及下次刷髒的頁數,爲下一輪的刷髒作準備。
2)buf_flush_page_cleaner_worker工做線程
主循環啓動後就等在page_cleaner_t的is_requested事件上, 一旦協調線程經過is_requested喚醒全部等待的工做線程, 工做線程就調用pc_flush_slot()函數去完成刷髒動做。 pc_flush_slot: 先找到一個空間的slot, page_cleaner->n_slots_requested--; // 代表這個slot開始被處理,將未被處理的slot數減1 page_cleaner->n_slots_flushing++; //這個slot開始刷髒,將flushing加1 slot->state = PAGE_CLEANER_STATE_FLUSHING; 刷LRU,FLUSH LIST page_cleaner->n_slots_flushing--; // 刷髒工做線程完成次輪刷髒後,將flushing減1 p age_cleaner->n_slots_finished++; //刷髒工做線程完成次輪刷髒後,將完成的slot加一 slot->state = PAGE_CLEANER_STATE_FINISHED; // 設置此slot的狀態爲FINISHED 如果最後一個,os_event_set(page_cleaner->is_finished) pc_wait_finished: os_event_wait(page_cleaner->is_finished); 統計等 每次刷多少srv_max_buf_pool_modified_pct決定
3.log_checkpoint
入口函數爲log_checkpoint,其執行流程以下: Step1. 持有log_sys->mutex鎖,並獲取buffer pool的flush list鏈表尾的block上的lsn,這個lsn是buffer pool中未寫入數據文件的最老lsn,在該lsn以前的數據都保證已經寫入了磁盤。checkpoint 點, 在crash recover重啓時,會讀取記錄在checkpoint中的lsn信息,而後從該lsn開始掃描redo日誌。 Step 2. 調用函數fil_names_clear 掃描fil_system->named_spaces上的fil_space_t對象,若是表空間fil_space_t->max_lsn小於當前準備作checkpoint的Lsn,則從鏈表上移除並將max_lsn重置爲0。同時爲每一個被修改的表空間構建MLOG_FILE_NAME類型的redo記錄。(這一步將來可能會移除,只要跟蹤第一次修改該表空間的min_lsn,而且min_lsn大於當前checkpoint的lsn,就能夠忽略調用fil_names_write) 寫入一個MLOG_CHECKPOINT類型的CHECKPOINT REDO記錄,並記入當前的checkpoint LSN Step3 . fsync 被修改的redo log文件 更新相關變量: log_sys->next_checkpoint_no++ log_sys->last_checkpoint_lsn = log_sys->next_checkpoint_lsn Step4. 寫入checkpoint信息 函數:log_write_checkpoint_info --> log_group_checkpoint checkpoint信息被寫入到了第一個iblogfile的頭部,但寫入的文件偏移位置比較有意思,當log_sys->next_checkpoint_no爲奇數時,寫入到LOG_CHECKPOINT_2(3 *512字節)位置,爲偶數時,寫入到LOG_CHECKPOINT_1(512字節)位置。
崩潰恢復
1.從第一個iblogfile的頭部定位要掃描的LSN(數據落盤點)
2.掃描redo log
1) 第一次redo log的掃描,主要是查找MLOG_CHECKPOINT
,不進行redo log的解析,
2) 第二次掃描是在第一次找到MLOG_CHECKPOINT(獲取表和路徑)基礎之上進行的,該次掃描會把redo log解析到哈希表中,若是掃描完整個文件,哈希表尚未被填滿,則不須要第三次掃描,直接進行recovery就結束
3)第二次掃描把哈希表填滿後,還有redo log剩餘,則須要循環進行掃描,哈希表滿後當即進行recovery,直到全部的redo log被apply完爲止。
3.具體redo log的恢復
MLOG_UNDO_HDR_CREATE:解析事務ID,爲其重建undo log頭; MLOG_REC_INSERT 解析出索引信息(mlog_parse_index)和記錄信息( page_cur_parse_insert_rec)等 在完成修復page後,須要將髒頁加入到buffer pool的flush list上;查找紅黑樹找到合適的插入位置 MLOG_FILE_NAME用於記錄在checkpoint以後,全部被修改過的信息(space, filepath); MLOG_CHECKPOINT用於標誌MLOG_FILE_NAME的結束。 在恢復過程當中,只須要打開這些ibd文件便可,固然因爲space和filepath的對應關係經過redo存了下來,恢復的時候也再也不依賴數據字典。 在恢復數據頁的過程當中不產生新的redo 日誌;
Redo爲了保證原子性,要求一塊一寫
。不夠的話要先讀舊的而後改而後寫。以512字節(最小扇區)對其方式寫入,不須要二次寫。設置一個值innodb_log_write_ahead_size,不須要這個過程,超過該值補0到一塊直接插入
[ps 數據須要二次寫,由於可能誇多扇區,leveldb的log增長頭直接跳過壞頁,redo log固定大小,正常日誌都是寫成功纔會被回放,寫內存與寫壞後丟只能丟失,解析錯誤跳過到下一塊吧,問題就是要有個大小找到下一個位置]
提交 入口: MYSQL_BIN_LOG::commit,若是是分佈式事務,用xa,兩階段。prepare和commit。咱們先研究普通的提交。XA不做爲重點。可是因爲server層和Innodb層兩個日誌,須要保證順序,也按照XA的兩階段設計。也叫內部xa
1) xa兩階段
Prepare
undo log寫入xid,設置狀態爲PREPARED
Commit
Flush Stage:由leader依次爲別的線程對flush redo log到LSN,再寫binlog文件 Sync Stage:若是sync_binlog計數超過配置值,以組爲維度文件fsync Commit Stage:隊列中的事務依次進行innodb commit,修改undo頭的狀態爲完成;並釋放事務鎖,清理讀寫事務鏈表、readview等一系列操做,落盤redo。
2) 緣由
兩階段是爲了保證binlog和redo log一致性。server和備庫用binlog來恢復同步。innodb用undo和redo恢復。
1落undo 2flush redo 3 flush binlog 4fsync binlog 5fsync redo [ps:sync可能只是內核緩衝放入磁盤隊列,fsync只保證放入磁盤,都是同步] 6 undo D
保證binlog若成功了,根據Undo的p結果不會回滾出現主從不一致的狀況
3) 組提交:兩階段提交,在併發時沒法保證順序一致,用ordered_commit控制
一個On-line的backup程序新建一個slave來作replication,那麼事務T1在slave機器restore MySQL數據庫的時候發現未在存儲引擎內提交,T1事務被roll back,此時主備數據不一致(搭建Slave時,change master to的日誌偏移量記錄T3在事務位置以後)。
若是關閉binlog_order_commits。事務各自提交。這時候沒有設置不寫redo log。不能保證Innodb commit順序和Binlog寫入順序一直,可是不會影響數據一致性。只是物理備份數據不一致。可是依賴於事務頁上記錄binlog恢復的,好比xtrabackup就會發生備份數據不一致的狀況。
每一個stage階段都有各自的隊列,使每一個session的事務進行排隊。,leader控制,當一組事務在進行Commit階段時,其餘新的事務能夠進行Flush階段
2.基於語句的複製 statement
優勢:日誌量少
缺點:特定功能函數致使主從數據不一致,重複執行時沒法保證冪等
3.混合類型的複製 mixed (默認語句,語句沒法精準複製,則基於行)
其中1. Slave 上面的IO線程鏈接上 Master,並請求從指定日誌文件的指定位置(或者從最開始的日誌)以後的日誌內容;
重放過程和master同樣,也redolog
GTID
MySQL 5.6引入全局事務ID的首要目的,是保證Slave在並行複製(並行順序會亂)的時候不會重複執行相同的事務操做;用全局事務IDs代替由文件名和物理偏移量組成的複製位點(每一個日誌包含GID_Sets,xx:1-100形式)。
GTID的組成部分:
前面是server_uuid:後面是一個串行號 例如:server_uuid:sequence number 7800a22c-95ae-11e4-983d-080027de205a:10 UUID:每一個mysql實例的惟一ID,因爲會傳遞到slave,因此也能夠理解爲源ID。 Sequence number:在每臺MySQL服務器上都是從1開始自增加的串行,一個數值對應一個事務。
當事務提交時,無論是STATEMENT仍是ROW格式的binlog,都會添加一個XID_EVENT事件做爲事務的結束。該事件記錄了該事務的id(這個是存儲引擎裏的事務id,崩潰恢復時決是否提交存儲引擎中狀態爲prepared的事務)。
同步方案
1.同步複製 所謂的同步複製,意思是master的變化,必須等待slave-1,slave-2,...,slave-n完成後才能返回。
2.異步複製 master只須要完成本身的數據庫操做便可。至於slaves是否收到二進制日誌,是否完成操做,不用關心
3.半同步複製 master只保證slaves中的一個操做成功,就返回,其餘slave無論。
這裏有個不一致的問題。 開始提交事務 =>write binlog => sync binlog => engine commit => send events =>返回 commit後崩潰,send_events失敗,會致使master有slave沒有,須要靠binlog同步補一下。 開始提交事務 =>write binlog => sync binlog => send events => engine commit =>返回 send_events失敗,若sync binlog未落盤,致使XA不會重作,slave領先,若binlog落盤則沒有問題,可接受和單機redo同樣。
master既要負責寫操做,還的維護N個線程,負擔會很重。能夠這樣,slave-1是master的從,slave-1又是slave-2,slave-3,...的主,同時slave-1再也不負責select。slave-1將master的複製線程的負擔,轉移到本身的身上。這就是所謂的多級複製的概念。
按期checkout-point將隊列中執行結束的刪除。記錄checkpoint後每一個worker是否執行過的bitmap。崩潰恢復時執行Bitmap未執行的部分。按db分粒度大能夠換成table
當主庫支撐不了。水平擴展。拆表。
須要proxy保證
同步策略影響。
XA分爲外部和內部。對於外部。要應用程序或proxy做爲協調者。(二階段提交協調者判斷全部prepare後commit)。對於內部,binlog控制。
合併
緩存
路由
故障
發現與定位XA
一致性的實現過濾
加註釋