精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

MySQL 崩潰恢復(fù)過程分析

數(shù)據(jù)庫 MySQL
本文介紹的崩潰恢復(fù)過程,包含 Server 層和 InnoDB,不涉及其它存儲引擎,內(nèi)容基于 MySQL 8.0.29 源碼。

天有不測風(fēng)云,數(shù)據(jù)庫有旦夕禍福。

前面寫 Redo 日志的文章介紹過,數(shù)據(jù)庫正常運行時,Redo 日志就是個累贅。

現(xiàn)在,終于到了 Redo 日志揚眉吐氣,大顯身手的時候了。

本文我們一起來看看,MySQL 在崩潰恢復(fù)過程中都干了哪些事情,Redo 日志又是怎么大顯身手的。

本文介紹的崩潰恢復(fù)過程,包含 server 層和 InnoDB,不涉及其它存儲引擎,內(nèi)容基于 MySQL 8.0.29 源碼。

正文

1、概述

MySQL 崩潰也是一次關(guān)閉過程,只是比正常關(guān)閉著急了一些。

正常關(guān)閉時,MySQL 會做一系列收尾工作,例如:清理 undo 日志、合并 change buffer 緩沖區(qū)等操作。

具體會進行哪些收尾工作,取決于系統(tǒng)變量 innodb_fast_shutdown 的配置。

崩潰直接就是戛然而止,撂挑子不干了,還沒來得及進行的那些收尾工作怎么辦?

那就只能等待下次啟動的時候再干了,這就是本文要介紹的崩潰恢復(fù)過程。

2、讀取兩次寫頁面

MySQL 一旦崩潰,Redo 日志就要去拯救世界了(MySQL 就是它的世界),Redo 日志拯救世界的方式就是把還沒來得及刷盤的臟頁恢復(fù)到崩潰之前那一刻的狀態(tài)。

雖然 Redo 日志能夠用來恢復(fù)數(shù)據(jù)頁,但這是有前提條件的:數(shù)據(jù)頁必須完好無損的狀態(tài)。

本文我們把系統(tǒng)表空間、獨立表空間、undo 表空間中的頁統(tǒng)稱為數(shù)據(jù)頁。

如果數(shù)據(jù)頁剛寫了一半,MySQL 就戛然而止,這個數(shù)據(jù)頁就損壞了,面對這種情況,Redo 日志也是巧婦難為無米之炊。

Redo 日志拯救世界之路就要因為這個問題停滯不前嗎?

那顯示是不能的,這就該輪到兩次寫上場了。

兩次寫?的官方名字是 double write?,它包含內(nèi)存緩沖區(qū)?和 dblwr 文件兩個部分,InnoDB 臟頁刷盤前,都會先把臟頁寫入內(nèi)存緩沖區(qū),再寫入 dblwr 文件,成功之后才會把臟頁刷盤。

兩次寫通過系統(tǒng)變量 innodb_doublewrite? 控制開啟或關(guān)閉,本文內(nèi)容基于該系統(tǒng)變量的默認值 ON,表示開啟兩次寫。

如果臟頁寫入內(nèi)存緩沖區(qū)和 dblwr 文件的程中,MySQL 崩潰了,表空間中對應(yīng)的數(shù)據(jù)頁還是完整的,下次啟動時,不需要用兩次寫頁面修復(fù)這個數(shù)據(jù)頁。

如果臟頁刷盤時,MySQL 崩潰了,表空間對應(yīng)的數(shù)據(jù)頁損壞了,下次啟動時,應(yīng)用 Redo 日志到數(shù)據(jù)頁之前?,需要用兩次寫頁面修復(fù)這個數(shù)據(jù)頁。

dblwr 文件 默認位于 MySQL 數(shù)據(jù)目錄下:

[csch@csch /usr/local/mysql_8_0_29/data] ls -l | grep dblwr
-rw-r----- 1 csch staff 192K 8 27 12:04 #ib_16384_0.dblwr
-rw-r----- 1 csch staff 8.2M 8 1 16:29 #ib_16384_1.dblwr

MySQL 啟動過程中,會把 *.dblwr 文件中的所有兩次寫頁面加載到兩次寫內(nèi)存緩沖區(qū),并用內(nèi)存緩沖區(qū)中的兩次寫頁面修復(fù)損壞的數(shù)據(jù)頁,然后再應(yīng)用 Redo 日志到數(shù)據(jù)頁。

3、恢復(fù)數(shù)據(jù)頁

應(yīng)用 Redo 日志到數(shù)據(jù)頁(3.4 小節(jié)),需要先讀取 Redo 日志(3.3 小節(jié))。

讀取日志 Redo 日志,需要有個起點,起點就是最后一次 checkpoint 的 lsn(3.1 小節(jié))。

應(yīng)用 Redo 日志有一個前提:數(shù)據(jù)頁必須是完好無損的。要保證數(shù)據(jù)頁的完整性,應(yīng)用 Redo 日志之前需要修復(fù)損壞的數(shù)據(jù)頁(3.2 小節(jié))。

修復(fù)損壞數(shù)據(jù)頁只需要保證在應(yīng)用 Redo 日志之前就行了,之所以安排在 3.2 小節(jié),是遵循了源碼中的順序。

了解本節(jié)安排內(nèi)容順序的邏輯,有助于理解應(yīng)用 Redo 日志恢復(fù)數(shù)據(jù)頁的過程,接下來我們正式進入下一個環(huán)節(jié)。

(1)找到 last_checkpoint_lsn

讀取 Redo 日志之前,必須先確定一個起點,這個起點就是 InnoDB 最后一次 checkpoint 操作的 lsn,也就是 last_checkpoint_lsn。

每個 Redo 日志文件的前 4 個 block 都是保留空間,不會用來寫 Redo 日志,last_checkpoint_lsn? 和其它 checkpoint 信息一起,位于第 1 個 Redo 日志文件的第 2、4 個 block 中。

Redo 日志文件中每個 block 的大小為 512 字節(jié)。

InnoDB 每次進行 checkpoint 操作時,都會把 checkpoint_no 加 1,用于標識一次 checkpoint 操作。

然后把本次 checkpoint 信息寫入 Redo 日志文件的第 2 或第 4 個 block 中。具體寫入哪個 block,取決于 checkpoint_no。

如果 checkpoint_no 是奇數(shù),checkpoint 信息寫入第 4 個 block。

如果 checkpoint_no 是偶數(shù),checkpoint 信息寫入第 2 個 block。

確定讀取 Redo 日志的起點時,從第 2、4 個 block 中讀取較大的那個 last_checkpoint_lsn 作為起點。

為什么 checkpoint 信息要存儲到 2 個 block 中?

這是一個用于保證 checkpoint 信息安全性的簡單好用的方法,因為每次 checkpoint 只會往其中一個 block 寫入信息。

萬一就在某次寫 checkpoint 信息的過程中 MySQL 崩潰了,有可能導(dǎo)致正在寫入的這個 block 中的 checkpoint 信息不正確。

這種情況下,另一個 block 中的 checkpoint 信息肯定是正確的了,因為它里面的信息是上一次正常寫入的。

能夠用這種冗余方式來保證 checkpoint block 的安全性,基于一個前提:last_checkpoint_lsn 不需要那么精確。

last_checkpoint_lsn 比實際需要應(yīng)用 Redo 日志起點處的 lsn 小是沒關(guān)系的,不會造成數(shù)據(jù)頁不正確,只是會多掃描一點 Redo 日志而已,應(yīng)用 Redo 日志時會過濾已經(jīng)刷盤的臟頁對應(yīng)的 Redo 日志。

(2)修復(fù)損壞的數(shù)據(jù)頁

把兩次寫文件中的所有數(shù)據(jù)頁都加載到內(nèi)存緩沖區(qū)之后,需要用這些頁來把系統(tǒng)表空間、獨立表空間、undo 表空間中損壞的數(shù)據(jù)頁恢復(fù)到正常狀態(tài)。

正常狀態(tài)指的是 MySQL 崩潰之前,數(shù)據(jù)頁最后一次正確的刷新到磁盤的狀態(tài)。

恢復(fù)數(shù)據(jù)頁的過程是對兩次寫內(nèi)存緩沖區(qū)中的所有數(shù)據(jù)頁進行循環(huán),從兩次寫數(shù)據(jù)頁中讀取表空間 ID、頁號,然后根據(jù)表空間 ID 和頁號去系統(tǒng)表空間、獨立表空間、undo 表空間中讀取對應(yīng)的數(shù)據(jù)頁。

讀取到對應(yīng)的數(shù)據(jù)頁之后,會根據(jù)其 File Header、File Trailer 中的一些字段判斷數(shù)據(jù)頁是不是已經(jīng)損壞了:

首先,從 File Header 中讀取 FILE_PAGE_LSN 字段,如果 FILE_PAGE_LSN 字段值大于當前系統(tǒng)已經(jīng)生成的 Redo 日志的最大 LSN,說明數(shù)據(jù)庫出現(xiàn)了不可描述的錯誤,數(shù)據(jù)頁已經(jīng)損壞。

然后,從 File Header 中讀取 FILE_PAGE_SPACE_OR_CHECKSUM 字段值,從 File Trailer 的前 4 字節(jié)中讀取 checksum。

如果 FILE_PAGE_SPACE_OR_CHECKSUM 字段值和 File Trailer checksum 不一樣,說明數(shù)據(jù)頁已經(jīng)損壞。

一旦出現(xiàn)了上面 2 種情況中的 1 種,把兩次寫數(shù)據(jù)頁的內(nèi)容復(fù)制到對應(yīng)的數(shù)據(jù)頁中,數(shù)據(jù)頁就會恢復(fù)到正常狀態(tài)了。

(3)讀取 Redo 日志

前面確定了讀取 Redo 日志的起點 last_checkpoint_lsn,接下來就該讀取 Redo 日志了,主要流程如下:

圖片

?第 1 步,InnoDB 會以 64K? 為單位,從 Redo 日志文件讀取日志到 log buffer 中。

64K = 4 * innodb_page_size,所以,每次從 Redo 日志文件讀取的數(shù)據(jù)量取決于系統(tǒng)變量 innodb_page_size。

第 2 步,已經(jīng)讀取到 log buffer 中的 block,利用 block header 和 block tailer 中的信息對 block 進行完整性檢驗之后,把 block body 信息拷貝到另一個緩沖區(qū) parsing buffer。

parsing buffer 是一個 2M 的固定大小緩沖區(qū),用于存放即將要被解析的 Redo 日志。

Redo 日志每個 block 的大小為 512 字節(jié),block header 為 12 字節(jié),block trailer 為 4 字節(jié)。

從 log buffer 的每個 block 中拷貝到 parsing buffer 的 block body 大小就是 512-12-4 = 496 字節(jié),也就是每個 block 中存放的 Redo 日志數(shù)據(jù)部分。

第 3 步,解析 parsing buffer 中的 Redo 日志。

這一步解析 Redo 日志,實際上只是個預(yù)處理操作,并不會完整的解析每一條 Redo 日志,而是只會解析每一條 Redo 日志中的頭信息以及數(shù)據(jù)地址,包括以 4 個部分:

  • Redo 日志類型。
  • Redo 日志所屬數(shù)據(jù)頁的表空間 ID。
  • Redo 日志所屬數(shù)據(jù)頁的頁號。
  • Redo 日志數(shù)據(jù),這部分只是得到了每一條 Redo 日志在 block body 中的地址,后面應(yīng)用 Redo 日志到數(shù)據(jù)頁時會用到。

第 4 步,把第 3 步解析出來的每一條 Redo 日志的 4 個部分都拷貝到 hash 表中。

圖片

這個 hash 表是個嵌套結(jié)構(gòu),第 1 層 hash key 是表空間 ID,value 也是個 hash 結(jié)構(gòu),也就是第 2 層。

同一個表空間的 Redo 日志以頁單位組織到一起,存放到以表空間 ID 為 key 的第 1 層 hash value 中。

第 2 層的 hash key 是頁號,value 是需要應(yīng)用到這個數(shù)據(jù)頁的 Redo 日志組成的鏈表。

同一個數(shù)據(jù)頁的 Redo 日志鏈表以頁號為 key,放在第 2 層 hash value 中。

鏈表中的 Redo 日志按照產(chǎn)生的先后順序排列,第 1 條就是要應(yīng)用的這些 Redo 日志中最早產(chǎn)生的那條。

第 5 步,應(yīng)用 Redo 日志到數(shù)據(jù)頁。

如果第 4 步進行的過程中,Redo 日志數(shù)據(jù)拷貝到 hash 表之后,導(dǎo)致 hash 表占用的空間大于 max_memory,那么需要應(yīng)用 Redo 日志到數(shù)據(jù)頁,應(yīng)用完成之后,清空 hash 表,為下一批 Redo 日志數(shù)據(jù)騰出空間。

這里的 max_memory 表示 hash 表能夠使用的最大內(nèi)存空間。

1 ~ 5 步是個循環(huán)執(zhí)行過程,經(jīng)過 N 輪循環(huán)之后,hash 表中有非常大的可能性還存在著最后一批 Redo 日志,因為占用空間??小于等于?? max_memory 而只能在那里苦苦等待著被應(yīng)用到 Redo 日志,這個工作就要等待第 6 步去干了。

第 6 步,收尾工作。

1 ~ 5 步循環(huán)結(jié)束之后,收尾工作就把 hash 表中剩下的 Redo 日志應(yīng)用到數(shù)據(jù)頁,這是崩潰過程中最后一次應(yīng)用 Redo 日志。

前面都沒有提到過存放 Redo 日志的 hash 表在哪里,能使用多大內(nèi)存,不知道你有沒有好奇過?

這個 hash 表并不會單獨申請一大塊內(nèi)存,而是借用了 buffer pool 中的內(nèi)存。

因為在崩潰恢復(fù)過程中,進行到讀取 Redo 日志階段時,buffer pool 還沒有真正開始用,所以可以先借來給 hash 表用一下。

不過 hash 表并不能使用 buffer pool 的全部內(nèi)存,而是需要保留一部分內(nèi)存,用于應(yīng)用 Redo 日志到數(shù)據(jù)頁的過程中,加載數(shù)據(jù)頁到 buffer pool 中。

保留內(nèi)存大小為:buffer pool 實例數(shù)量 * 256 個數(shù)據(jù)頁?,buffer pool 中的剩余內(nèi)存,就是第 5 步提到的 max_memory,也就是 hash 表能夠使用的最大內(nèi)存。

(4)應(yīng)用 Redo 日志

前面介紹讀取 Redo 日志,為了流程的完整性,有 2 個步驟已經(jīng)涉及到應(yīng)用 Redo 日志了。這里要介紹的是應(yīng)用 Redo 日志的過程,會比上一小節(jié)深入一些。

讀取 Redo 日志階段,已經(jīng)把所有需要應(yīng)用的 Redo 日志都進行過預(yù)處理,并拷貝到 hash 表了。

存放 Redo 日志的 hash 表是一個嵌套結(jié)構(gòu):

  • 第 1 層的 hash key 是表空間 ID,hash value 還是一個 hash 表。
  • 第 2 層的 hash key 是頁號,hash value 是個 Redo 日志鏈表,鏈表中的每個元素就是一條需要應(yīng)用的 Redo 日志,按照產(chǎn)生的先后排序。

把每個數(shù)據(jù)頁的 Redo 日志匯總到一起再去應(yīng)用 Redo 日志,這樣做的好處是效率高。

在崩潰恢復(fù)過程中,每個數(shù)據(jù)頁只需要被加載到 buffer pool 中一次,一個數(shù)據(jù)頁的 Redo 日志能夠一次性應(yīng)用,干脆利落。

應(yīng)用 Redo 日志就是循環(huán)這個嵌套的 hash 表,把每一條 Redo 日志都應(yīng)用到數(shù)據(jù)頁中,主要流程如下:

圖片

第 1 步,從第 1 層 hash 表中取到表空間 ID 和這個 undo 表空間下需要應(yīng)用的 Redo 日志組成的第 2 層 hash 表。

第 2 步,從第 2 層 hash 表中取到一個頁號和該數(shù)據(jù)頁中需要應(yīng)用的 Redo 日志鏈表。

第 3 步,判斷當前循環(huán)的數(shù)據(jù)頁是不是已經(jīng)加載到 buffer pool 中了。

如果當前頁沒有加載到 buffer pool 中,進入第 4 步。

如果當前頁已經(jīng)加載到 buffer pool 中,進入第 5 步。

第 4 步,把不在 buffer pool 中的數(shù)據(jù)頁加載到 buffer pool 中。

加載數(shù)據(jù)頁到 buffer pool 中,是一個異步的批量操作,有可能會一次加載多個數(shù)據(jù)頁。

也就是說,把數(shù)據(jù)頁從表空間加載到 buffer pool 中會觸發(fā)預(yù)讀,提前把一批需要應(yīng)用 Redo 日志的數(shù)據(jù)頁一次性加載到 buffer pool 中。

預(yù)讀的數(shù)據(jù)頁,不是隨機讀取的,而是根據(jù)第 3 步判斷不在 buffer pool 中的數(shù)據(jù)頁的頁號(記為 page_no),計算出一個頁號范圍,把這個范圍內(nèi)需要應(yīng)用 Redo 日志的數(shù)據(jù)頁,全都加載到 buffer pool 中。

頁號范圍的起點:low_limit = page_no - page % 32,終點:low_limit + 32。

循環(huán) low_limit ~ low_limit + 32 范圍內(nèi)的頁號,只要碰到需要應(yīng)用 Redo 日志的數(shù)據(jù)頁,就先把頁號臨時存放到一個數(shù)組里。

循環(huán)結(jié)束后,把數(shù)組里的頁號對應(yīng)的數(shù)據(jù)頁異步批量加載到 buffer pool 中。

從上面的邏輯可以看到,一次預(yù)讀最多只讀 32 個數(shù)據(jù)頁。

第 5 步,應(yīng)用 Redo 日志到數(shù)據(jù)頁。

根據(jù)第 1 步取到的表空間 ID和第 2 步取到的頁號,從 hash 表中獲取該數(shù)據(jù)頁需要應(yīng)用的 Redo 日志鏈表。

從數(shù)據(jù)頁的 File Header 中讀取 FILE_PAGE_LSN,循環(huán) Redo 日志鏈表中的每一條日志,判斷該日志的 start_lsn 是否大于等于 FILE_PAGE_LSN。

如果 start_lsn < FILE_PAGE_LSN,說明該 Redo 日志對應(yīng)的操作修改的數(shù)據(jù)頁,在 MySQL 崩潰之前就已經(jīng)刷盤,該 Redo 日志就不需要應(yīng)用到數(shù)據(jù)頁了。

如果 start_lsn >= FILE_PAGE_LSN,說明該 Redo 日志需要應(yīng)用到數(shù)據(jù)頁。

然后,根據(jù) Redo 日志類型,調(diào)用不同的方法解析 Redo 日志,直接修改 buffer pool 中的數(shù)據(jù)頁,對該數(shù)據(jù)頁應(yīng)用 Redo 日志的過程就完成了。

1 ~ 5 步是個循環(huán)過程,直到所有 undo 表空間的 Redo 日志都被應(yīng)用到數(shù)據(jù)頁,循環(huán)過程結(jié)束。

4、刪除 undo 表空間

MySQL 運行過程中,如果有大事務(wù)往 undo 表空間中寫入大量 undo 日志,undo 表空間會變大。

在早期版本中,undo 表空間變大之后,就不能再縮回去了。

現(xiàn)在,如果系統(tǒng)變量 innodb_undo_log_truncate 設(shè)置為 on,當 undo 表空間增長到 innodb_max_undo_log_size 設(shè)置的大?。J值為 1G)之后,InnoDB 會把這個 undo 表空間截斷為初始大小(16M)。

除了通過系統(tǒng)變量控制 undo 表空間自動截斷之外,還可以用下面這個 SQL 手動觸發(fā):

ALTER UNDO TABLESPACE tablespace_name
SET INACTIVE

不管自動還是手動,有可能 InnoDB 正在進行 undo 表空間截斷操作,MySQL 就突然崩潰了,截斷表空間操作還沒有完成,那怎么辦?

等到下次啟動的時候,InnoDB 需要把未完成的 undo 表空間截斷操作繼續(xù)完成。

InnoDB 怎么知道哪些 undo 表空間的截斷操作沒有完成?

這就需要用到一個標記文件了,InnoDB 對某個 undo 表空間進行截斷操作之前,會創(chuàng)建一個對應(yīng)的標記文件,文件名是這樣的:undo_表空間編號_trunc.log。

解釋一下表空間的兩個標識:表空間編號是給咱們?nèi)祟惪吹?,表空間 ID 是 MySQL 內(nèi)部使用的,這兩者不一樣。

以 undo_001 表空間為例,表空間編號為 1?,InnoDB 對 undo_001 表空間進行截斷操作之前,會創(chuàng)建一個 undo_1_trunc.log 文件,如下:

[csch@csch /usr/local/mysql_8_0_29/data] ls -l | grep undo
-rw-r----- 1 csch staff 16M 8 27 12:04 undo_001
-rw-r----- 1 csch staff 16M 8 27 12:04 undo_002
-rw-r--r-- 1 csch staff 16K 6 22 12:36 undo_1_trunc.log

崩潰恢復(fù)過程中,InnoDB 如果發(fā)現(xiàn)某個表空間存在對應(yīng)的 trunc.log 文件,說明這個 undo 表空間在 MySQL 崩潰時正在進行截斷操作。

但是,只通過 trunc.log 文件存在這一個條件,并不能確定 undo 表空間截斷操作沒有完成,還要進一步判斷。

接著讀取 trunc.log 文件的內(nèi)容,把讀到的內(nèi)容轉(zhuǎn)換成數(shù)字,判斷這個數(shù)字是不是等于 76845412。

76845412 是什么?稍候介紹。

如果等于,說明在 MySQL 崩潰之前,undo 表空間截斷操作已經(jīng)完成,只是 trunc.log 文件還沒來得及刪除。此時,直接刪除這個文件就可以了。

如果不等于,說明 MySQL 崩潰時,undo 表空間截斷操作還沒有完成,那就需要繼續(xù)完成。此時,直接刪除 undo 表空間文件。

被刪除的 undo 表空間要等到初始化事務(wù)子系統(tǒng)之后,才會重建,重建過程我們稍后介紹。

舉個例子:啟動過程中發(fā)現(xiàn)了 undo_001 表空間對應(yīng)的 trunc.log 文件,并且文件中存儲的數(shù)字不是 76845412,那就直接刪除 undo_001 表空間。

刪除之后,就只有 undo_1_trunc.log 文件能證明 undo_001 表空間存在過了,就像下面這樣:

[csch@csch /usr/local/mysql_8_0_29/data] ls -l | grep undo
-rw-r----- 1 csch staff 16M 8 27 12:04 undo_002
-rw-r--r-- 1 csch staff 16K 6 22 12:36 undo_1_trunc.log

為什么這里不把 undo 表空間對應(yīng)的 trunc.log 文件一起刪除?

因為 undo 表空間要等到初始化事務(wù)子系統(tǒng)完成之后再重建,而 trunc.log 是 undo 表空間重建的憑證,所以,現(xiàn)在還不能刪除。

接下來我們再看看 trunc.log 文件的創(chuàng)建和寫入過程。

InnoDB 進行 undo 表空間截斷操作之前,就會創(chuàng)建 trunc.log 文件(大小為 innodb_page_size 字節(jié)),并把文件內(nèi)容的所有字節(jié)都初始化為 NULL,然后開始進行 undo 表空間截斷操作。

操作完成之后,會往 trunc.log 文件中寫入一個被稱為魔數(shù)的數(shù)字:76845412,用于標識 undo 表空間截斷操作已經(jīng)完成。

如果魔數(shù)成功寫入 trunc.log 文件,接下來會把 trunc.log 文件刪除,undo 表空間的截斷操作就結(jié)束了。

5、初始化事務(wù)子系統(tǒng)

現(xiàn)在,我們來到了初始化事務(wù)子系統(tǒng)階段。

InnoDB 之所以把初始化事務(wù)子系統(tǒng)安排在刪除 undo 表空間之后,有可能是為了避免讀取要被刪除的 undo 表空間,能夠節(jié)省一點點時間。

刪除還沒有完成截斷操作的 undo 表空間文件之后,剩下的 undo 表空間文件都需要讀取。

從 undo 表空間文件讀取未完成的事務(wù),初始化事務(wù)子系統(tǒng),主要過程如下:

初始化事務(wù)子系統(tǒng)還包含其它操作,不在本文介紹的范圍內(nèi)。

圖片

第 1 步,從內(nèi)存中的 undo 表空間對象數(shù)組中讀取 undo 表空間信息。

undo 表空間默認為 2 個,最多可以有 127 個。

有了獨立 undo 表空間之后,位于系統(tǒng)表空間中的回滾段就已經(jīng)不再使用了,所以不需要從系統(tǒng)表空間的回滾段中讀取事務(wù)信息。

第 2 步,從 undo 表空間中頁號 = 3 的數(shù)據(jù)頁中讀取回滾段。

每個 undo 表空間可以有 1 ~ 128 個回滾段,由系統(tǒng)變量 innodb_rollback_segments 控制,默認值為 2.

第 3 步,從回滾段中讀取 undo slot。

回滾段的段頭頁中有 1024 個 undo slot(4 字節(jié)),每個 undo slot 對應(yīng)一個 undo 段。

如果 undo slot 的值 等于 FIL_NULL,表示這個 undo slot 沒有關(guān)聯(lián)到 undo 段,繼續(xù)執(zhí)行第 3 步,讀取下一個 undo slot。

如果 undo slot 的值 不等于 FIL_NULL,表示這個 undo slot 關(guān)聯(lián)了 undo 段,進入第 4 步。

第 4 步,從 undo slot 對應(yīng)的 undo 段中讀取未完成事務(wù)的信息。

此時,undo slot 的值就是 undo 段的段頭頁的頁號,通過這個頁號可以讀取到 undo 段中的事務(wù)信息。

undo slot 關(guān)聯(lián)了 undo 段,說明數(shù)據(jù)庫崩潰時,undo 段中的事務(wù)還沒有完成,事務(wù)狀態(tài)可能是以下 3 種之一:

  • TRX_STATE_ACTIVE,表示事務(wù)還沒有進入提交階段。
  • TRX_STATE_PREPARED,表示事務(wù)已經(jīng)提交了,但是只完成了二階段提交的 PREPARE 階段,還沒有完成 COMMIT 階段。
  • TRX_STATE_COMMITTED_IN_MEMORY,表示事務(wù)已經(jīng)完成了二階段提交的 2 個階段,還剩一些收尾工作沒做,這種狀態(tài)的事務(wù)修改的數(shù)據(jù)已經(jīng)可以被其它事務(wù)看見了。
  • 事務(wù)的收尾工作有哪些?清理已提交事務(wù)小節(jié)會介紹。

第 1 ~ 4 步是個循環(huán)的過程,直到讀完所有 undo 表空間中的事務(wù)信息結(jié)束。

6、重建 undo 表空間

對于存在 trunc.log 文件的 undo 表空間,因為之前 undo 表空間文件被刪除了,現(xiàn)在要開始著手重建 undo 表空間了,主要流程如下:

圖片

第 1 步,創(chuàng)建 trunc.log 文件,標記 undo 表空間重建操作正在進行中。

看到這里你可能會奇怪,undo 表空間對應(yīng)的 trunc.log 文件不是沒有刪除嗎?這里為什么又要創(chuàng)建一次?

別急,且往下看。

在創(chuàng)建 undo 表空間對應(yīng)的 trunc.log 文件之前,會先刪除之前舊的 trunc.log 文件,然后創(chuàng)建新的 trunc.log 文件。

新舊 trunc.log 文件名是一樣的,例如:對于 undo_001 表空間來說,新舊 trunc.log 文件名都是 undo_1_trunc.log。

?為什么要刪除舊的 trunc.log 文件再創(chuàng)建新的同名 trunc.log 文件呢?

因為重建? undo 表空間和新建 undo 表空間是同一套邏輯,而新建 undo 表空間之前,該表空間并不存在對應(yīng)的 trunc.log 文件。

為了保持統(tǒng)一的邏輯,所以會先刪除已經(jīng)存在的 trunc.log 文件。

第 2 步,創(chuàng)建 undo 表空間文件,初始大小為 16M,這個大小是硬編碼的。

第 3 步,初始化 undo 表空間,把表空間 ID、各種鏈表信息寫入表空間的 0 號頁中,然后分配一個新的數(shù)據(jù)頁,創(chuàng)建并初始化回滾段,回滾段數(shù)量由系統(tǒng)變量 innodb_rollback_segments 控制。

第 4 步,循環(huán) undo 表空間中的所有回滾段,把每個回滾段中的 1024 個 undo slot 都初始化為 FIL_NULL。

第 5 步,標記 undo 表空間重建操作已經(jīng)完成。

InnoDB 會先往 trunc.log 文件中寫入一個魔數(shù) 76845412,表示重建表空間操作已經(jīng)完成。

寫入魔數(shù)成功之后,再把 trunc.log 文件刪除,重建一個 undo 表空間的過程就結(jié)束了。

如果有多個 undo 表空間需要重建,對于每個 undo 表空間都需要進行 1 ~ 5 步的流程。

7、處理事務(wù)

在初始化事務(wù)子系統(tǒng)小節(jié),我們介紹過,從 undo 表空間中讀取出來的事務(wù)有 3 種狀態(tài):

  • TRX_STATE_ACTIVE。
  • TRX_STATE_PREPARED。
  • TRX_STATE_COMMITTED_IN_MEMORY。

處理事務(wù)階段對這 3 種狀態(tài)會進行不同的處理,請接著往下看。

(1)清理已提交事務(wù)

這里要清理的已提交事務(wù),指的是狀態(tài)為 TRX_STATE_COMMITTED_IN_MEMORY 的事務(wù),包含 DDL 和 DML 事務(wù)。

這種狀態(tài)的事務(wù)已經(jīng)完成二階段提交的 PREPARE 和 COMMIT 階段,是已經(jīng)提交成功的事務(wù),只差最后一點點清理工作,它們修改的數(shù)據(jù)已經(jīng)能被其它事務(wù)看見了。

清理工作主要有幾點:

  • 處理 insert undo 段。如果 insert undo 段能被緩存,undo 段會被加入 insert_undo_cached 鏈表尾部,以備重復(fù)使用;如果 insert undo 段不能被緩存,undo 段就會被釋放。
  • 把事務(wù)從讀寫事務(wù)鏈表中刪除。
  • 把事務(wù)狀態(tài)修改為TRX_STATE_NOT_STARTED。

(2)回滾未提交 DDL 事務(wù)

未提交事務(wù)指的是狀態(tài)為 TRX_STATE_ACTIVE 的事務(wù),也就是活躍事務(wù)。

崩潰恢復(fù)過程中,這種狀態(tài)的事務(wù)是需要直接回滾的。

你可能會有個疑問,DDL 事務(wù)不是不能回滾嗎?

DDL 事務(wù)不能回滾,這只是針對 MySQL 用戶而言,MySQL 內(nèi)部并不會受到這個限制。

我們在使用 MySQL 的過程中,如果在一個 DML 事務(wù)中間執(zhí)行了一條 DDL 語句,會觸發(fā)隱式提交,直接把 DML 事務(wù)提交了。

然后 DDL 會開啟一個新事務(wù),這個新事務(wù)是自動提交的,DDL 執(zhí)行完成之后,事務(wù)就直接提交了,我們是沒有機會對 DDL 事務(wù)進行回滾操作的。

MySQL 沒給我們回滾 DDL 事務(wù)的機會,但是它自己有這個特權(quán)。

(3)回滾未提交 DML 事務(wù)

未提交的 DDL 事務(wù)和 DML 事務(wù)在源碼中是在不同時間觸發(fā)的,它的回滾過程和 DDL 事務(wù)一樣。

事務(wù)回滾的過程比較復(fù)雜,本文我們就不展開說了,后續(xù)會寫一篇文章專門介紹事務(wù)回滾的過程。

(4)處理 PREPARE 事務(wù)

PREPARE 事務(wù)指的是狀態(tài)為 TRX_STATE_PREPARED 的事務(wù),這種狀態(tài)的事務(wù)比較特殊,在崩潰恢復(fù)過程中,既有可能被提交,也有可能被回滾。

PREPARE 事務(wù)提交還是回滾,取決于這個事務(wù)的 XID 是否已經(jīng)寫入到 binlog 日志文件中。

事務(wù) XID 是以 binlog event 的方式寫入 binlog 日志文件的,event 的名字是 XID_EVENT。

一個事務(wù)只會有一個 XID,也就只會有一個 XID_EVENT 了。

要知道事務(wù)的 XID_EVENT 是否已經(jīng)寫入到 binlog 日志文件,需要先讀取 binlog 日志文件。

從上面的介紹可以看到,處理 PREPARE 事務(wù)依賴于 binlog 日志文件,因此,這部分邏輯是在打開 binlog 日志文件的過程中實現(xiàn)的。

MySQL 在同一時刻只會往一個 binlog 日志文件中寫入  binlog event,在崩潰那一刻,承載寫入 event 的文件是最后一個 binlog 日志文件。

因此,崩潰恢復(fù)過程中,只需要掃描最后一個 binlog 日志文件,找到其中所有的 XID_EVENT, 用于判斷 PREPARE 事務(wù)的 XID_EVENT 是否已經(jīng)寫入 binlog 日志文件。

如果 MySQL 上一次是正常關(guān)閉,啟動過程中,不會存在沒有完成的事務(wù),沒有 PREPARE 事務(wù)需要處理,也就不用掃描最后一個 binlog 日志文件了。

MySQL 怎么知道上一次是不是正常關(guān)閉呢?

每個 binlog 日志文件的第 1 個 EVENT 都是 FORMAT_DESCRIPTION_EVENT,用于描述 binlog 日志文件格式信息,這個 EVENT 中包含一個標記 LOG_EVENT_BINLOG_IN_USE_F。

binlog 日志文件創(chuàng)建時,這個標記位會被設(shè)置為 1,表示 binlog 日志文件正在被使用。

LOG_EVENT_BINLOG_IN_USE_F 標記在 2 種情況下會被清除:

  • 切換 binlog 日志文件時,舊 binlog 日志文件的LOG_EVENT_BINLOG_IN_USE_F 標記會被清除。
  • MySQL 正常關(guān)閉時,正在使用的 binlog 日志文件的LOG_EVENT_BINLOG_IN_USE_F 標記會被清除。

如果 MySQL 突然崩潰,來不及把這個標記設(shè)置為 0。

那么下次啟動時,MySQL 讀取最后一個 binlog 日志文件的 FORMAT_DESCRIPTION_EVENT 發(fā)現(xiàn) LOG_EVENT_BINLOG_IN_USE_F 標記為 1,就會進入處理 PREPARE 事務(wù)階段,主要流程如下:

圖片

第 1 步,掃描最后一個 binlog 日志文件,讀取 EVENT,找到其中所有的 XID_EVENT,并把讀取到的事務(wù) XID 存放到一個集合中。

第 2 步,InnoDB 循環(huán)讀寫事務(wù)鏈表,每找到一個 PREPARE 事務(wù)都存放到數(shù)組中,最后把數(shù)組返回給 server 層。

第 3 步,讀取 InnoDB 返回的 PREPARE 事務(wù)數(shù)組,判斷事務(wù) XID 是否在第 1 步的事務(wù) XID 集合中。

第 4 步,提交或回滾事務(wù)。

如果事務(wù) XID 在集合中,說明 MySQL 崩潰之前,事務(wù) XID_EVENT 就已經(jīng)寫入 binlog 日志文件了。

XID_EVENT 有可能已經(jīng)同步給從服務(wù)器,從服務(wù)器上可能已經(jīng)重放了這個事務(wù)。

這種情況下,為了保證主從數(shù)據(jù)的一致性,事務(wù)在主服務(wù)器上也需要提交。

如果事務(wù) XID 不在集合中,說明 MySQL 崩潰之前,事務(wù) XID_EVENT 沒有寫入 binlog 日志文件。

XID_EVENT 肯定也就沒有同步給從服務(wù)器了,同樣為了保證主從數(shù)據(jù)的一致性,事務(wù)在主服務(wù)器上也不能提交,而是需要回滾。

3 ~ 4 步是個循環(huán)過程,循環(huán)完 InnoDB 返回的 PREPARE 事務(wù)數(shù)組之后,處理 PREPARE 事務(wù)的過程結(jié)束,崩潰恢復(fù)主要流程也就完成了。

8、總結(jié)

MySQL 崩潰恢復(fù)過程的核心工作有 2 點:

  • 對于 MySQL 崩潰之前還沒有刷新到磁盤的數(shù)據(jù)頁(也就是臟頁),用 Redo 日志把這些數(shù)據(jù)頁恢復(fù)到 MySQL 崩潰之前那一刻的狀態(tài),這相當于對臟頁進行一次刷盤操作。在這之前,需要用兩次寫緩沖區(qū)中的頁把損壞的數(shù)據(jù)頁修復(fù)為正常狀態(tài),然后才能在此基礎(chǔ)上用 Redo 日志恢復(fù)數(shù)據(jù)頁。
  • 清理、提交、回滾還沒有完成的事務(wù)。


    對于已完成二階段提交的 PREPARE、COMMIT 2 個階段的事務(wù),做收尾工作。


    對于活躍狀態(tài)的事務(wù),直接回滾。


    對于 PREPARE 狀態(tài)的事務(wù),如果事務(wù) XID 已寫入 binlog 日志文件,提交事務(wù),否則回滾事務(wù)。

本文轉(zhuǎn)載自微信公眾號「一樹一溪」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系一樹一溪公眾號。

責(zé)任編輯:姜華 來源: 一樹一溪
相關(guān)推薦

2010-07-15 08:50:11

2021-01-26 13:47:08

MySQL存儲數(shù)據(jù)

2010-07-02 09:36:30

SQL Server

2015-07-15 14:38:38

HBase集群崩潰恢復(fù)Hadoop

2019-09-11 11:38:30

MySQLInnoDB數(shù)據(jù)庫

2019-09-11 09:37:17

數(shù)據(jù)庫MySQL系統(tǒng)

2017-09-27 13:42:42

數(shù)據(jù)庫MySQL斷電恢復(fù)

2011-08-15 09:19:22

2010-08-13 14:08:45

Oracle

2010-09-01 16:35:12

SQL刪除存儲過程

2018-07-24 15:26:01

蘋果MBP主板

2009-01-22 10:18:39

2012-02-23 09:43:36

虛擬化VDIVHD備份

2012-09-19 15:06:45

Lucene

2021-05-13 21:51:38

鴻蒙HarmonyOS應(yīng)用

2021-11-23 21:21:07

線上排查服務(wù)

2014-03-26 11:40:49

金山毒霸系統(tǒng)崩潰

2018-03-06 09:54:48

數(shù)據(jù)庫備份恢復(fù)

2017-09-06 08:23:01

數(shù)據(jù)備份恢復(fù)過程正確姿勢

2009-11-20 13:29:59

Oracle數(shù)據(jù)庫恢復(fù)
點贊
收藏

51CTO技術(shù)棧公眾號

精品人妻av一区二区三区| 阿v天堂2014| 午夜影视一区二区三区| 久久精品亚洲精品国产欧美| 成人福利网站在线观看11| 久久久美女视频| 欧洲毛片在线视频免费观看| 日韩一区国产二区欧美三区| 男女av免费观看| h片在线免费观看| 久久色中文字幕| 成人综合国产精品| 黄色在线视频网址| 欧美国产综合| 一区二区成人精品| 天堂www中文在线资源| 精品福利在线| 欧美日韩另类视频| 久久久久福利视频| 91免费在线| 99re66热这里只有精品3直播| 91精品国产综合久久久久久蜜臀| 九九九在线观看| 日韩欧美午夜| 亚洲视频视频在线| 国产精品无码一区二区三| 成人在线精品| 欧美日韩一级视频| 国产精品亚洲二区在线观看| 国内在线免费视频| 亚洲品质自拍视频| 一区二区精品免费视频| 极品美乳网红视频免费在线观看 | 免费黄色一级网站| 看黄在线观看| 亚洲国产欧美日韩另类综合 | 老牛影视av老牛影视av| youjizz亚洲| 日韩一区二区免费高清| 精品亚洲视频在线| 成人福利片在线| 欧美影院一区二区| 久久久国产欧美| 日韩av福利| 色婷婷激情综合| 成人免费观看视频在线观看| 人人草在线视频| 福利精品视频在线| av黄色在线网站| 无遮挡爽大片在线观看视频| 天天色天天操综合| 国产肥臀一区二区福利视频| 九色porny自拍视频在线观看 | 久久久不卡影院| 欧美日韩在线观看一区二区三区| 日本福利片高清在线观看| 99精品久久只有精品| 精品卡一卡二| 欧美日韩国产中文字幕在线| 2021中文字幕一区亚洲| 另类欧美小说| 在线观看a视频| 日韩美女视频19| 欧美一区二区激情| 波多视频一区| 在线免费观看日本一区| av在线网址导航| 日韩精品第二页| 日韩一区二区三区观看| 人妻激情偷乱频一区二区三区| 久久国产精品色av免费看| 日韩福利在线播放| 亚洲最大成人综合网| 99国产精品一区二区| 久久av资源网站| 国产无遮无挡120秒| 国产婷婷精品| 国产精品私拍pans大尺度在线 | 亚洲国产成人一区| 成都免费高清电影| 五月激情综合| 97久久精品视频| 午夜精品久久久久久久蜜桃| 久久66热re国产| 国产精品成人观看视频免费| 日韩三级电影网| 国产精品久久久久久妇女6080 | 国产精品亚洲一区| 手机福利在线| 日韩理论片在线| 无码中文字幕色专区| 欧美精品总汇| 日韩三级.com| 色哟哟精品观看| 欧美日韩三区| 国产精品久久久久久av福利软件| 成人激情四射网| 国产午夜精品久久| 久久这里只有精品8| 高清成人在线| 亚洲第一页在线| 天堂av免费在线| aa级大片欧美三级| 亚洲一区二区三区成人在线视频精品 | 亚洲午夜91| 国产在线高清精品| 日韩a级作爱片一二三区免费观看| 中文字幕日韩一区二区| 成人免费在线小视频| 日韩高清一区| 正在播放欧美一区| 日本中文在线播放| 国产成人精品亚洲777人妖| 日本视频一区二区不卡| av中文在线资源| 欧美一区二区三区日韩| 亚洲а∨天堂久久精品2021| 99精品99| 国产经品一区二区| 菠萝菠萝蜜在线观看| 色94色欧美sute亚洲线路二| 少妇一级淫免费观看| 欧美精品成人| 成人午夜高潮视频| 福利在线视频导航| 黑人欧美xxxx| 无码成人精品区在线观看| 欧洲美女日日| 国产成人精品久久亚洲高清不卡| 性高潮免费视频| 这里只有精品999| 成人av资源网站| 青青在线免费视频| 高清在线一区二区| 按摩亚洲人久久| 免费中文字幕av| а√中文在线8| 在线精品视频免费播放| youjizz.com国产| 亚洲最新色图| 成人深夜直播免费观看| 麻豆视频在线免费观看| 欧美日韩久久久一区| 永久免费av无码网站性色av| 日韩中文字幕亚洲一区二区va在线| 精品欧美一区二区在线观看视频| 18aaaa精品欧美大片h| 精品国产123| 久久久精品99| 成人网男人的天堂| 日本xxxxxxxxxx75| 欧美成人午夜77777| 7777kkkk成人观看| 午夜av免费观看| 色综合天天狠狠| 久久久久久久久久久久| 蜜桃精品在线观看| 一区二区三区视频在线播放| 国产一区二区主播在线| 中文字幕久久亚洲| 国产麻豆精品一区| 亚洲一区二区三区四区中文字幕| 亚洲国产精品第一页| 日韩一级在线| 欧美一区二区综合| 久久人体av| 欧美成aaa人片在线观看蜜臀| 精品久久无码中文字幕| 亚洲国产精品精华液网站| 免费a级黄色片| 男男视频亚洲欧美| 8x8x华人在线| 欧美电影免费网站| 国产精品黄页免费高清在线观看| 日本中文在线观看| 日韩精品资源二区在线| 影音先锋亚洲天堂| 欧美激情一区在线| 能看毛片的网站| 国产一区91| 亚洲人久久久| 91精品啪在线观看国产爱臀| 欧美伊久线香蕉线新在线| av在线免费一区| 精品人在线二区三区| 青草视频在线观看免费| 国产精品久久久久影视| 最新中文字幕日本| 亚洲免费在线| 日本美女爱爱视频| 久久不见久久见中文字幕免费| 国产日韩欧美日韩大片| 69av成人| 日韩午夜在线视频| 亚州男人的天堂| 7777精品伊人久久久大香线蕉| 日韩av无码中文字幕| 国产精品女同一区二区三区| 久久久久久久人妻无码中文字幕爆| 快she精品国产999| 日本精品久久久久久久久久| 成人免费av| 久久综合精品一区| 亚洲乱码一区| 国产精品女人久久久久久| hd国产人妖ts另类视频| 久久中文字幕一区| 国产片在线观看| 亚洲成av人影院在线观看| 一级全黄少妇性色生活片| 欧美日韩国产限制| 日韩一区二区三区四区在线| 国产欧美一区二区在线| 视频免费在线观看| 国产乱一区二区| 亚洲精品高清无码视频| 国产视频一区免费看| 人妻互换免费中文字幕| 91精品啪在线观看国产18| 欧美一级爽aaaaa大片| 成人三级av在线| 91欧美精品午夜性色福利在线 | 日本一欧美一欧美一亚洲视频| 99热国产在线| www国产精品视频| 高清在线观看av| 亚洲欧美在线免费| 亚洲av激情无码专区在线播放| 91精品国产美女浴室洗澡无遮挡| 波多野结衣网站| 一本在线高清不卡dvd| 99热国产在线观看| 亚洲成人自拍一区| 妺妺窝人体色www婷婷| 亚洲天堂a在线| 男人天堂资源网| 亚洲国产精品v| 一本在线免费视频| 亚洲国产精品精华液2区45| 欧美色图亚洲激情| 91蝌蚪国产九色| 欧美一区二区三区成人精品| va亚洲va日韩不卡在线观看| 国产精品熟妇一区二区三区四区| 国产成人免费视频| 亚洲美女高潮久久久| 国产不卡一区视频| 俄罗斯黄色录像| 成人免费精品视频| 亚洲麻豆一区二区三区| av一区二区三区四区| 少妇毛片一区二区三区| 久久精品夜夜夜夜久久| 国产成人精品无码免费看夜聊软件| 久久久影院官网| 精品欧美一区二区久久久| 国产欧美日韩激情| 可以免费看av的网址| 国产精品二三区| 黄色一级片中国| 亚洲www啪成人一区二区麻豆| 精品少妇一二三区| 亚洲国产精品人人做人人爽| 中日韩精品视频在线观看| 狠狠色噜噜狠狠狠狠97| 中文字幕在线日本| 欧美人体做爰大胆视频| 国产又黄又粗又长| 亚洲第一精品久久忘忧草社区| 日韩av资源| xvideos亚洲人网站| 欧洲黄色一区| 日本久久久久久久久久久| 久久91超碰青草在哪里看| 97久久夜色精品国产九色| 日本福利一区| 亚洲国产欧美不卡在线观看| 欧美91精品| 动漫av网站免费观看| 久久99国产精品尤物| 88av在线播放| 日本一区二区三区免费乱视频| 欧美人禽zoz0强交| 欧美日韩亚洲成人| 91精品在线视频观看| 亚洲第一av在线| xxxxx日韩| 欧美激情精品久久久久久久变态 | 国产 高清 精品 在线 a | 亚洲欧洲一区二区| 激情综合亚洲| 日本肉体xxxx裸体xxx免费| 高清不卡一二三区| 中文字幕第24页| 亚洲成人激情综合网| 亚洲天堂狠狠干| 日韩av一区二区在线观看| 麻豆视频在线观看免费| 38少妇精品导航| 亚洲不卡视频| 亚洲高清在线观看一区| 99在线观看免费视频精品观看| 一本一道久久a久久综合蜜桃| 91香蕉视频在线| 劲爆欧美第一页| 欧美日韩亚洲高清一区二区| 五月婷婷伊人网| 欧美猛交免费看| 成人高清一区| 欧美裸体网站| 99精品免费| 中文字幕人妻一区| 亚洲日本va在线观看| 中文字幕av久久爽| 国产视频精品自拍| 国产在线天堂www网在线观看| 91久久久国产精品| 欧美肉体xxxx裸体137大胆| 精品国产一二三四区| 国产99久久久国产精品免费看| 欧美xxxooo| 欧美私模裸体表演在线观看| 视频二区在线| 69av在线视频| 麻豆视频一区| 日本a视频在线观看| 国产精品一区免费视频| 二区三区四区视频| 欧美日韩精品三区| av天在线观看| 国产精品久久久久av免费| 久久综合亚洲| 久久久精品在线视频| 95精品视频在线| 国产精品免费av一区二区| 欧美大片一区二区| 欧美黑人猛交| 91色精品视频在线| 一个色综合网| 男人添女人荫蒂国产| 亚洲精品成人精品456| 国产黄a三级三级三级| 欧美成人全部免费| 亚洲第一二区| 天堂8在线天堂资源bt| jizz一区二区| 国产又黄又粗又爽| 亚洲品质视频自拍网| 欧美日韩不卡| 一区二区视频在线免费| 国内精品久久久久影院一蜜桃| 亚洲伦理一区二区三区| 日韩一区二区三区在线观看| 国产精品国精产品一二| 精品欧美国产| 日本亚洲三级在线| 在线观看黄网址| 欧美一卡2卡三卡4卡5免费| 影院在线观看全集免费观看| 国产精品免费区二区三区观看| 99视频精品免费观看| 亚洲av无码一区二区三区人| 欧美特级限制片免费在线观看| 久草免费在线| 国产精品一区二区三区在线 | 日本少妇xxxx软件| 狠狠躁天天躁日日躁欧美| 国产精品影院在线| 91精品久久久久久久久久久久久| 中国成人一区| 国产中文字幕一区二区| 欧美无砖砖区免费| 中文字幕中文字幕在线十八区| 任你弄精品视频免费观看| 日韩少妇内射免费播放| 国产日产精品1区| 一区二区国产欧美| 国内外成人免费激情在线视频网站 | 5566中文字幕| 亚洲成人中文字幕| 粉嫩一区二区三区| 无码人妻精品一区二区三区99v| 成人免费视频视频| 中文字幕有码无码人妻av蜜桃| 久久国产精彩视频| 丝袜连裤袜欧美激情日韩| 日韩av在线中文| 亚洲成人第一页| 日日夜夜精品一区| 国产区二精品视| 麻豆精品久久精品色综合| 国产在线视频卡一卡二| 一区二区在线视频| 都市激情亚洲| 免费看污污网站| 亚洲妇女屁股眼交7| 蜜桃视频在线观看www社区 | 女人色偷偷aa久久天堂 | 亚洲天堂成人网| 欧美女同网站|