當前位置:首頁 » 硬碟大全 » binlog准實時清理緩存
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

binlog准實時清理緩存

發布時間: 2023-04-29 04:32:16

1. 主從復制主資料庫發生宕機,binlog文件還存在嗎

存在。Mysql主從復制是一個非同步的復制過程,底層是基於Mysql數敬旦衫據庫自帶的二進制日誌 binlog 功能。簡單的說,就是一台或多台MySQL資料庫遲凱(slave,即從庫)從另一台MySQL資料庫(master,即主庫)進行日誌的復制,然後再解析日誌並應用到自身,最終實現 從庫 的數據和亮腔 主庫 的數據保持一致。

2. 資料庫篇:mysql日誌類型之 redo、undo、binlog

可以說mysql的多數特性都是圍繞日誌文件實現,而其中最重要的有以下三種

innodb 為了提高磁碟I/O讀寫性能,存在一個 buffer pool 的內存空間,數據頁讀入會緩存到 buffer pool,事務的提交則實時更新到 buffer pool,而不實時同步到磁碟(innodb 是按 16KB 一頁同步的,一事務可涉及多個數據頁,實時同步會造成浪費,隨機I/O)。事務暫存在內存,則存在一致性問題,為了解決系統崩潰,保證事務的持久性,我們只需把事務對應的 redo 日誌持久化到磁碟即可(redo 日誌佔用空間小,順序寫入磁碟,順序I/O)

sql 語句在執行的時候,可能會修改多個頁面,還會更新聚簇索引和二級索引的頁面,過程產生的redo會被分割成多個不可分割的組(Mini-Transaction)。MTR怎麼理解呢?如一條 insert 語句可能會使得頁分裂,新建葉子節點,原先頁的數據需要復制到新數據頁里,然後將新記錄插入,再添加一個目錄項指向新建的頁子。這對應多條 redo 日誌,它們需要在原子性的 MTR 內完成

MTR 產生的 redo 日誌先會被復制到一個 log buffer 里(類似 buffer pool)。而同步到磁碟的時機如下:

事務需要保證原子性,也是說事務中的操作要麼全部完成,要麼什麼也不做。如果事務執行到一半,出錯了怎麼辦-回滾。但是怎麼回滾呢,靠 undo 日誌。undo 日誌就是我們執行sql的逆操作

binlog有三種格式:Statement、Row以及Mixed。

redolog 中的事務如果經歷了二階段提交中的prepare階段,則會打上 prepare 標識,如果經歷commit階段,則會打上commit標識(此時redolog和binlog均已落盤)。崩潰恢復邏輯如下:

3. MySQL清理binlog日誌的方法

永久生效:修改mysql的配置文件my.cnf,添加binlog過期時間的配置項: expire_logs_days=30 ,然後重啟mysql,這個有個致命的缺點就是需要重啟mysql。

臨時生效:進入mysql,用以絕仿卜下命令設置全局的參數: set global expire_logs_days=30 ;

(上面的數字30是保留30天的意思。)

可以直接刪除 binlog 文件,但是可以通過 mysql 提供的工具來刪除更大穗安全,因為 purge 會更新 mysql-bin.index 中的條目,而直接刪除的話, mysql-bin.index 文件不會更新。 mysql-bin.index 的作用是加快查找 binlog 文件的速度。

命令查看 binlog 文件

刪除舉例:並穗

4. mysql 核心內容-上

1、SQL語句執行流程

MySQL大體上可分為Server層和存儲引擎層兩部分。

Server層:

連接器:TCP握手後伺服器來驗證登陸用戶身份,A用戶創建連接後,管理員對A用戶許可權修改了也不會影響到已經創建的鏈接許可權,必須重新登陸。

查詢緩存:查詢後的結果存儲位置,MySQL8.0版本以後已經取消,因為查詢緩存失效太頻繁,得不償失。

分析器:根據語法規則,判斷你輸入的這個SQL語句是否滿足MySQL語法。

優化器:多種執行策略可實現目標,系統自動選擇最優進行執行。

執行器:判斷是否有許可權,將最終任務提交到存儲引擎。

存儲引擎層

負責數據的存儲和提取。其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多個存儲引擎。現在最常用的存儲引擎是InnoDB,它從MySQL 5.5.5版本開始成為了默認存儲引擎(經常用的也是這個)。

SQL執行順序

2、BinLog、RedoLog、UndoLog

BinLog

BinLog是記錄所有資料庫表結構變更(例如create、alter table)以及表數據修改(insert、update、delete)的二進制日誌,主從資料庫同步用到的都是BinLog文件。BinLog日誌文件有三種模式。

STATEMENT 模式

內容:binlog 記錄可能引起數據變更的 sql 語句

優勢:該模式下,因為沒有記錄實際的數據,所以日誌量很少 IO 都消耗很低,性能是最優的

劣勢:但有些操作並不是確定的,比如 uuid() 函數會隨機產生唯一標識,當依賴 binlog 回放時,該操作生成的數據與原數據必然是不同的,此時可能造成無法預料的後果。

ROW 模式

內容:在該模式下,binlog 會記錄每次操作的源數據與修改後的目標數據,StreamSets就要求該模式。

優勢:可以絕對精準的還原,從而保證了數據的安全與可靠,並且復制和數據恢復過程可以是並發進行的

劣勢:缺點在於 binlog 體積會非常大,同時,對於修改記錄多、欄位長度大的操作來說,記錄時性能消耗會很嚴重。閱讀的時候也需要特殊指令來進行讀取數據。

MIXED 模式

內容:是對上述STATEMENT 跟 ROW 兩種模式的混合使用。

細節:對於絕大部分操作,都是使用 STATEMENT 來進行 binlog 沒有記錄,只有以下操作使用 ROW 來實現:表的存儲引擎為 NDB,使用了uuid() 等不確定函數,使用了 insert delay 語句,使用了臨時表

主從同步流程:

1、主節點必須啟用二進制日誌,記錄任何修改了資料庫數據的事件。

2、從節點開啟一個線程(I/O Thread)把自己扮演成 mysql 的客戶端,通過 mysql 協議,請求主節點的二進制日誌文件中的事件 。

3、主節點啟動一個線程(mp Thread),檢查自己二進制日誌中的事件,跟對方請求的位置對比,如果不帶請求位置參數,則主節點就會從第一個日誌文件中的第一個事件一個一個發送給從節點。

4、從節點接收到主節點發送過來的數據把它放置到中繼日誌(Relay log)文件中。並記錄該次請求到主節點的具體哪一個二進制日誌文件內部的哪一個位置(主節點中的二進制文件會有多個)。

5、從節點啟動另外一個線程(sql Thread ),把 Relay log 中的事件讀取出來,並在本地再執行一次。

mysql默認的復制方式是非同步的,並且復制的時候是有並行復制能力的。主庫把日誌發送給從庫後不管了,這樣會產生一個問題就是假設主庫掛了,從庫處理失敗了,這時候從庫升為主庫後,日誌就丟失了。由此產生兩個概念。

全同步復制

主庫寫入binlog後強制同步日誌到從庫,所有的從庫都執行完成後才返回給客戶端,但是很顯然這個方式的話性能會受到嚴重影響。

半同步復制

半同步復制的邏輯是這樣,從庫寫入日誌成功後返回ACK確認給主庫,主庫收到至少一個從庫的確認就認為寫操作完成。

還可以延伸到由於主從配置不一樣、主庫大事務、從庫壓力過大、網路震盪等造成主備延遲,如何避免這個問題?主備切換的時候用可靠性優先原則還是可用性優先原則?如何判斷主庫Crash了?互為主備的情況下如何避免主備循環復制?被刪庫跑路了如何正確恢復?( o )… 感覺越來越扯到DBA的活兒上去了。

RedoLog

可以先通過下面demo理解:

飯點記賬可以把賬單寫在賬本上也可以寫在粉板上。有人賒賬或者還賬的話,一般有兩種做法:

1、直接把賬本翻出來,把這次賒的賬加上去或者扣除掉。

2、先在粉板上記下這次的賬,等打烊以後再把賬本翻出來核算。

生意忙時選後者,因為前者太麻煩了。得在密密麻麻的記錄中找到這個人的賒賬總額信息,找到之後再拿出算盤計算,最後再將結果寫回到賬本上。

同樣在MySQL中如果每一次的更新操作都需要寫進磁碟,然後磁碟也要找到對應的那條記錄,然後再更新,整個過程IO成本、查找成本都很高。而粉板和賬本配合的整個過程就是MySQL用到的是Write-Ahead Logging 技術,它的關鍵點就是先寫日誌,再寫磁碟。此時賬本 = BinLog,粉板 = RedoLog。

1、 記錄更新時,InnoDB引擎就會先把記錄寫到RedoLog(粉板)裡面,並更新內存。同時,InnoDB引擎會在空閑時將這個操作記錄更新到磁碟裡面。

2、 如果更新太多RedoLog處理不了的時候,需先將RedoLog部分數據寫到磁碟,然後擦除RedoLog部分數據。RedoLog類似轉盤。

RedoLog有write pos 跟checkpoint

write pos :是當前記錄的位置,一邊寫一邊後移,寫到第3號文件末尾後就回到0號文件開頭。

check point:是當前要擦除的位置,也是往後推移並且循環的,擦除記錄前要把記錄更新到數據文件。

write pos和check point之間的是粉板上還空著的部分,可以用來記錄新的操作。如果write pos追上checkpoint,表示粉板滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。

有了redo log,InnoDB就可以保證即使資料庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe。 redolog兩階段提交:為了讓binlog跟redolog兩份日誌之間的邏輯一致。提交流程大致如下:

1 prepare階段 --> 2 寫binlog --> 3 commit

當在2之前崩潰時,重啟恢復後發現沒有commit,回滾。備份恢復:沒有binlog 。一致

當在3之前崩潰時,重啟恢復發現雖沒有commit,但滿足prepare和binlog完整,所以重啟後會自動commit。備份:有binlog. 一致

binlog跟redolog區別:

redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。

redo log是物理日誌,記錄的是在某個數據頁上做了什麼修改;binlog是邏輯日誌,記錄的是這個語句的原始邏輯,比如給ID=2這一行的c欄位加1。

redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的。追加寫是指binlog文件寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌。

UndoLog

UndoLog 一般是邏輯日誌,主要分為兩種:

insert undo log

代表事務在insert新記錄時產生的undo log, 只在事務回滾時需要,並且在事務提交後可以被立即丟棄

update undo log

事務在進行update或delete時產生的undo log; 不僅在事務回滾時需要,在快照讀時也需要;所以不能隨便刪除,只有在快速讀或事務回滾不涉及該日誌時,對應的日誌才會被purge線程統一清除

3、MySQL中的索引

索引的常見模型有哈希表、有序數組和搜索樹。

哈希表:一種以KV存儲數據的結構,只適合等值查詢,不適合范圍查詢。

有序數組:只適用於靜態存儲引擎,涉及到插入的時候比較麻煩。可以參考Java中的ArrayList。

搜索樹:按照數據結構中的二叉樹來存儲數據,不過此時是N叉樹(B+樹)。廣泛應用在存儲引擎層中。

B+樹比B樹優勢在於:

B+ 樹非葉子節點存儲的只是索引,可以存儲的更多。B+樹比B樹更加矮胖,IO次數更少。

B+ 樹葉子節點前後管理,更加方便范圍查詢。同時結果都在葉子節點,查詢效率穩定。

B+樹中更有利於對數據掃描,可以避免B樹的回溯掃描。

索引的優點:

1、唯一索引可以保證每一行數據的唯一性

2、提高查詢速度

3、加速表與表的連接

4、顯著的減少查詢中分組和排序的時間

5、通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

索引的缺點:

1、創建跟維護都需要耗時

2、創建索引時,需要對表加鎖,在鎖表的同時,可能會影響到其他的數據操作

3、 索引需要磁碟的空間進行存儲,磁碟佔用也很快。

4、當對表中的數據進行CRUD的時,也會觸發索引的維護,而維護索引需要時間,可能會降低數據操作性能

索引設計的原則不應該:

1、索引不是越多越好。索引太多,維護索引需要時間跟空間。

2、 頻繁更新的數據,不宜建索引。

3、數據量小的表沒必要建立索引。

應該:

1、重復率小的列建議生成索引。因為重復數據少,索引樹查詢更有效率,等價基數越大越好。

2、數據具有唯一性,建議生成唯一性索引。在資料庫的層面,保證數據正確性

3、頻繁group by、order by的列建議生成索引。可以大幅提高分組和排序效率

4、經常用於查詢條件的欄位建議生成索引。通過索引查詢,速度更快

索引失效的場景

1、模糊搜索:左模糊或全模糊都會導致索引失效,比如'%a'和'%a%'。但是右模糊是可以利用索引的,比如'a%' 。

2、隱式類型轉換:比如select * from t where name = xxx , name是字元串類型,但是沒有加引號,所以是由MySQL隱式轉換的,所以會讓索引失效 3、當語句中帶有or的時候:比如select * from t where name=『sw』 or age=14

4、不符合聯合索引的最左前綴匹配:(A,B,C)的聯合索引,你只where了C或B或只有B,C

關於索引的知識點:

主鍵索引:主鍵索引的葉子節點存的是整行數據信息。在InnoDB里,主鍵索引也被稱為聚簇索引(clustered index)。主鍵自增是無法保證完全自增的哦,遇到唯一鍵沖突、事務回滾等都可能導致不連續。

唯一索引:以唯一列生成的索引,該列不允許有重復值,但允許有空值(NULL)

普通索引跟唯一索引查詢性能:InnoDB的數據是按數據頁為單位來讀寫的,默認每頁16KB,因此這兩種索引查詢數據性能差別微乎其微。

change buffer:普通索引用在更新過程的加速,更新的欄位如果在緩存中,如果是普通索引則直接更新即可。如果是唯一索引需要將所有數據讀入內存來確保不違背唯一性,所以盡量用普通索引。

非主鍵索引:非主鍵索引的葉子節點內容是主鍵的值。在InnoDB里,非主鍵索引也被稱為二級索引(secondary index)

回表:先通過資料庫索引掃描出數據所在的行,再通過行主鍵id取出索引中未提供的數據,即基於非主鍵索引的查詢需要多掃描一棵索引樹。

覆蓋索引:如果一個索引包含(或者說覆蓋)所有需要查詢的欄位的值,我們就稱之為覆蓋索引。

聯合索引:相對單列索引,組合索引是用多個列組合構建的索引,一次性最多聯合16個。

最左前綴原則:對多個欄位同時建立的組合索引(有順序,ABC,ACB是完全不同的兩種聯合索引) 以聯合索引(a,b,c)為例,建立這樣的索引相當於建立了索引a、ab、abc三個索引。另外組合索引實際還是一個索引,並非真的創建了多個索引,只是產生的效果等價於產生多個索引。

索引下推:MySQL 5.6引入了索引下推優化,可以在索引遍歷過程中,對索引中包含的欄位先做判斷,過濾掉不符合條件的記錄,減少回表字數。

索引維護:B+樹為了維護索引有序性涉及到頁分裂跟頁合並。增刪數據時需考慮頁空間利用率。

自增主鍵:一般會建立與業務無關的自增主鍵,不會觸發葉子節點分裂。

延遲關聯:通過使用覆蓋索引查詢返回需要的主鍵,再根據主鍵關聯原表獲得需要的數據。

InnoDB存儲: * .frm文件是一份定義文件,也就是定義資料庫表是一張怎麼樣的表。*.ibd文件則是該表的索引,數據存儲文件,既該表的所有索引樹,所有行記錄數據都存儲在該文件中。

MyISAM存儲:* .frm文件是一份定義文件,也就是定義資料庫表是一張怎麼樣的表。* .MYD文件是MyISAM存儲引擎表的所有行數據的文件。* .MYI文件存放的是MyISAM存儲引擎表的索引相關數據的文件。MyISAM引擎下,表數據和表索引數據是分開存儲的。

MyISAM查詢:在MyISAM下,主鍵索引和輔助鍵索引都屬於非聚簇索引。查詢不管是走主鍵索引,還是非主鍵索引,在葉子結點得到的都是目的數據的地址,還需要通過該地址,才能在數據文件中找到目的數據。

PS:InnoDB支持聚簇索引,MyISAM不支持聚簇索引

4、SQL事務隔離級別

ACID的四個特性

原子性(Atomicity):把多個操作放到一個事務中,保證這些操作要麼都成功,要麼都不成功

一致性(Consistency):理解成一串對數據進行操作的程序執行下來,不會對數據產生不好的影響,比如憑空產生,或消失

隔離性(Isolation,又稱獨立性):隔離性的意思就是多個事務之間互相不幹擾,即使是並發事務的情況下,他們只是兩個並發執行沒有交集,互不影響的東西;當然實現中,也不一定需要這么完整隔離性,即不一定需要這么的互不幹擾,有時候還是允許有部分干擾的。所以MySQL可以支持4種事務隔離性

持久性(Durability):當某個操作操作完畢了,那麼結果就是這樣了,並且這個操作會持久化到日誌記錄中

PS:ACID中C與CAP定理中C的區別

ACID的C著重強調單資料庫事務操作時,要保證數據的完整和正確性,數據不會憑空消失跟增加。CAP 理論中的C指的是對一個數據多個備份的讀寫一致性

事務操作可能會出現的數據問題

1、臟讀(dirty read):B事務更改數據還未提交,A事務已經看到並且用了。B事務如果回滾,則A事務做錯了

2、 不可重復讀(non-repeatable read):不可重復讀的重點是修改: 同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了,只需要鎖住滿足條件的記錄

3、 幻讀(phantom read):事務A先修改了某個表的所有紀錄的狀態欄位為已處理,未提交;事務B也在此時新增了一條未處理的記錄,並提交了;事務A隨後查詢記錄,卻發現有一條記錄是未處理的造成幻讀現象,幻讀僅專指新插入的行。幻讀會造成語義上的問題跟數據一致性問題。

4、 在可重復讀RR隔離級別下,普通查詢是快照讀,是不會看到別的事務插入的數據的。因此,幻讀在當前讀下才會出現。要用間隙鎖解決此問題。

在說隔離級別之前,你首先要知道,你隔離得越嚴實,效率就會越低。因此很多時候,我們都要在二者之間尋找一個平衡點。SQL標準的事務隔離級別由低到高如下: 上圖從上到下的模式會導致系統的並行性能依次降低,安全性依次提高。

讀未提交:別人改數據的事務尚未提交,我在我的事務中也能讀到。

讀已提交(Oracle默認):別人改數據的事務已經提交,我在我的事務中才能讀到。

可重復讀(MySQL默認):別人改數據的事務已經提交,我在我的事務中也不去讀,以此保證重復讀一致性。

串列:我的事務尚未提交,別人就別想改數據。

標准跟實現:上面都是關於事務的標准,但是每一種資料庫都有不同的實現,比如MySQL InnDB 默認為RR級別,但是不會出現幻讀。因為當事務A更新了所有記錄的某個欄位,此時事務A會獲得對這個表的表鎖,因為事務A還沒有提交,所以事務A獲得的鎖沒有釋放,此時事務B在該表插入新記錄,會因為無法獲得該表的鎖,則導致插入操作被阻塞。只有事務A提交了事務後,釋放了鎖,事務B才能進行接下去的操作。所以可以說 MySQL的RR級別的隔離是已經實現解決了臟讀,不可重復讀和幻讀的。

5、MySQL中的鎖

無論是Java的並發編程還是資料庫的並發操作都會涉及到鎖,研發人員引入了悲觀鎖跟樂觀鎖這樣一種鎖的設計思想。

悲觀鎖:

優點:適合在寫多讀少的並發環境中使用,雖然無法維持非常高的性能,但是在樂觀鎖無法提更好的性能前提下,可以做到數據的安全性

缺點:加鎖會增加系統開銷,雖然能保證數據的安全,但數據處理吞吐量低,不適合在讀書寫少的場合下使用

樂觀鎖:

優點:在讀多寫少的並發場景下,可以避免資料庫加鎖的開銷,提高DAO層的響應性能,很多情況下ORM工具都有帶有樂觀鎖的實現,所以這些方法不一定需要我們人為的去實現。

缺點:在寫多讀少的並發場景下,即在寫操作競爭激烈的情況下,會導致CAS多次重試,沖突頻率過高,導致開銷比悲觀鎖更高。

實現:資料庫層面的樂觀鎖其實跟CAS思想類似, 通數據版本號或者時間戳也可以實現。

資料庫並發場景主要有三種:

讀-讀:不存在任何問題,也不需要並發控制

讀-寫:有隔離性問題,可能遇到臟讀,幻讀,不可重復讀

寫-寫:可能存更新丟失問題,比如第一類更新丟失,第二類更新丟失

兩類更新丟失問題:

第一類更新丟失:事務A的事務回滾覆蓋了事務B已提交的結果 第二類更新丟失:事務A的提交覆蓋了事務B已提交的結果

為了合理貫徹落實鎖的思想,MySQL中引入了雜七雜八的各種鎖:

鎖分類

MySQL支持三種層級的鎖定,分別為

表級鎖定

MySQL中鎖定粒度最大的一種鎖,最常使用的MYISAM與INNODB都支持表級鎖定。

頁級鎖定

是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖,表級鎖速度快,但沖突多,行級沖突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。

行級鎖定

Mysql中鎖定粒度最細的一種鎖,表示只針對當前操作的行進行加鎖。行級鎖能大大減少資料庫操作的沖突。其加鎖粒度最小,但加鎖的開銷也最大行級鎖不一定比表級鎖要好:鎖的粒度越細,代價越高,相比表級鎖在表的頭部直接加鎖,行級鎖還要掃描找到對應的行對其上鎖,這樣的代價其實是比較高的,所以表鎖和行鎖各有所長。

MyISAM中的鎖

雖然MySQL支持表,頁,行三級鎖定,但MyISAM存儲引擎只支持表鎖。所以MyISAM的加鎖相對比較開銷低,但數據操作的並發性能相對就不高。但如果寫操作都是尾插入,那還是可以支持一定程度的讀寫並發

從MyISAM所支持的鎖中也可以看出,MyISAM是一個支持讀讀並發,但不支持通用讀寫並發,寫寫並發的資料庫引擎,所以它更適合用於讀多寫少的應用場合,一般工程中也用的較少。

InnoDB中的鎖

該模式下支持的鎖實在是太多了,具體如下:

共享鎖和排他鎖 (Shared and Exclusive Locks)

意向鎖(Intention Locks)

記錄鎖(Record Locks)

間隙鎖(Gap Locks)

臨鍵鎖 (Next-Key Locks)

插入意向鎖(Insert Intention Locks)

主鍵自增鎖 (AUTO-INC Locks)

空間索引斷言鎖(Predicate Locks for Spatial Indexes)

舉個栗子,比如行鎖里的共享鎖跟排它鎖:lock in share modle 共享讀鎖:

為了確保自己查到的數據沒有被其他的事務正在修改,也就是說確保查到的數據是最新的數據,並且不允許其他人來修改數據。但是自己不一定能夠修改數據,因為有可能其他的事務也對這些數據使用了 in share mode 的方式上了S 鎖。如果不及時的commit 或者rollback 也可能會造成大量的事務等待。

for update排它寫鎖:

為了讓自己查到的數據確保是最新數據,並且查到後的數據只允許自己來修改的時候,需要用到for update。相當於一個 update 語句。在業務繁忙的情況下,如果事務沒有及時的commit或者rollback 可能會造成其他事務長時間的等待,從而影響資料庫的並發使用效率。

Gap Lock間隙鎖:

1、行鎖只能鎖住行,如果在記錄之間的間隙插入數據就無法解決了,因此MySQL引入了間隙鎖(Gap Lock)。間隙鎖是左右開區間。間隙鎖之間不會沖突。

2、間隙鎖和行鎖合稱NextKeyLock,每個NextKeyLock是前開後閉區間。

間隙鎖加鎖原則(學完忘那種):

1、加鎖的基本單位是 NextKeyLock,是前開後閉區間。

2、查找過程中訪問到的對象才會加鎖。

3、索引上的等值查詢,給唯一索引加鎖的時候,NextKeyLock退化為行鎖。

4、索引上的等值查詢,向右遍歷時且最後一個值不滿足等值條件的時候,NextKeyLock退化為間隙鎖。

5、唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。

5. 如何保證資料庫緩存的最終一致性

對於互聯網業務來說,傳統的直接訪問資料庫方式,主要通過數據分片、一主多從等方式來扛住讀寫流量,但隨著數據量的積累和流量的激增,僅依賴資料庫來承接所有流量,不僅成本高、效率低、而且還伴隨著穩定性降低的風險。

鑒於大部分業務通常是讀多寫少(讀取頻率遠遠高於更新頻率),甚至存在讀操作數量高出寫操作多個數量級的情況。因此, 在架構設計中,常採用增加緩存層來提高系統的響應能力 ,提升數據讀寫性能、減少資料庫訪問壓力,從而提升業務的穩定性和訪問體驗。

根據 CAP 原理,分布式系統在可用性、一致性和分區容錯性上無法兼得,通常由於分區容錯無法避免,所以一致性和可用性難以同時成立。對於緩存系統來說, 如何保證其數據一致性是一個在應用緩存的同時不得不解決的問題 。

需要明確的是,緩存系統的數據一致性通常包括持久化層和緩存層的一致性、以及多級緩存之間的一致性,這里我們僅討論前者。持久化層和緩存層的一致性問題也通常被稱為雙寫一致性問題,「雙寫」意為數據既在資料庫中保存一份,也在緩存中保存一份。

對於一致性來說,包含強一致性和弱一致性 ,強一致性保證寫入後立即可以讀取,弱一致性則不保證立即可以讀取寫入後的值,而是盡可能的保證在經過一定時間後可以讀取到,在弱一致性中應用最為廣泛的模型則是最終一致性模型,即保證在一定時間之後寫入和讀取達到一致的狀態。對於應用緩存的大部分場景來說,追求的則是最終一致性,少部分對數據一致性要求極高的場景則會追求強一致性。

為了達到最終一致性,針對不同的場景,業界逐步形成了下面這幾種應用緩存的策略。


1

Cache-Aside


Cache-Aside 意為旁路緩存模式,是應用最為廣泛的一種緩存策略。下面的圖示展示了它的讀寫流程,來看看它是如何保證最終一致性的。在讀請求中,首先請求緩存,若緩存命中(cache hit),則直接返回緩存中的數據;若緩存未命中(cache miss),則查詢資料庫並將查詢結果更新至緩存,然後返回查詢出的數據(demand-filled look-aside )。在寫請求中,先更新資料庫,再刪除緩存(write-invalidate)。


1、為什麼刪除緩存,而不是更新緩存?

在 Cache-Aside 中,對於讀請求的處理比較容易理解,但在寫請求中,可能會有讀者提出疑問,為什麼要刪除緩存,而不是更新緩存?站在符合直覺的角度來看,更新緩存是一個容易被理解的方案,但站在性能和安全的角度,更新緩存則可能會導致一些不好的後果。

首先是性能 ,當該緩存對應的結果需要消耗大量的計算過程才能得到時,比如需要訪問多張資料庫表並聯合計算,那麼在寫操作中更新緩存的動作將會是一筆不小的開銷。同時,當寫操作較多時,可能也會存在剛更新的緩存還沒有被讀取到,又再次被更新的情況(這常被稱為緩存擾動),顯然,這樣的更新是白白消耗機器性能的,會導致緩存利用率不高。

而等到讀請求未命中緩存時再去更新,也符合懶載入的思路,需要時再進行計算。刪除緩存的操作不僅是冪等的,可以在發生異常時重試,而且寫-刪除和讀-更新在語義上更加對稱。

其次是安全 ,在並發場景下,在寫請求中更新緩存可能會引發數據的不一致問題。參考下面的圖示,若存在兩個來自不同線程的寫請求,首先來自線程 1 的寫請求更新了資料庫(step 1),接著來自線程 2 的寫請求再次更新了資料庫(step 3),但由於網路延遲等原因,線程 1 可能會晚於線程 2 更新緩存(step 4 晚於 step 3),那麼這樣便會導致最終寫入資料庫的結果是來自線程 2 的新值,寫入緩存的結果是來自線程 1 的舊值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。


2、為什麼先更新資料庫,而不是先刪除緩存?

另外,有讀者也會對更新資料庫和刪除緩存的時序產生疑問,那麼為什麼不先刪除緩存,再更新資料庫呢?在單線程下,這種方案看似具有一定合理性,這種合理性體現在刪除緩存成功。

但更新資料庫失敗的場景下,盡管緩存被刪除了,下次讀操作時,仍能將正確的數據寫回緩存,相對於 Cache-Aside 中更新資料庫成功,刪除緩存失敗的場景來說,先刪除緩存的方案似乎更合理一些。那麼,先刪除緩存有什麼問題呢?

問題仍然出現在並發場景下,首先來自線程 1 的寫請求刪除了緩存(step 1),接著來自線程 2 的讀請求由於緩存的刪除導致緩存未命中,根據 Cache-Aside 模式,線程 2 繼而查詢資料庫(step 2),但由於寫請求通常慢於讀請求,線程 1 更新資料庫的操作可能會晚於線程 2 查詢資料庫後更新緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存的結果是來自線程 2 中查詢到的舊值,而寫入資料庫的結果是來自線程 1 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存( step 5 ),讀取到的便是舊值。


另外,先刪除緩存,由於緩存中數據缺失,加劇資料庫的請求壓力,可能會增大緩存穿透出現的概率。

3、如果選擇先刪除緩存,再更新資料庫,那如何解決一致性問題呢?

為了避免「先刪除緩存,再更新資料庫」這一方案在讀寫並發時可能帶來的緩存臟數據,業界又提出了延時雙刪的策略,即在更新資料庫之後,延遲一段時間再次刪除緩存,為了保證第二次刪除緩存的時間點在讀請求更新緩存之後,這個延遲時間的經驗值通常應稍大於業務中讀請求的耗時。

延遲的實現可以在代碼中 sleep 或採用延遲隊列。顯而易見的是,無論這個值如何預估,都很難和讀請求的完成時間點准確銜接,這也是延時雙刪被詬病的主要原因。


4、那麼 Cache-Aside 存在數據不一致的可能嗎?

在 Cache-Aside 中,也存在數據不一致的可能性。在下面的讀寫並發場景下,首先來自線程 1 的讀請求在未命中緩存的情況下查詢資料庫(step 1),接著來自線程 2 的寫請求更新資料庫(step 2),但由於一些極端原因,線程 1 中讀請求的更新緩存操作晚於線程 2 中寫請求的刪除緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存中的是來自線程 1 的舊值,而寫入資料庫中的是來自線程 2 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。

這種場景的出現,不僅需要緩存失效且讀寫並發執行,而且還需要讀請求查詢資料庫的執行早於寫請求更新資料庫,同時讀請求的執行完成晚於寫請求。足以見得,這種 不一致場景產生的條件非常嚴格,在實際的生產中出現的可能性較小 。


除此之外,在並發環境下,Cache-Aside 中也存在讀請求命中緩存的時間點在寫請求更新資料庫之後,刪除緩存之前,這樣也會導致讀請求查詢到的緩存落後於資料庫的情況。


雖然在下一次讀請求中,緩存會被更新,但如果業務層面對這種情況的容忍度較低,那麼可以採用加鎖在寫請求中保證「更新資料庫&刪除緩存」的串列執行為原子性操作(同理也可對讀請求中緩存的更新加鎖)。 加鎖勢必會導致吞吐量的下降,故採取加鎖的方案應該對性能的損耗有所預期。


2

補償機制


我們在上面提到了,在 Cache-Aside 中可能存在更新資料庫成功,但刪除緩存失敗的場景,如果發生這種情況,那麼便會導致緩存中的數據落後於資料庫,產生數據的不一致的問題。

其實,不僅 Cache-Aside 存在這樣的問題,在延時雙刪等策略中也存在這樣的問題。針對可能出現的刪除失敗問題,目前業界主要有以下幾種補償機制。

1、刪除重試機制

由於同步重試刪除在性能上會影響吞吐量,所以常通過引入消息隊列,將刪除失敗的緩存對應的 key 放入消息隊列中,在對應的消費者中獲取刪除失敗的 key ,非同步重試刪除。這種方法在實現上相對簡單,但由於刪除失敗後的邏輯需要基於業務代碼的 trigger 來觸發 ,對業務代碼具有一定入侵性。


鑒於上述方案對業務代碼具有一定入侵性,所以需要一種更加優雅的解決方案,讓緩存刪除失敗的補償機制運行在背後,盡量少的耦合於業務代碼。一個簡單的思路是通過後台任務使用更新時間戳或者版本作為對比獲取資料庫的增量數據更新至緩存中,這種方式在小規模數據的場景可以起到一定作用,但其擴展性、穩定性都有所欠缺。

一個相對成熟的方案是基於 MySQL 資料庫增量日誌進行解析和消費,這里較為流行的是阿里巴巴開源的作為 MySQL binlog 增量獲取和解析的組件 canal(類似的開源組件還有 Maxwell、Databus 等)。

canal sever 模擬 MySQL slave 的交互協議,偽裝為 MySQL slave,向 MySQL master 發送 mp 協議,MySQL master 收到 mp 請求,開始推送 binary log 給 slave (即 canal sever ),canal sever 解析 binary log 對象(原始為 byte 流),可由 canal client 拉取進行消費,同時 canal server 也默認支持將變更記錄投遞到 MQ 系統中,主動推送給其他系統進行消費。

在 ack 機制的加持下,不管是推送還是拉取,都可以有效的保證數據按照預期被消費。當前版本的 canal 支持的 MQ 有 Kafka 或者 RocketMQ。另外, canal 依賴 ZooKeeper 作為分布式協調組件來實現 HA ,canal 的 HA 分為兩個部分:


那麼,針對緩存的刪除操作便可以在 canal client 或 consumer 中編寫相關業務代碼來完成。這樣,結合資料庫日誌增量解析消費的方案以及 Cache-Aside 模型,在讀請求中未命中緩存時更新緩存(通常這里會涉及到復雜的業務邏輯),在寫請求更新資料庫後刪除緩存,並基於日誌增量解析來補償資料庫更新時可能的緩存刪除失敗問題,在絕大多數場景下,可以有效的保證緩存的最終一致性。

另外需要注意的是,還應該隔離事務與緩存,確保資料庫入庫後再進行緩存的刪除操作。 比如考慮到資料庫的主從架構,主從同步及讀從寫主的場景下,可能會造成讀取到從庫的舊數據後便更新了緩存,導致緩存落後於資料庫的問題,這就要求對緩存的刪除應該確保在資料庫操作完成之後。所以,基於 binlog 增量日誌進行數據同步的方案,可以通過選擇解析從節點的 binlog,來避免主從同步下刪除緩存過早的問題。

3、數據傳輸服務 DTS


3

Read-Through


Read-Through 意為讀穿透模式,它的流程和 Cache-Aside 類似,不同點在於 Read-Through 中多了一個訪問控制層,讀請求只和該訪問控制層進行交互,而背後緩存命中與否的邏輯則由訪問控制層與數據源進行交互,業務層的實現會更加簡潔,並且對於緩存層及持久化層交互的封裝程度更高,更易於移植。


4

Write-Through


Write-Through 意為直寫模式,對於 Write-Through 直寫模式來說,它也增加了訪問控制層來提供更高程度的封裝。不同於 Cache-Aside 的是,Write-Through 直寫模式在寫請求更新資料庫之後,並不會刪除緩存,而是更新緩存。


這種方式的 優勢在於讀請求過程簡單 ,不需要查詢資料庫更新緩存等操作。但其劣勢也非常明顯,除了上面我們提到的更新資料庫再更新緩存的弊端之外,這種方案還會造成更新效率低,並且兩個寫操作任何一次寫失敗都會造成數據不一致。

如果要使用這種方案, 最好可以將這兩個操作作為事務處理,可以同時失敗或者同時成功,支持回滾,並且防止並發環境下的不一致 。另外,為了防止緩存擾動的頻發,也可以給緩存增加 TTL 來緩解。

站在可行性的角度,不管是 Write-Through 模式還是 Cache-Aside 模式,理想狀況下都可以通過分布式事務保證緩存層數據與持久化層數據的一致性,但在實際項目中,大多都對一致性的要求存在一些寬容度,所以在方案上往往有所折衷。

Write-Through 直寫模式適合寫操作較多,並且對一致性要求較高的場景,在應用 Write-Through 模式時,也需要通過一定的補償機制來解決它的問題。首先,在並發環境下,我們前面提到了先更新資料庫,再更新緩存會導致緩存和資料庫的不一致,那麼先更新緩存,再更新資料庫呢?

這樣的操作時序仍然會導致下面這樣線程 1 先更新緩存,最後更新資料庫的情況,即由於線程 1 和 線程 2 的執行不確定性導致資料庫和緩存的不一致。這種由於線程競爭導致的緩存不一致,可以通過分布式鎖解決,保證對緩存和資料庫的操作僅能由同一個線程完成。對於沒有拿到鎖的線程,一是通過鎖的 timeout 時間進行控制,二是將請求暫存在消息隊列中順序消費。


在下面這種並發執行場景下,來自線程 1 的寫請求更新了資料庫,接著來自線程 2 的讀請求命中緩存,接著線程 1 才更新緩存,這樣便會導致線程 2 讀取到的緩存落後於資料庫。同理,先更新緩存後更新資料庫在寫請求和讀請求並發時,也會出現類似的問題。面對這種場景,我們也可以加鎖解決。


另在,在 Write-Through 模式下,不管是先更新緩存還是先更新資料庫,都存在更新緩存或者更新資料庫失敗的情況,上面提到的重試機制和補償機制在這里也是奏效的。


5

Write-Behind


Write behind 意為非同步回寫模式,它也具有類似 Read-Through/Write-Through 的訪問控制層,不同的是,Write behind 在處理寫請求時,只更新緩存而不更新資料庫,對於資料庫的更新,則是通過批量非同步更新的方式進行的,批量寫入的時間點可以選在資料庫負載較低的時間進行。

在 Write-Behind 模式下,寫請求延遲較低,減輕了資料庫的壓力,具有較好的吞吐性。但資料庫和緩存的一致性較弱,比如當更新的數據還未被寫入資料庫時,直接從資料庫中查詢數據是落後於緩存的。同時,緩存的負載較大,如果緩存宕機會導致數據丟失,所以需要做好緩存的高可用。顯然,Write behind 模式下適合大量寫操作的場景,常用於電商秒殺場景中庫存的扣減。


6

Write-Around


如果一些非核心業務,對一致性的要求較弱,可以選擇在 cache aside 讀模式下增加一個緩存過期時間,在寫請求中僅僅更新資料庫,不做任何刪除或更新緩存的操作,這樣,緩存僅能通過過期時間失效。這種方案實現簡單,但緩存中的數據和資料庫數據一致性較差,往往會造成用戶的體驗較差,應慎重選擇。


7

總結


在解決緩存一致性的過程中,有多種途徑可以保證緩存的最終一致性,應該根據場景來設計合適的方案,讀多寫少的場景下,可以選擇採用「Cache-Aside 結合消費資料庫日誌做補償」的方案,寫多的場景下,可以選擇採用「Write-Through 結合分布式鎖」的方案 ,寫多的極端場景下,可以選擇採用「Write-Behind」的方案。

6. 主庫清理binlog日誌後從庫沒清理

[方法一]手動清理binlog

清理前的准備:

1.查看主庫和從庫正在使用的binlog是哪個文件

show master status
show slave status\G
2.在刪除binlog日誌之前,首先對binlog日誌備份,以防萬一

開始手動清除binlog,刪除指定日期以前的日誌

purge master logs before '2016-09-01 17:20:00'; //刪除指定日期以前的日誌索引中binlog日誌文件


purge master logs to'mysql-bin.000022'; //刪除指定日誌文件的日誌索引中binlog日誌文件
注意:使用該語法,會將對應的文件和mysql-bin.index中對應路徑刪除

時間和文件名一定不可以寫錯,尤其是時羨棗肢間中的年和文件名中的序號,以防不下心將正在使用的binlog刪除!!!切勿刪除正在使用的binlog

補充:(參考 https://www.aliyun.com/jiaocheng/1405382.html)
reset master:將刪除日誌索引文件中記錄的所有binlog文件,創建一個新的日誌文件,起始值從000001開始。不要輕易使用該命令,這個命令通常僅僅用於第一次用於搭建主從關系的時的主庫。
reset slave:清除master.info文件、relay-log.info文件,以及所有的relay log文件,並重新啟用一個新的relaylog文件
使用reset slave之前必須使用stop slave 命令將復制進程停止。

[方法二]通過設置binlog過期時間,使系統自動刪除binlog文件

1.在mysql中修改

查看binlog過期時間,這個值默認是0天,也就是說不自動清理,可以根據生產情況修改,本例修改為7天

mysql> show variables like 'expire_logs_days';

+------------------------+-------+

| Variable_name | Value |

+------------------------+-------+

| expire_logs_days | 0 |

+------------------------+-------+

mysql> set global expire_logs_days = 7; #設置binlog多少天過期
設置之後不會立即清除,觸發條件是以下之一:

1.binlog大小超過max_binlog_size,max_binlog_size默認為1G

2.手動執行flush logs

如果binlog非常多,不要輕易設置該參數,有可能導致IO爭用,這個時候可以使用purge命令予以清除:

將bin.000055之前的binlog清掉:

mysql>purge binary logs to 'bin.000055';
將指定時間之前的binlog清掉:

mysql>purge binary logs before '2017-05-01 13:09:51';
2.在配置文件my.cnf中修改

mysqld在每個二進制日誌名後面添加一個數字擴展名。每兄世次你啟動伺服器或刷新日誌時該數字則增加。如果當前日誌大小達到max_binlog_size,還會自動創建新的二進制日誌。如果你正則使用大的事務,二進制日誌還會超過max_binlog_size:事務全寫入一個二進制日誌中,絕對不要寫入不同的二進制日誌中。

expire_logs_days :定義了mysql清除過期日誌的時間。默認值為0,表示「沒有自動刪除」。

max_binlog_size:二進制日誌最大大小,如果二進制日誌寫入的內容超出給定值,日誌就會發生滾動。你不能將該變數設置為大於1GB或小於4096位元組。 默認值是1GB。

在my.cnf中添加配置,設置過期時間為30天

expire_logs_days = 30
max_binlog_size使用默認岩畢值即可

注意:

過期時間設置的要適當,對於主從復制,要看從庫的延遲決定過期時間,避免主庫binlog還未傳到從庫便因過期而刪除,導致主從不一致!!!

7. MySQL的Binlog與主從復制

在MySQL中,可以使用多種存儲引擎。其中最常用的InnoDB引擎支持事務,Redo Log和Undo Log就是InnoDB裡面的工具,用於實現事務。而Binlog是MySQL層面的東西,用於實現主從復制,與使用的存儲引擎無關。

通過監聽並解析Mater的Binlog,也可以實現將MySQL中的數據同步到其他應用組件中(比如更新緩存)的效果。

在不發生宕機的情況下,未提交的事務和已回滾的事務是不山租知寫入Binlog日誌中的,只有提交成功的事務才寫入Binlog日誌。這一點和Redo Log不一樣,Redo Log中逗消會記錄未提交、已回滾的事務內容。

Binlog是一種邏輯日誌——例如Binlog的statement格式記錄原始SQL語句、RAW格式記錄某一行修改前後的值——且一個事務的日誌在Binlog中是連續排列的,因此要求每個事務都要串列地寫入,這意味著每個事務在寫Binlog之前都要排他地鎖住Binlog,這會導致寫的效率很低。MySQL5.6之後,通過pipline技術非同步地批量化將已提交的事務內容寫入Binlog。

一個事務的提交既要寫Binlog日誌又要寫Redo Log日誌,如何保證雙寫的原子性?一個寫成功,寫另外一個時發生宕機,重啟後如何處理?在討論這個問題之前,先說下Binlog自身寫入的原子性問題:Binlog刷盤到一半,出現宕機,這個問題和Redo Log的寫入原子性是同樣的問題,通過類似於checksum的辦法或者Binlog中的結束標記來判斷出某個事務的Binlog這是不是不完整的Binlog,從而把不完整的部分截掉。對於客戶端來說,此時宕機,事務肯定是沒有提交成功的,所以截掉也沒問題。下面來講如何保證雙寫Binlog和Redo Log的原子性。由於雙寫Binlog和Redo Log發生在同一台機器上,這其實是一個內部分布式事務,可以使用兩階段提交法來實現雙寫的原子性。簡單來說就是:

1)第一階段(准備階段):MySQL Server要求innoDB完成將事務內容寫入Redo Log中的工作,只等事務提交;以及,MySQL Server完成Binlog內容寫入內存的工作,只等刷盤。兩個都准備好之後,會向MySQL Server發送OK反饋,MySQL Server緊接著執行第二階段。

2)第二階段(提交階段):收到客戶端的Commit指令,型迅MySQL Server先將內存中的Binlog刷盤,然後讓innoDB執行事務的提交。兩個都完成之後,會向MySQL Server發送OK反饋,兩階段提交結束。

若雙寫Binlog和Redo Log的過程中發生宕機,處理思路為:

1)若宕機發生在第一階段,此時Binlog還在內存中,宕機導致全部消失。而Redo Log記錄了未提交的日誌,MySQL Server重啟後感知到Binlog中不存在Redo Log中記錄的未提交事務,會自行回滾未提交事務的Redo Log日誌;

2)若宕機發生在第二階段,Binlog寫了一半,innoDB還未執行提交,MySQL Server重啟後會對Binlog做截斷,對Redo Log中記錄的未提交事務做回滾;

3)若宕機發生在第二階段,Binlog寫入成功,innoDB還未執行提交,MySQL Server重啟後會通過checksum的辦法或者Binlog中的結束標記感知到Binlog寫入成功,緊接著對Binlog中存在的、但Redo Log未提交的事務發起提交。

在MySQL的Master / Slave集群模式中,有三種主從復制模式:

1)同步復制:所有的Slave都收到Master發送的Binlog,並且接收完,Master才認為事務提交成功,再對客戶端返回成功。這種方式最安全,但是性能很差;

2)非同步復制:只要Master事務提交成功,就對客戶端返回成功。後台線程非同步地將Binlog發送給Slave,然後Slave回放Binlog。這種方式性能最好,但是可能會導致數據丟失;

3)半同步復制:Master事務提交後,同時把Binlog同步給Slave,只要有部分(數量可以配置)Slave收到了Binlog,就認為事務提交成功,對客戶端返回。

對於半非同步復制,如果Slave超時後還未返回,也會退化為非同步復制。所以無論是非同步復制還是半非同步復制,都無法嚴格保證主從中的數據完全一致,主從復制的延遲會導致主節點宕機後部分數據未來得及同步到從節點,從而丟失數據。但是主節點宕機後,還是要立即切換到從節點,保證服務的可用(犧牲一致性保證可用性),數據的丟失可以通過後續的人工干預來補償。

8. MySQL: 你的binlog_expire_logs_seconds可能正在失效

如果你正在使用MySQL8.0,並且在使用物理熱備工具,那麼 binlog_expire_logs_seconds 可能不會如你預想的那樣生效。

為了防止 binlog 文件過大導致無可用的磁碟空間,MySQL提供了一個系統變數用來配置過期時間,MySQL5.7時變數名為 expire_logs_days ,精確度為天;MySQL8.0使用 binlog_expire_logs_seconds 來控制,其效果和名字的變化一樣,精確度由友缺頌天變成了秒。超好鄭過這個時間的 binlog 會被自動清理,自動清理的觸發時機為(注意:並不是以每秒這樣的固定頻率檢查是否有過期日誌):

MySQL啟動不用多說,binlog 刷新又分兩種情況:

下面我們來看一個 binlog_expire_logs_seconds 失效的場景。

這是因為MySQL8.0為了解決備份時的全局鎖問題,新引入了 LOCK INSTANCE FOR BACKUP 備份鎖,而這把鎖恰好導致了 binlog_expire_logs_seconds 的失效,下面兩張圖說明這個問題:

如果 MySQL 每天的數據修改很少,產生的 binlog 很小,而扮和 max_binlog_size 設置很大。每次在達到單個 binlog 的最大大小前,執行定時任務調用 xtrabackup 備份,備份時加的備份鎖 LOCK INSTANCE FOR BACKUP 和 FLUSH NO_WRITE_TO_BINLOG BINARY LOGS 會導致:binlog 刷新了,但是無法自動刪除過期的 binlog。新的 binlog 寫一天沒有達到最大大小,又進行備份,每天循環這個邏輯,導致過期的 binlog 越來越多,一直無法被自動刪除。

當然,如果你使用的是 MySQL5.7,那並不會有這個問題,雖然 MySQL5.7時備份時會加全局鎖,但是並不影響過期 binlog 的自動刪除。從這個角度看,這是個 bug,所以報給官方後很快被確認了: https://bugs.mysql.com/bug.php?id=104785

等待修復的過程是漫長的,如果你恰好遇見了這個冷門的 bug,可以把 max_binlog_size 調小,保證在備份前 binlog 就能夠達到最大大小,自然的刷新可以正常觸發自動刪除。

9. MySQL參數:innodb_flush_log_at_trx_commit 和 sync_binlog

innodb_flush_log_at_trx_commit 和 sync_binlog 是 MySQL 的兩個配置參數,前者是 InnoDB 引擎特有的。之所以把這兩個參數放在一起討論,是因為在實際應用中,它們的配置對於 MySQL 的性能有很大影響。

1. innodb_flush_log_at_trx_commit

簡而言之, innodb_flush_log_at_trx_commit  參數指定了 InnoDB 在事務提交後的日誌寫入頻率。這么說其實並不嚴謹,且看其不同取值的意義和表現。

當 innodb_flush_log_at_trx_commit 取值為 0 的時候,log buffer 會 每秒寫入到日誌文件並刷寫(flush)到磁碟。但每次事務提交不會有任何影響,也就是 log buffer 的刷寫操作和事務提交操作沒有關系。在這種情況下,MySQL性能最好,謹謹但如果 mysqld 進程崩潰,通常會導致最後笑晌前 1s 的日誌丟失。

當取值為 1 時,每次事務提交時,log buffer 會被寫入到日誌文件並刷寫到磁碟。這也是默認值。這是最安全的配置,但由於每次事務都需要進行磁碟I/O,所以也最慢。

當取值為 2 時,每次事務提交會寫入日誌文件,但並不會立即刷寫到磁碟,日誌文件會每秒刷寫一次碰清到磁碟。這時如果 mysqld 進程崩潰,由於日誌已經寫入到系統緩存,所以並不會丟失數據;在操作系統崩潰的情況下,通常會導致最後 1s 的日誌丟失。

上面說到的「最後 1s」並不是絕對的,有的時候會丟失更多數據。有時候由於調度的問題,每秒刷寫(once-per-second flushing)並不能保證 100% 執行。對於一些數據一致性和完整性要求不高的應用,配置為 2 就足夠了;如果為了最高性能,可以設置為 0。有些應用,如支付服務,對一致性和完整性要求很高,所以即使最慢,也最好設置為 1.

2. sync_binlog

sync_binlog  是 MySQL 的二進制日誌(binary log)同步到磁碟的頻率。MySQL server 在 binary log 每寫入 sync_binlog 次後,刷寫到磁碟。

如果 autocommit 開啟,每個語句都寫一次 binary log,否則每次事務寫一次。默認值是 0,不主動同步,而依賴操作系統本身不定期把文件內容 flush 到磁碟。設為 1 最安全,在每個語句或事務後同步一次 binary log,即使在崩潰時也最多丟失一個語句或事務的日誌,但因此也最慢。

大多數情況下,對數據的一致性並沒有很嚴格的要求,所以並不會把 sync_binlog 配置成 1. 為了追求高並發,提升性能,可以設置為 100 或直接用 0. 而和 innodb_flush_log_at_trx_commit 一樣,對於支付服務這樣的應用,還是比較推薦 sync_binlog = 1.

10. 怎麼實現redis的資料庫的緩存(redis實現緩存的流程)

大致為兩種措施:

一、腳本同步:

1、自己寫腳本將資料庫數據寫入到redis/memcached。

2、這就涉及到實時數據變更的問題(mysqlrowbinlog的實時分析),binlog增量訂閱Alibaba的canal,以及緩存層數據丟失/失效後的數據同步恢復問題。

二、純賀業務層實現:

1、先讀取nosql緩存層,沒有數據再讀取mysql層,並寫入數據到nosql。

2、nosql層做好多節點分布式(一致性hash),以及節點失效後替代方案(多層hash尋找相鄰替代節點),和數據震盪恢復了。

redis實現資料庫緩存的分析:

對於變化頻率非常快的數據來說,如果還選擇傳統的靜態緩存方式(Memocached、FileSystem等)展示數據,可能在緩存的存取上會有很大的開銷則褲差,並不能很好的滿足需要,而Redis這樣基於內存的NoSQL資料庫,就非常適合擔任實時數據的容器。

但是往往又有數據可靠性的需求,採用MySQL作為數據存儲,不會因為內存問題而引起數據丟失,同時也可以利用關系資料庫的特性實現很多功能。所以就會很自然的想到是否可以採用MySQL作為數據存孫皮儲引擎,Redis則作為Cache。

MySQL到Redis數據復制方案,無論MySQL還是Redis,自身都帶有數據同步的機制,比較常用的MySQL的Master/Slave模式,就是由Slave端分析Master的binlog來實現的,這樣的數據復制其實還是一個非同步過程,只不過當伺服器都在同一內網時,非同步的延遲幾乎可以忽略。那麼理論上也可用同樣方式,分析MySQL的binlog文件並將數據插入Redis。

因此這里選擇了一種開發成本更加低廉的方式,借用已經比較成熟的MySQLUDF,將MySQL數據首先放入Gearman中,然後通過一個自己編寫的PHPGearmanWorker,將數據同步到Redis。比分析binlog的方式增加了不少流程,但是實現成本更低,更容易操作。