《MySQL實戰45講》學習小結(運維篇)

丁奇老師《MySQL實戰45講》的學習小結

 

第一篇:基礎概念

 

第二篇:運維管理

 

之前對數據庫主要是使用,運維管理做得很少,主要是備份、備份、備份 ^_^

通過這門課,在運維管理這方面學到的知識最多,學習內容的整理也最喫力。

 

照例從問題入手。

要理解MySQL在運維方面的機制,首先要理解數據庫運維面對的問題:

  1. 數據庫服務崩潰,但存儲器正常,如何恢復?

  2. 服務器損毀,服務器上數據損壞或丟失,如何恢復數據和服務?

  3. 誤刪數據,如何恢復?

  4. 系統壓力超過單個數據庫服務器的能力,如何分佈到多個服務器?

  5. 數據庫性能急劇下降,怎麼辦?

 

在處理這些問題之前,先理解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來判斷。

 

詳見如下流程圖:

 

幾點說明:

  1. 客戶端發出“提交事務”命令後,服務器成功返回,則客戶端認爲事務完成、數據已更新。但是,如果 2、3 這兩步都是異步請求的話,在服務器崩潰時,binlog和redo log有可能都未被持久化。因此教程中建議採用“雙1”配置,保證在一個事務完成前,binlog和prepare狀態的redo log被持久化,以保證能恢復數據。

  2. 客戶端發出提交“提交事務”命令後,服務器崩潰,客戶端沒有收到response,這種情況下,只要binlog已持久化,即視爲事務已完成,需要用相應的redo log恢復數據。

  3. 數據的持久化,即“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講)

 

一主多從結構,主庫可讀寫,從庫只讀,這是典型的讀寫分離結構,以分攤主庫的壓力。

在這種結構下,客戶端訪問數據庫服務,有兩種方案

  1. 客戶端自行制定訪問哪個庫

  2. 通過一個proxy層,由proxy來選擇訪問哪個庫

 

兩種方案,都會遇到“過期讀”的問題。即由於存在同步延遲,在從庫上讀到的不是最新數據。

存在幾種不同的處理方案:

  1. 強制走主庫,由客戶端判斷業務上是否必須讀取最新的數據,是則指定訪問主庫。

  2. sleep,多用在寫完之後的讀,例如發帖後的查看,控制延遲1秒再發查看請求。看起來很笨拙,但很實用。主從延遲,正常情況下不超過1s。

  3. 等待從庫獲取最新數據,這又可細分3種方法
     - 等待 seconds_behind_master = 0
     - 先查已接收的最新位點,等待從庫執行的最新位點跟前者一致
     - 先查備庫已收到的所有日誌的GTID集合,等待這些事務在從庫上都已經被執行

後兩種方法,只能保證已接收的日誌都已經被執行,無法處理主庫執行後,從庫還未接收日誌的情況。爲了解決這個問題,需要結合 semi-sync 機制。

 

 

5. 數據庫性能急劇下降,怎麼辦(第22講)

 

首先分析原因,按照原因,對症下藥。必要時,犧牲一定的可靠性,來提升可用性。

可能的原因:

連接風暴、慢查詢、QPS突增、長事務、online DDL、死鎖檢測。

 

應對策略

  • 白名單、賬號管理,可用於屏蔽來自特定服務器、特定應用的請求,避免局部問題拖垮整體。

  • sql動態重寫,可臨時屏蔽大量佔用資源的問題sql,或強制其使用正確的索引。

  • online DDL是風險比較高的操作,最好在從庫上執行,然後做主從切換,再在原主庫上執行。

  • 長事務往往會帶來併發問題,應進行監控,發現問題,及早解決。

 

這裏要再次強調下:

  1. “消滅問題”優於“解決問題”。設立規範、流程,減少人爲操作失誤。良醫治未病。

  2. 建立監控體系,定期監測長事務、慢SQL等問題,儘早發現,不讓小問題堆積成大問題。

  3. 問題應對方法流程化、工具化,定期演練,遇到問題時才能快速響應。功夫在平時。

 

 

本文內容爲丁奇老師《MySQL實戰45講》的學習筆記,只是一個提綱。

這門課程的內容和組織方式,每一講的思考題、大家的留言、老師的點評,都非常棒。

繼續推薦。

 

 

黃鶴

2019-12-03

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章