丁奇老師《MySQL實戰45講》的學習小結
第二篇:運維管理
之前對數據庫主要是使用,運維管理做得很少,主要是備份、備份、備份 ^_^
通過這門課,在運維管理這方面學到的知識最多,學習內容的整理也最喫力。
照例從問題入手。
要理解MySQL在運維方面的機制,首先要理解數據庫運維面對的問題:
-
數據庫服務崩潰,但存儲器正常,如何恢復?
-
服務器損毀,服務器上數據損壞或丟失,如何恢復數據和服務?
-
誤刪數據,如何恢復?
-
系統壓力超過單個數據庫服務器的能力,如何分佈到多個服務器?
-
數據庫性能急劇下降,怎麼辦?
在處理這些問題之前,先理解MySQL的幾個運行機制,再來梳理如何應對這些問題。
第一部分:運行機制
1. 日誌和主備同步(第24講,28講)
主庫和從庫之間的數據同步過程,如下圖所示:
從庫上有兩個線程。
io_thread負責向主庫發起請求,建立長連接,然後接收主庫發送的binlog,寫入relay log。從庫在發起請求時,可指定從哪個位置開始獲取binlog。
sql_thread負責讀取relay log,解析出日誌裏的命令,並執行。
從一個事務在主庫上完成,到這個事務在從庫上完成,時間差即爲同步延遲。
如果主庫掉電,事務已完成,但binlog尚未發送給備庫,會導致數據丟失。
爲了解決這個問題,可採用 semi-sync 模式。在主庫上,事務需等待至少一個從庫確認已收到日誌,才能完成事務,向客戶端發送response。
主庫上有個dump_thread,負責監聽來自從庫的請求,並隨時把binlog發送給從庫。
一個主庫可能要監聽多個從庫的請求,多個從庫請求的binlog起始位置也可能不同。
因此一個主庫不要帶很多個從庫,以免binlog的同步消耗太多資源。
2. 主備切換(第24講,25講,27講)
主備服務器的配備,目前多采用對稱結構,即主備服務器的硬件配置一致,各一臺,都提供讀寫服務,這樣一旦主庫出現問題,可以快速切換到備庫,對應用的影響最小。備庫雖然可讀寫,但正常情況下不做寫操作。以下的內容均以此爲前提。
由於存在同步延遲,因此在主備切換時,從可靠性優先角度,常見做法是先把主庫設爲只讀,待同步延遲歸0後,再切換到備庫。通常情況下同步延遲小於1秒,這期間出現短暫的全庫只讀,對應用的影響不大。(需要HA工具支持)
但如果遇到異常,導致同步延遲很大,或者主庫掉電無法訪問等情況,只能選擇可用性優先策略。這種情況下,可能會出現數據錯誤,需要人工介入。
數據庫服務器的配備,一主多從是目前使用最多的結構。
一主多從結構下,備庫是從庫的一種,它可能被切換爲主庫,而其他從庫不會。
一主多從結構下的主備切換,如下圖所示:
《MySQL實戰54講》第27講 一主多從基本結構 -- 主備切換
在一主多從結構下,主備切換包括了從庫切換master的過程。
除了主備的數據同步延遲之外,還需要考慮各從庫的同步延遲,且每個從庫的同步延遲不盡相同。
位點:
5.6版本之前,MySQL只能通過找位點的方式。
從庫跟主庫建立聯繫時,可指定從哪個binlog文件、哪個偏移量開始同步。這個文件+偏移量就是位點。但對於同一個事務(sql),在主庫和備庫上,位點是不同的。可通過切換時間定位到一個近似值,但並不精確。需要人工介入。
GTID:
MySQL 5.6版本引入了GTID以解決這個問題。
GTID = server_uuid:gno
gno是在每個MySQL實例上,單調遞增的數值,在事務提交時分配。server_uuid + gno 的組合可以保證全局唯一。GTID被包含在binlog中,發給從庫。
每個MySQL實例都維護了一個GTID集合,記錄“這個實例執行過的所有事務”。
在主備切換時,從庫會發送本實例的GTID集合,例如 set B 給新的主庫 A'。新的主庫 A' 對比自己的和從庫B的GTID集合,先檢查set B在本實例上全部存在,再發送 set A' - set B 這些事務的binlog給從庫B,然後就回到正常的binlog同步。
GTID的全部集合很大,MySQL應該會定期移除歷史,不斷提高水位。這個教程中未涉及,是猜測。
GTID除了解決主備切換時的同步問題,還可以用來在同步時跳過特定的事務。
3. 緩存和落盤(第12講,23講)
爲了提升性能,MySQL對數據的讀寫都優先在內存上進行,並選擇合適的時機將數據讀入內存、將修改結果同步到磁盤。不僅對數據如此,對日誌也使用這個策略。
爲了保證數據可靠性,需要控制redo log和binlog儘快寫入磁盤。
MySQL提供了兩個參數來控制:
-
innodb_flush_log_at_trx_commit = 1 每次事務提交時都將 redo log 直接持久化到磁盤
-
sync_binlog = 1 每次提交事務都會執行 fsync
redo log的刷盤有兩點說明:
-
根據上一篇redo log + binlog 實現crash-safe的機制,有binlog的情況下,有prepare狀態的redo log即可恢復數據。因此,一個事務只需要等待redo log prepare刷盤,不用等redo log commit刷盤。
-
redo log cache爲共用,在一個事務提交時,把該事務最後一條日誌及在此之前的所有redo log刷盤,實現group commit,提升IO效率。
在這樣的機制下,我們可以認爲MySQL的數據(及log)不會丟失。
後續關於運維問題的小結,均在此基礎上進行。
第二部分:運維場景
1. 數據庫服務崩潰,但存儲器正常,如何恢復(第2講,23講)
先明確恢復的目標。
站在應用的角度,提交了事務,數據庫服務返回response,即認爲數據已更新。這類數據需要恢復。如果事務未提交,數據庫服務崩潰,則數據不應更新,數據庫恢復後的狀態,同事務回滾。
如果提交了事務,但未收到response服務器連接就斷開了,事務的狀態依賴於數據庫的log來判斷。
詳見如下流程圖:
幾點說明:
-
客戶端發出“提交事務”命令後,服務器成功返回,則客戶端認爲事務完成、數據已更新。但是,如果 2、3 這兩步都是異步請求的話,在服務器崩潰時,binlog和redo log有可能都未被持久化。因此教程中建議採用“雙1”配置,保證在一個事務完成前,binlog和prepare狀態的redo log被持久化,以保證能恢復數據。
-
客戶端發出提交“提交事務”命令後,服務器崩潰,客戶端沒有收到response,這種情況下,只要binlog已持久化,即視爲事務已完成,需要用相應的redo log恢復數據。
-
數據的持久化,即“flush髒頁”,必然是異步的。
2. 服務器損毀,服務器上數據損壞或丟失,如何恢復數據和服務
這個問題分兩種情況,一是主庫損毀,二是從庫損毀。兩種情況都要利用備份。
2.1 主庫損毀:
如果做了熱備份,就可以將備庫切換爲新的主庫。見上文的“主備切換”。
如果原主庫上的binlog尚未被髮送到備庫,會發生數據損失。
binlog被髮送到備庫,轉存爲relay log後再被執行。這裏有個時間差。只要業務上能接受,應在relog log都被執行後再開放服務(至少把寫操作放在這個時間點後)。
如果沒有熱備,那就只能用 全量備份 + 全量備份時間點之後的binlog備份 來恢復。數據損失大。
2.2 從庫損毀
從庫損毀,只要主庫正常,數據不會丟失。重建從庫即可。
先把從庫恢復到某個時間點的數據版本,再執行這個時間點之後的binlog。前一步通過物理拷貝恢復,後一步爲邏輯恢復。完成這個操作後,再從主庫獲取後續的binlog(用位點或GTID找齊)。
從庫損毀,主要考慮的是應用的影響,需要儘快發現問題,把原來指向從庫的請求重定向到其他庫,並通知運維人員,儘快重建從庫。
儘快發現數據庫出現異常,這很重要,早發現早處理,減少對業務造成的影響。這也是HA的基礎。
判斷數據庫是否出現問題,有很多種方式,見第29講,這裏不再贅述。
3. 誤刪數據,如何恢復(第31講)
誤刪行:
根據binlog,flashback(binlog_format=row,binlog_row_image=FULL)
恢復操作不要直接在主庫上執行。在別的庫上執行,並確認後,再將其恢復回主庫。
誤刪庫/表:
全量備份,加增量日誌恢復。重放增量日誌時,跳過誤操作的那個語句。最好使用GTID模式。
要求線上有定期的全量備份,並且實時備份binlog。
因爲全量備份可能是數天前的,重放增量日誌很慢,要着重考慮如何加速恢復速度。
rm刪除數據
即上一條,服務器損毀、數據損壞的情況。
建立有高可用機制的MySQL集羣應對這種情況,並做好備份。
強調兩點:
- 做好預案,儘量把恢復過程固化爲工具,定期演練。
- 預防數據誤刪,例如:只分配必要的權限,刪除前先做標記(更新、重命名)。
4. 系統壓力超過單個數據庫服務器的能力,如何分佈到多個服務器(第28講)
一主多從結構,主庫可讀寫,從庫只讀,這是典型的讀寫分離結構,以分攤主庫的壓力。
在這種結構下,客戶端訪問數據庫服務,有兩種方案
-
客戶端自行制定訪問哪個庫
-
通過一個proxy層,由proxy來選擇訪問哪個庫
兩種方案,都會遇到“過期讀”的問題。即由於存在同步延遲,在從庫上讀到的不是最新數據。
存在幾種不同的處理方案:
-
強制走主庫,由客戶端判斷業務上是否必須讀取最新的數據,是則指定訪問主庫。
-
sleep,多用在寫完之後的讀,例如發帖後的查看,控制延遲1秒再發查看請求。看起來很笨拙,但很實用。主從延遲,正常情況下不超過1s。
-
等待從庫獲取最新數據,這又可細分3種方法
- 等待 seconds_behind_master = 0
- 先查已接收的最新位點,等待從庫執行的最新位點跟前者一致
- 先查備庫已收到的所有日誌的GTID集合,等待這些事務在從庫上都已經被執行
後兩種方法,只能保證已接收的日誌都已經被執行,無法處理主庫執行後,從庫還未接收日誌的情況。爲了解決這個問題,需要結合 semi-sync 機制。
5. 數據庫性能急劇下降,怎麼辦(第22講)
首先分析原因,按照原因,對症下藥。必要時,犧牲一定的可靠性,來提升可用性。
可能的原因:
連接風暴、慢查詢、QPS突增、長事務、online DDL、死鎖檢測。
應對策略
-
白名單、賬號管理,可用於屏蔽來自特定服務器、特定應用的請求,避免局部問題拖垮整體。
-
sql動態重寫,可臨時屏蔽大量佔用資源的問題sql,或強制其使用正確的索引。
-
online DDL是風險比較高的操作,最好在從庫上執行,然後做主從切換,再在原主庫上執行。
-
長事務往往會帶來併發問題,應進行監控,發現問題,及早解決。
這裏要再次強調下:
-
“消滅問題”優於“解決問題”。設立規範、流程,減少人爲操作失誤。良醫治未病。
-
建立監控體系,定期監測長事務、慢SQL等問題,儘早發現,不讓小問題堆積成大問題。
-
問題應對方法流程化、工具化,定期演練,遇到問題時才能快速響應。功夫在平時。
本文內容爲丁奇老師《MySQL實戰45講》的學習筆記,只是一個提綱。
這門課程的內容和組織方式,每一講的思考題、大家的留言、老師的點評,都非常棒。
繼續推薦。
黃鶴
2019-12-03