天天看點

《高性能MYSQL》

MYSQL的架構和曆史

1.1 MYSQL邏輯架構

《高性能MYSQL》

第一層:連接配接處理,授權認證,安全 等等

第二層:查詢解析、分析、優化、緩存 以及 所有的内置函數。包含跨存儲引擎的功能:存儲過程、觸發器、視圖等。

第三層:存儲引擎。負責MySQL中資料的存儲和提取。

1.2并發控制

無論何時,隻要有多個查詢需要在同一時刻修改資料,都會産生并發控制的問題。

讨論mysql兩個層面的并發控制:存儲引擎層 與 伺服器層

讀寫鎖

在處理并發寫或并發讀寫時,可以通過由兩種類型的鎖組成的鎖系統來解決問題,這兩種鎖就是 共享鎖(讀鎖) 和 排它鎖(寫鎖)。讀鎖是共享的,互相不阻塞,寫鎖是排它的,隻有一個線程能進行寫操作,其他讀鎖和寫鎖都是阻塞的。

并且,寫鎖擁有更高的優先級。在一個鎖隊列中,寫鎖可以插到讀鎖的前面。

** 鎖粒度**

一種理想的鎖方式是,盡量隻鎖定需要修改的資源,而不是所有資源。鎖定的資料量越小,并發程度越高。

但是鎖也是需要時空開銷的,判斷是否有鎖、加鎖、釋放鎖的操作都需要額外的開銷,如果鎖粒度太小,雖然并發程度高,但系統花大量資源去管理鎖,而不是存取資料,也是得不償失。

鎖政策

就是在 鎖的開銷 和 資料安全性 之間尋求平衡。大多資料庫都是行級鎖 ,而mysql提供了更多鎖的可能性。每種存儲引擎都可以實作自己的鎖政策和鎖粒度。将鎖粒度固定在某一級别,可以為特定的應用場景提供更好的性能,但同時也會失去對一些應用場景的支援。但好在mysql支援多個存儲引擎。

表鎖

表鎖是mysql中最基本、開銷最小的鎖政策。盡管存儲引擎可以設計管理自己的鎖,但mysql伺服器還是利用表鎖來實作不同的目的。例如:會為alter table之類的語句使用表鎖,而忽略存儲引擎的鎖機制。

行級鎖

行級鎖是mysql中支援并發量最大、開銷最大的鎖政策。行級鎖隻在存儲引擎層實作,伺服器層沒有實作。并且伺服器層完全不了解存儲引擎層的鎖實作。

1.3 事務

事務是一組原子性的操作,是一個獨立執行單元。事務内的語句,要麼全部執行成功,要麼全部執行失敗。

事務的四個标準特征(ACID)

  • 原子性(atomicity):一個事務必須被視為不可分割的最小工作單元,整個事務的所有操作要麼全部送出成功,要麼全部失敗復原。
  • 一緻性(consistency):資料庫總是保持一緻性的狀态
  • 隔離性(isolation):通常來說,一個事務在沒有最終送出以前,對其它事務是不可見的。
  • 持久性(durability):一旦事務送出,則其所做的唏噓該就會永久儲存到資料庫中。

隔離級别

  • 未送出讀(READ UNCOMMITTED):事務中的修改,即使沒有送出,對其他事務也都是可見的。髒讀問題(事務1改,事務2讀,事務1復原)。
  • 送出讀(READ COMMITTED):大多數資料庫的預設隔離級别是送出讀(MySQL不是)。事務從開始直到送出之前,所作的修改對其他事務都是不可見的。也叫不可重複讀。不可重複讀問題(事務1讀,事務2改,事務1讀)。
  • 可重複讀(REPEATABLE READ):保證在同一個事務中多次讀取同樣記錄的結果是一緻的。可重複讀是MySQL的預設事務隔離級别。幻讀(事務1範圍操作,事務2插入資料,事務1讀,産生幻行)
  • 可串行化(SERIALIZABLE):非常需要資料的一緻性和接受沒有并發的情況性,才考慮該級别。

死鎖

當兩個或多個事物占用着自己的資源,而都在等待對方占用的資源時,會形成死鎖。

start transaction
update a
update b
commit

start transaction
update b
update a
commit

           

一種是在事務開始前檢測死鎖的循環依賴,若有可能導緻死鎖,則報錯。

一種是發生死鎖時,設定最長等待時間,大于這個時間則放棄資源(不推薦,性能變差)。

一種是發生死鎖時,将擁有最少行級寫鎖的事務復原。

1.4 多版本并發控制

以InnoDB的MVCC為例,InnoDB以 在每行記錄後面儲存兩個隐藏的列 來實作MVCC。這兩個列,一列儲存 這一行的建立時間,一列儲存 這一行的過期時間。但存儲的并不是真正的時間,而是 系統版本号。每開始一個事務,系統版本号就會自增。事務通過自身的系統版本号與這兩個隐藏的列對比,來操作資料。

在可重複度的隔離級别下,InnoDB的MVCC的具體操作如下:

  • select InnoDB查找 建立系統版本号 <= 事務系統版本号的行,即在事務開始前就存在的行,或者由事務自身插入或修改的行。同時該行的過期系統版本号要麼未定義,要麼 > 事務系統版本号。
  • insert 新插入的每一行 建立時間都是目前事務的系統版本号。
  • delete 删除的每一行 過期時間都是目前事務的系統版本号。
  • update 插入一條新資料,建立時間是目前事務的系統版本号,将原來行的過期時間置為目前事務的系統版本号。

MVCC隻在 送出讀 和 可重複讀 兩個隔離級别下工作,因為未送出讀總是能讀取到最新的行,而不是符合目前事務版本的行,而串行化會對所有行加鎖。

MyISAM和InnoDB的差別

事務: MyISAM不支援事務,InnoDB支援事務。

鎖粒度: MyISAM隻支援表鎖。InnoDB支援行鎖。

存儲: MyISAM存三個檔案(.frm .MYD .MYI),支援動态,靜态和壓縮三種存儲格式。InnoDB存一個檔案(.frm)。

外鍵: MyISAM不支援外鍵,InnoDB支援外鍵。

索引: MyISAM是索引+行指針,InnoDB是聚簇+非聚簇(這個點決定了其主鍵的必要性)。

安全: MyISAM不支援崩潰安全回複,InnoDB支援(redo_log)。

行數統計: MyISAM中維護一個計數器記錄總行數,select(*)時很快,而InnoDB需要全表掃描,是以較慢。

Schema與資料類型優化

4.1選擇優化的資料類型

  • 更小的通常更好。

    ​ 應該盡量使用可以正确存儲資料的最小類型,更小的資料類型通常更快,因為他們占用更少的磁盤,記憶體和CPU緩存,并且處理時需要的CPU周期更少。

  • 簡單就好

    ​ 更簡單的資料類型的操作通常需要更少的CPU周期。例如,整型數字比字元操作代價更低,因為字元集和校對規則(排序規則)使字元比較相對整型數字比較更複雜。比如,應使用INTERGER存儲IP位址(inet_aton)

  • 盡量避免NULL

    ​ 通常情況下,最好指定列為NOT NULL。如果查詢中包含可為NULL的列,對MySQL來說更難優化,因為可為NULL的列使得索引,索引統計和值比較非常複雜,可為NULL的列會使用更多的存儲空間,當可謂NULL的列被索引時,每個索引記錄需要一個額外的位元組。但是把可為NULL的列改成NOT NULL帶來的性能提升比較小,但如果計劃在列上建立索引,就應該避免設計成可為NULL的列。

4.1.1整數類型

整數類型 占用空間 範圍
TINYINT 8 [-2^7,2^7-1]
SMALLINT 16 [-2^15,2^15-1]
MEDIUMINT 24 [-2^23,2^23-1]
INT 32 [-2^31,2^31-1]
BIGINT 64 [-2^63,2^63-1]
INT(11)隻是指定顯示字元的範圍。不會限制值得範圍。

4.1.1.2實數類型

實數是帶有小數部分的數字,可以使用DECIMAL存儲比BIGINT還大的整數。

DECIMAL類型用于存儲精确的小數,支援精确計算。

4.1.3字元串類型

varchar類型用于存儲可變長字元串,比定長更節省空間。

char 定長字元串,MySQL在存儲時會去除char尾部的空格。

blob

​ 采用二進制的方式存儲,沒有排序規則和字元集。包含tinyblob,blob,mediumblob,longblob

text

​ 采用字元串的方式存儲,有排序規則和字元集,包含tinytext,text,mediumtext,longtext。

4.1.4日期和時間類型

DATETIME和TIMESTAMP

​ 現在推薦使用DATETIME,範圍更大,與時區無關,占用8個位元組

datetime:大範圍的值 1001 9999 s YYYYMMDDHHMMSS 與時區無關 8位元組。

timestamp:1970 2038,1970 1 1以來的秒數,時區 4位元組 。

4.2MySQL 模式設計的陷阱

太多的列

太多的關聯

NULL值

4.3範式和反範式

​ 在範式化的資料庫中,每個事實資料隻會出現一次,

​ 反範式化的資料庫中,資訊是備援的,可能會存儲在多個地方。

範式優點:

​ 範式化的更新操作更快,隻需要更改較少的資料。

​ 範式化的表更小,可以更好的放在記憶體裡,執行操作會更快。

​ 沒有多餘的資料,可以減少distinct或GROUP BY的操作。

範式缺點:

​ 通常需要關聯,關聯代價昂貴,也可能使一些索引政策無效。

反範式優點:

​ 所有的資料都在一張表中,可以避免關聯。

​ 不關聯的時候即使全表掃描,也是順序IO。

反範式缺點:

​ 備援的多餘資料,更新更慢

​ 表大,放到記憶體中,占用大,容易擠出熱資料

4.4緩存表和彙總表

4.5 加快Alter table 的速度

​ ALTER TABLE操作對特大表來說,是個大問題。

隻修改frm(表結構)檔案

建立高性能的索引

索引是存儲引擎用于快速找到記錄的一種資料結構。

5.1 索引基礎

要了解MySQL中索引是如何工作的,最簡單的方法就是去看看一本書的 “索引” 部分:如果想在一本書中找到某個特定主題,一般會先看書的“索引”,找到對應的頁碼。

在MySQL中, 存儲引擎用類似的方法使用索引, 其先在索引中找到對應值, 然後根據比對的索引記錄找到對應的資料行。 假如要運作下面的查詢:

mysql> SELECT first_name FRom sakila.actor WHERE actor_id = 5;
           

如果在actor_id列上建有索引, 則MySQL将使用該索引找到actor_id為5的行, 也就是說, MySQL先在索引上按值進行查找, 然後傳回所有包含該值的資料行。

索引可以包含一個列或多個列的值。如果索引包含多個列,那麼列的順序也很重要。因為MYSQL隻能高效地使用索引的最左字首列。

索引的類型

在MySQL中, 索引是在存儲引擎層而不是伺服器層實作的。

《高性能MYSQL》

B-Tree索引

  • 全值比對:指和索引中所有的列進行比對。例如比對key的所有字段(last_name,first_name,dob)。
  • 比對最左字首:隻使用索引的第一列。例如隻使用last_name.
  • 比對列字首:也可比對某列的值開頭部分。比如以J開頭的人。這裡隻使用索引的第一列。
  • 比對範圍值:查找姓在Allen到Barry之間的人。這裡隻使用索引的第一列。
  • 精确比對某一列并範圍比對另一列:第一列last_name精确比對,第二列first_name範圍比對。
  • 隻通路索引的查詢:即查詢隻需要通路索引。

B-Tree索引限制

  • 如果不是按照索引的最左列開始查找,則無法使用索引。如上面例子的索引無法查找名稱為BIll的人。也無法查找某個特定生日的人。

    不能跳過索引中的列。無法查找姓為Smath并且在特定日期出生的人,因為跳過了first_name 列。

  • 如果查詢中有某個列的範圍查詢,則其右邊所有的列都無法使用索引優化查找。例如查詢Where last_name = ‘Smath’ and first_name like ‘%J’ and dob=‘1970-02-01’.這個查詢隻能使用索引的前兩列,因為like是一個範圍條件。

哈希索引

《高性能MYSQL》

哈希索引使用哈希表實作,隻有精确比對索引所有列的查詢才有效。

哈希索引限制

  • 哈希索引隻能包含哈希值和行指針。是以不能用索引的值避免讀行。
  • 哈希索引并不是按照索引值順序存儲的,是以無法進行排序。
  • 哈希索引不支援部分索引比對列查找。
  • 哈希索引隻支援等值比較查詢。
  • 通路哈希索引非常快,除非有哈希沖突。當哈希出現沖突時,會進行連表存儲。
  • 哈希沖突時,索引重建

    代價會很高。

InnoDB引擎有一個特殊的功能叫做 “自适應哈希索引(adaptivehash index)"。當InnoDB注意到某些索引值被使用得非常頻繁時,它會在記憶體中基千B-Tree索引之上再建立一個哈希索引,這樣就讓B-Tree索引也具有哈希索引的一些優點,比如快速的哈希 查找。

5.2 索引的優點

  1. 索引大大減少了伺服器需要掃描的資料量。
  2. 索引可以幫助伺服器避免排序和臨時表。
  3. 索引可以将随機I/O變為順序I/O。

5.3 高性能的索引政策

獨立的列

slelect actor_id from actor where actor_id +1=5
           

actor_id +1=5無法被解析成actor_id = 4。是以要将索引列單獨存放在比較符合的一側。

字首索引和索引選擇性

有時候需要索引很長的字元列, 這會讓索引變得大且慢。

通常可以索引開始的部分字元,這樣可以大大節約索引空間, 進而提高索引效率。但這樣也會降低索引的選擇性。索引的選擇性是指,不重複的索引值(也稱為基數,cardinality)和資料表的記錄總數(#T)的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,因為選擇性高的索引可以讓MySQL在查找時過濾掉更多的行。唯一索引的選擇性是1, 這是最好的索引選擇性,性能也是最好的。

不重複索引值/記錄總數接近0.031就可以使用了。

slelect count(DISTINT city) /count(*) from city_dome.
ALERT TABLE city ADD KEY (city(7)).
           

多列索引

單列索引會引起索引的合并,并不是最優政策。

選擇合适的索引列順序

  • 當不需要考慮排序和分組時,将選擇性最高的列放在前面通常是很好的。這時候索引的作用隻是用于優化WHERE條件的查找。
slelect * from payment where staff_id=584 and (customer_id=30.
           

通過執行

slelect sum(customer_id=30),sum(staff_id=584) from payment 
           

哪個列的基數小,需要把customer_id放在最前列。

字首索引的條件值基數比正常值高的時候,索引基本沒什麼用。比如索引的列滿足全表所有的行。

聚簇索引

聚簇索引不是一種單獨的索引類型,而是一種資料存儲方式。InnoDB的聚簇索引儲存了B-Tree索引和資料行。

《高性能MYSQL》

覆寫索引

如果索引包含所需要查詢字段的值,成為覆寫索引。

覆寫索引好處:

  • 索引條目通常遠小于資料行大小, 是以如果隻需要讀取索引, 那MySQL就會極大地減少資料通路量。
  • 因為索引是按照列值順序存儲的(至少在單個頁内是如此), 是以對千1/0密集型的範圍查詢會比随機從磁盤讀取每一行資料的I/O要少得多。
  • 由于InnoDB的聚簇索引, 覆寫索引對lnnoDB表特别有用。InnoDB的二級索引在葉子節點中儲存了行的主鍵值,是以如果二級主鍵能夠覆寫查詢, 則可以避免對主鍵索引的二次查詢。

覆寫索引缺點:

1、插入速度嚴重依賴于插入順序,按照主鍵的順序插入是加載資料頁到InnoDB表中速度最快的方式。但是如果不按照主鍵順序加載資料,那麼在加載完成後最好使用 optimize table 指令重新組織一下表。

2、更新聚簇索引列的代價很高,因為會強制InnoDB将每個被更新的行移動到新的位置。插入的時候會面臨頁分裂的問題。頁分裂會導緻表占用更多的磁盤空間。

3、二級索引可能比想象的大,因為二級索引的葉子結點儲存了引用行的主鍵

4、二級索引通路需要兩次索引查找,要回表,對于innodb,自适應hash索引能夠減少這樣的重複工作。

二級索引查找行,需要找到葉子節點所對應的主鍵值,再去聚簇索引對應的值

使用了覆寫索引 EXPLAIN的Extra 列會顯示Using index。

在Innodb中按主鍵順序插入行

随機插入缺點:

  • 寫入的目标頁可能不在記憶體緩存區,那麼插入記錄的時候需要先從磁盤讀取目标頁到記憶體中。這會導緻大量的随機IO.如果是順序插入,由于是插入到上一個記錄的後面,則大多數情況下(不需要開辟新頁的情況)磁盤頁是已經加載到記憶體了的。
  • 因為寫入是亂序的,InnoDB可能需要不斷的的做頁分裂操作,以便為新的行配置設定空間。而頁分裂會導緻移動大量的資料,而且一次分裂至少要修改三個頁而不是一個頁。
  • 由于頻繁的分頁,頁面會變得稀疏并被不規則的填充,最後會導緻資料碎片。

順序的主鍵什麼時候會造成更壞的結果?

對于高并發工作負載,在InnoDB中按主鍵順序插入可能會造成明顯的争用。主鍵的上屆會變成“熱點”。因為所有的插入都發生在這裡,是以并發插入可能導緻間隙鎖競争。另一個熱點可能是AUTO_INCREMENT鎖機制;如果遇到這個問題,則可能需要考慮重新設計表或者應用,或者更改innodb_autoinc_lock_mode配置。如果你的伺服器版本還不支援innodb_autoinc_lock_mode參數,可以更新到新版本的InnoDB,可能對這種場景會工作得更好。

假如索引覆寫了where 條件中的字段,但不是整個查詢涉及的字段,還是會回表擷取資料行。

select * from products where actor='SEAN' and title like  '%APOLLO%'
           

可以使用延遲關聯解決:

SELECT
	*
FROM
	products
JOIN (
	SELECT
		product_id
	FROM
		products
	WHERE
		actor = 'SEAN'
	AND title LIKE '%APOLLO%'
) t1 ON t1.product_id = products.id
           

使用索引掃描來做排序

MySQL 有兩種方式可以生成有序的結果:通過排序操作; 或者按索引順序掃描注。 如果 EXPLAIN 出來的 type 列的值為 “index”, 則說明 MySQL 使用了索引掃描來做排序(不 要和 Extra 列的 “Using index” 搞混淆了)。

隻有當索引的列順序和ORDER BY子句的順序完全一緻, 井且所有列的排序方向(倒序或正序)都一樣時,MySQL才能夠使用索引來對結果做排序注。

如果查詢需要關聯多張表,則隻有當ORDE R BY 子句引用的字段全部為第一個表時,才能使用索引做排序。ORDE R BY 子句和查找型查詢的限制是一樣的:需要滿足索引的最左字首的要求;否則,MySQL都需要執行排序操作,而無法利用索引排序。

壓縮索引

MyISAM使用字首壓縮來減少索引的大小。

備援和重複索引

重複索引是沒有必要的。

備援索引可以滿足不同條件的查詢。

備援索引和重複索引有一些不同。 如果建立了索引(A, B), 再建立索引 (A) 就是備援索引,因為這隻是前一個索引的字首索引。 是以索引(A, B) 也可以當作索引 (A) 來使用(這種備援隻是對B-Tree索引來說的)。 但是如果再建立索引,(B, A), 則不是備援索引,索引 (B)也不是,因為 B不是索引 (A, B) 的最左字首列。 另外,其他不同類型的索引(例如哈希索引或者全文索引) 也不會是B-Tree索引的備援索引,而無論覆寫的索引列是什麼。

索引和鎖

InnoDB 在二級索引上使用共享鎖,主鍵索引使用排他鎖。

查詢性能優化

6.1 為什麼查詢會慢

如果把查詢看做一個任務的話,它是由一系列子任務構成的。每個子任務執行都會消耗一定的時間。

如果要優化查詢,實際上要優化其子任務,要麼清除其中一些子任務,要麼減少子任務的執行次數,要麼讓任務運作得更快。

6.2 慢查詢基礎:優化資料通路

  • 确認應用程式是否在檢索大量超過需要的資料。這通常意味着通路了太多的行或列。
  • 确認MySQL伺服器層是否在分析大量超過需要的資料行。

是否向資料庫請求了不必要的資料

  • 查詢不需要的記錄

    一個常見的錯誤是常常會誤以為MySQL會隻傳回需要的資料,實際上MySQL卻是先傳回全部結果集再進行計算。最簡單的辦法鎖加上LIMIt。

  • 多表關聯時傳回全部列

    查詢所有在電影Academy Dinosaur中出現的演員,千萬不要按下面的寫法編寫查詢。

  • 總是取出全部列

    每次看到SELECT *的時候都需要要用懷疑的眼光審視,是不是真的需要傳回全部的列?很可能不是必須的。取出全部的列,會讓優化器無法完成索引覆寫掃描這類優化,還會為伺服器帶來額外的I/O、記憶體和CPU消耗。

  • 重複查詢相同的資料

    初次查詢時将資料緩存,在需要時在緩存中取出來。

MySQL是否在掃描額外的記錄

對于MySQL, 最簡單的衡量查詢開銷的三個名額如下:

  • 響應時間
  • 掃描的行數
  • 傳回的行數

響應時間

響應時間是兩個部分之和:服務時間和排隊時間。

掃描的行數和傳回的行數

分析查詢時,檢視該查詢掃描的行數是非常有幫助的。 這在一定程度上能夠說明該查詢找到需要的資料的效率高不高。

理想情況下掃描的行數和傳回的行數應該是相同的。

掃描行數與通路類型

explain的type的顯示了通路類型,速度從慢到快,分别是:

all(全表掃描)、index(索引掃描)、range(範圍掃描)、ref(範圍索引)、ref_eq(唯一性索引)、const(常數引用)。

6.3 重構查詢的方式

一個複雜查詢還是多個簡單查詢

設計查詢的時候需要考慮是否需要将多個複雜查詢拆分為多個簡單查詢。

切分查詢

定時删除資料時,一次删除可能需要鎖定很多資料,可以拆分多次執行。

** 分解關聯查詢**

6.4 查詢執行的基礎

MYSQL執行查詢的過程:

《高性能MYSQL》

MySQL執行查詢的過程:

(1)用戶端發送一條查詢給伺服器

(2)伺服器先檢查查詢緩存,如果命中了緩存,則立即傳回存儲在緩存中的結果,否則進入下一個階段。

(3)伺服器端進行SQL解析、預處理,再由優化器生成對應的執行計劃

(4)将結果傳回給用戶端。

查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,那會優先檢測這個查詢是否命中緩存中的資料。

查詢優化處理

首先,mysql會将sql語句解析成文法樹,然後驗證文法是否錯誤。

文法樹合法後會被優化器轉成執行計劃。

MySQL如何執行關聯查詢

mysql對于任何關聯都執行嵌套循環關聯操作,即先在一個表中循環取出單條資料,然後再嵌套循環到下一個表中尋找比對的行,依次進行。

《高性能MYSQL》

6.7 優化特定類型的查詢

優化count()查詢

count()可以統計某個列的數值,也可以統計行數。

在統計列值是要求列是非空的(不統計NULL)。count(*) 統計行數。

//如果num > 0, 将num的值累加到total_num, 否則将0累加到total_num.
//如果num < 0, 将1累加到negative_num, 否則将0累加到negative_num.
SELECT
	sum(IF(num > 0, num, 0)) AS total_num,
	sum(IF(num < 0, 1, 0)) AS negative_num_count
FROM
	inventory_product
GROUP BY
	product_i


SELECT
	COUNT(color = 'blue' OR NULL) AS blue,
	COUNT(color = 'red' OR NULL) AS red
FROM
	items

SELECT
	sum(IF(color = 'blue', 1, 0)) AS blue,
	sum(IF(color = 'red', 1, 0)) AS red
FROM
	items
	
SELECT
	sum(color = 'blue') AS blue,
	sum(color = 'red') AS red
FROM
	items


           

優化limit分頁

偏移量非常大的時候,例如LIMIT 10000,20可能需要查詢100020條結果,然後傳回20條。

可以使用書簽記錄上一次查詢的位置,那麼下次查詢就會從書簽位置開始掃描。

select * from user where id > 10000 limit 10000,20
           

複制

1.2 複制如何工作

MySQL複制資料的三個步驟:

1.在主庫上把資料更改記錄到二進制日志中(二進制日志事件);

2.備庫将主庫上的日志複制到自己的中繼日志中;

3.備庫讀取中繼日志中的事件,将其重放到備庫資料之上。

第一步就是在主庫上記錄二進制日志。在每個事務更新資料完成之前,主庫将資料更新事件記錄到二進制日志記錄中。MySQL會按照事務的送出順序,而非每條語句的執行順序記錄二進制日志。在記錄二進制日志完成後,主庫會告訴存儲引擎送出事務。

下一步備庫主庫的二進制日志複制到其本地中繼日志。首先,備庫啟動一個工作線程(I/O線程)。I/O線程跟主庫上建立一個普通的連接配接,然後再主庫啟動一個二進制轉儲線程讀取主庫的二進制日志中的事件,如果已經跟上主庫,它會睡眠并等待主庫産生新的事件。I/O線程将這些事件寫入中繼日志。

SQL slave thread(SQL從線程)處理該過程的最後一步。SQL線程從中繼日志讀取事件,并重放其中的事件而更新slave的資料,使其與主庫中的資料一緻。隻要該線程與I/O線程保持一緻,中繼日志通常會位于OS的緩存中,是以中繼日志的開銷很小。