MySQL實戰45講學習筆記:第二講


一、重要的日志模塊:redo log

1、通過酒店掌櫃記賬思路刨析redo log工作原理

2、InnoDB 的 redo log 是固定大小的

 

只要賒賬記錄在了粉板上或寫了賬本上,之后即使掌櫃忘記了,
比如停業幾天,回復生意后依然可以通過賬本和粉板上的數據明確賒賬賬目

有了redo log,InnoDB就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失,這個能力成為crash-safe二、重要的日志模塊:binlog

二、重要的日志模塊binlog

1、redo和三點區別binlog

redo是物理的,binlog是邏輯的;現在由於redo是屬於InnoDB引擎,所以必須要有binlog,因為你可以使用別的引擎

 

2、binlog幾大模式

 

一般采用row,因為遇到時間,從庫可能會出現不一致的情況,但是row更新前后都有,會導致日志變大
最后2個參數,保證事務成功,日志必須落盤,這樣,數據庫crash后,就不會丟失某個事務的數據了

3、企業場景如何選擇binglog:

1、互聯網公司,使用MySQL的功能相對少(存儲過程、觸發器、函數) 

選擇默認的語句模式,Statement Level(默認) 

2、公司如果用到使用MySQL的特殊功能(存儲過程、觸發器、函數) 

則選擇Mixed模式 

3、公司如果用到使用MySQL的特殊功能(存儲過程、觸發器、函數)又希望數據最大化一直,此時最好選擇Row level模式

4、行模式和語句模式的區別

5、  ROW模式下binlog日志記錄效果

[root@db01]# mysqlbinlog --base64-output="decode-rows" --verbose mysql-bin.000248

/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;

/*!40019 SET @@session.max_insert_delayed_threads=0*/;

/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;

DELIMITER /*!*/;

# at 4

#160628 11:06:52 server id 1  end_log_pos 107   Start: binlog v 4, server v 5.5.49-log created 160628 11:06:52 at startup

# Warning: this binlog is either in use or was not closed properly.

ROLLBACK/*!*/;

# at 107

#160628 11:07:09 server id 1  end_log_pos 177   Query   thread_id=1     exec_time=0     error_code=0

SET TIMESTAMP=1467083229/*!*/;

SET @@session.pseudo_thread_id=1/*!*/;

SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;

SET @@session.sql_mode=0/*!*/;

SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;/*!\C utf8 *//*!*/;

SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;

SET @@session.lc_time_names=0/*!*/;

SET @@session.collation_database=DEFAULT/*!*/;

BEGIN

/*!*/;

# at 177

# at 223

#160628 11:07:09 server id 1  end_log_pos 223   Table_map: `oldboy`.`sc` mapped to number 33

#160628 11:07:09 server id 1  end_log_pos 785   Update_rows: table id 33 flags: STMT_END_F

### UPDATE `oldboy`.`sc`

### WHERE

###   @1=1

###   @2=1001

三、update 語句的執行流程圖

1、update語句的執行流程圖

圖中綠色框表示是在內粗執行,紅色框表示是在執行器中執行

2、 流程說明

1.首先客戶端通過tcp/ip發送一條sql語句到server層的SQL interface

2.SQL interface接到該請求后,先對該條語句進行解析,驗證權限是否匹配

3.驗證通過以后,分析器會對該語句分析,是否語法有錯誤等

4.接下來是優化器器生成相應的執行計划,選擇最優的執行計划

5.之后會是執行器根據執行計划執行這條語句。在這一步會去open table,如果該table上有MDL,則等待。如果沒有,則加在該表上加短暫的MDL(S),

(如果opend_table太大,表明open_table_cache太小。需要不停的去打開frm文件)

6.進入到引擎層,首先會去innodb_buffer_pool里的data dictionary(元數據信息)得到表信息

7.通過元數據信息,去lock info里查出是否會有相關的鎖信息,並把這條update語句需要的鎖信息寫入到lock info里(鎖這里還有待補充)

8.然后涉及到的老數據通過快照的方式存儲到innodb_buffer_pool里的undo page里,並且記錄undo log修改的redo

(如果data page里有就直接載入到undo page里,如果沒有,則需要去磁盤里取出相應page的數據,載入到undo page里)

9.在innodb_buffer_pool的data page做update操作。並把操作的物理數據頁修改記錄到redo log buffer里

由於update這個事務會涉及到多個頁面的修改,所以redo log buffer里會記錄多條頁面的修改信息。

因為group commit的原因,這次事務所產生的redo log buffer可能會跟隨其它事務一同flush並且sync到磁盤上

10.同時修改的信息,會按照event的格式,記錄到binlog_cache中。(這里注意binlog_cache_size是transaction級別的,不是session級別的參數,

一旦commit之后,dump線程會從binlog_cache里把event主動發送給slave的I/O線程)

11.之后把這條sql,需要在二級索引上做的修改,寫入到change buffer page,等到下次有其他sql需要讀取該二級索引時,再去與二級索引做merge
(隨機I/O變為順序I/O,但是由於現在的磁盤都是SSD,所以對於尋址來說,隨機I/O和順序I/O差距不大)

12.此時update語句已經完成,需要commit或者rollback。這里討論commit的情況,並且雙1

13.commit操作,由於存儲引擎層與server層之間采用的是內部XA(保證兩個事務的一致性,這里主要保證redo log和binlog的原子性),
所以提交分為prepare階段與commit階段

14.prepare階段,將事務的xid寫入,將binlog_cache里的進行flush以及sync操作(大事務的話這步非常耗時)

15.commit階段,由於之前該事務產生的redo log已經sync到磁盤了。所以這步只是在redo log里標記commit

16.當binlog和redo log都已經落盤以后,如果觸發了刷新臟頁的操作,先把該臟頁復制到doublewrite buffer里,把doublewrite buffer里的刷新到共享表空間,然后才是通過page cleaner線程把臟頁寫入到磁盤中

其實在實現上5是調用了6的過程了的,所以是一回事。MySQL server 層和InnoDB層都保存了表結構,所以有書上描述時會拆開說。

四、兩階段提交

1、保證數據庫的一致性:

必須要保證2份日志一致,使用的2階段式提交;其實感覺像事務,不是成功就是失敗,不能讓中間環節出現,也就是一個成功,一個失敗
如果有一天mysql只有InnoDB引擎了,有redo來實現復制,那么感覺oracle的DG就誕生了,物理的速度也將遠超邏輯的,畢竟只記錄了改動向量

 2、binlog能不能去掉?

老師,今天MYSQL第二講中提到binlog和redo log, 我感覺binlog很多余,按理是不是只要redo log就夠了?[費解] 

一個原因是,redolog只有InnoDB有,別的引擎沒有。
另一個原因是,redolog是循環寫的,不持久保存,binlog的“歸檔”這個功能,redolog是不具備的。

3、運維同學的實戰疑惑

老師您好,我之前是做運維的,通過binlog恢復誤操作的數據,但是實際上,我們會后知后覺,誤刪除一段時間了,才發現誤刪除,此時,我把之前誤刪除的binlog導入,再把誤刪除之后binlog導入,會出現問題,比如主鍵沖突,而且binlog導數據,不同模式下時間也有不同,但是一般都是row模式,時間還是很久,有沒什么方式,時間短且數據一致性強的方式

其實恢復數據只能恢復到誤刪之前到一刻,
誤刪之后的,不能只靠binlog來做,因為業務邏輯可能因為誤刪操作的行為,插入了邏輯錯誤的語句,
所以之后的,跟業務一起,從業務快速補數據的。只靠binlog補出來的往往不完整

4、怎樣讓數據庫恢復到半個月內任意一秒的狀態?

1 prepare階段

2 寫binlog

3 commit

當在2之前崩潰時

重啟恢復:后發現沒有commit,回滾。備份恢復:沒有binlog 。一致

當在3之前崩潰

重啟恢復:雖沒有commit,但滿足prepare和binlog完整,所以重啟后會自動commit。備份:有binlog. 一致

五、思考題(同學們的經典留言)

1、Jason同學

備份時間周期的長短,感覺有2個方便

首先,是恢復數據丟失的時間,既然需要恢復,肯定是數據丟失了。如果一天一備份的話,只要找到這天的全備,加入這天某段時間的binlog來恢復,如果一周一備份,假設是周一,而你要恢復的數據是周日某個時間點,那就,需要全備+周一到周日某個時間點的全部binlog用來恢復,時間相比前者需要增加很多;看業務能忍受的程度

其次,是數據庫丟失,如果一周一備份的話,需要確保整個一周的binlog都完好無損,否則將無法恢復;而一天一備,只要保證這天的binlog都完好無損;當然這個可以通過校驗,或者冗余等技術來實現,相比之下,上面那點更重要

2、justd同學的形象比喻

1、一個完整的交易過程:

1、賬本記上 賣一瓶可樂(redo log為 prepare狀態),
2、然后收錢放入錢箱(bin log記錄)
3、然后回過頭在賬本上打個勾(redo log置為commit)表示一筆交易結束。

2、如果收錢時交易被打斷

1、回過頭來整理此次交易,發現只有記賬沒有收錢,則交易失敗,刪掉賬本上的記錄(回滾);
2、如果收了錢后被終止,然后回過頭發現賬本有記錄(prepare)而且錢箱有本次收入(bin log),則繼續完善賬本(commit),本次交易有效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM