移動雲基於MySQL Galera的PXC運維實戰

本文由 dbaplus 社羣授權轉載。

前言

在衆多的MySQL開源軟件中,Galera是非常有特色的,它的特點及優勢是具有良好的併發性和一致性。Galera Cluster的主要用途是爲MySQL提供一致性的集羣化解決方案,以一個dlopenable通用複製庫的形式提供給MySQL,並通過自身的Write-Set提供複製服務,實現MySQL的多線程並行複製。此外,它自帶集羣節點管理機制,可以主動監測集羣節點狀態,自動管理有問題的數據節點,同時也可以實現集羣的多點寫入和平滑擴容。它對待事務的行爲時,要麼在所有節點上執行,要麼都不執行,這種實現機制決定了它對待一致性的行爲非常嚴格,能夠非常完美地保證MySQL集羣的數據一致性。

目前,對Galera Cluster的封裝有兩個,雖然名稱不同,但實質都是一樣的,使用的都是Galere羣集。一個是MySQL的創始人Monty在自己全新的MariaDB上實現的MariaDB Cluster,一個是著名的MySQL服務和工具提供商Percona實現的Percona Xtradb Cluster,簡稱爲PXC。

從2016年開始,我參與了“移動雲”的MySQL數據庫運維管理工作。“移動雲”是一個不斷髮展壯大的雲服務供應商,訂單和用戶數據非常重要,隨着“移動雲”在網用戶數量的不斷增長,對數據庫的高可用性和數據一致性提出了更高的要求。經過長期研究,不斷地試錯,終於在Galera的基礎上,實現了一套自己的MySQL運維方案,截止到現在,已經有相當數量的線上集羣運行着經過標準化改造的PXC,在這個過程中,我們也積累了很多Galera的技術經驗,希望這些經驗也能幫助其他Galera使用者解決疑難或規避問題。

PXC

Percona XtraDB Cluster是一個完全開源的MySQL的高可用性解決方案。它將Percona Server和Percona XtraBackup與Galera庫集成,以實現同步多主複製。集羣由節點組成,其中每個節點包含同一組數據同步的跨節點。推薦的配置是至少有3個節點。每個節點都是常規的MySQL服務器實例(例如Percona Server)。可以將現有的MySQL服務器實例轉換爲節點,並使用此節點作爲基礎來運行集羣。還可以從集羣中分離任何節點,並將其用作常規的MySQL服務器實例。

集羣爲多主的模式,三個節點之間完全是對等的,都可以作爲主節點,用戶可以使用結構化查詢語言(SQL)對數據進行修改和查詢。該系統採用share nothing的架構,每個節點都可以提供讀寫服務。任何節點的修改都會自動同步到所有節點,當有客戶端在某個節點寫入數據時,集羣會將新數據自動同步到其它節點,具有嚴格的數據一致性。

High Availability

集羣節點間通過同步複製進行數據同步,通過心跳實現異常節點的檢測和剔除。配合上層的負載均衡,可以實現集羣的高可用,單個節點宕機不會影響服務。在具有3個節點的基本設置中,將任何節點關閉,Percona XtraDB Cluster仍可以繼續工作。在任何時間點,可以關閉任何節點以執行維護或更改配置。即使在計劃外情況(如節點崩潰或它網絡變得不可用),羣集會繼續工作,可在工作節點上運行查詢。

如果在節點關閉時對數據進行了更改,則節點在加入時可以使用兩個選項加入集羣:

1、State Snapshot Transfer(SST) 是將所有數據從一個節點複製到另一個節點的過程。當新節點加入羣集並從現有節點接收所有數據時,通常使用SST。Percona XtraDB羣集中有三種SST方法:

  • mysqldump
  • rsync
  • xtrabackup

mysqldump和rsync的缺點是,你的集羣在數據存在時變成READ-ONLY 複製(SST應用FLUSH TABLES WITH READ LOCK命令)。

SST使用XtraBackup不需要READ LOCK命令整個同步過程中,只要同步.FRM文件(一樣定期備份)。

2、Incremental State Transfer(IST) 是指僅將增量更改從一個節點複製到另一個節點。即使在羣集沒有鎖定爲只讀狀態,SST在正常操作服務時可能會侵入和干擾。IST可以避免這樣。如果1個節點在很短的時間出現故障,它只能獲取發生在它失效時發生的那些更改。IST是在節點上使用高速緩存機制來實現的。每個節點包含一個緩存,環形緩衝區(大小可配置)最後N個更改,並且節點間能夠傳輸該高速緩存的一部分。顯然,只有當轉移所需的變化量小於N時IST才能完成。如果它超過N則加入節點必須執行SST。

可以使用以下命令監視節點的當前狀態:

SHOW STATUS LIKE 'wsrep_local_state_comment';

當節點處於Synced(6)狀態時,它是集羣的一部分並準備處理流量。

Multi-Master Replication

多主複製意味着可以寫入任何節點並確保寫入對集羣中所有節點都是一致的。這與常規MySQL複製不同,在常規MySQL複製中,您必須將寫入應用於master以確保它被同步。

PXC同步複製原理

  • 事務在本地節點執行時採取樂觀策略,成功廣播到所有節點後再做衝突檢測;
  • 檢測出衝突是,本地事務優先被回滾;
  • 每個節點獨立、異步執行隊列中的write set;
  • 事務在本地節點執行成功返回客戶端後,其他節點保證該事務一定會被執行,因此有可能存在延時,即虛擬同步。

PXC的複製架構圖(摘自官方文檔)

對於多主複製,任何寫入都在所有節點上提交或根本不提交。所有查詢都在節點上本地執行,並且僅在COMMIT上有特殊處理。當COMMIT查詢發出時,事務必須通過所有節點上的認證。如果它沒有通過,你會收到ERROR作爲響應。通過之後,事務在本地節點上應用。COMMIT的響應時間包括以下內容:

  • 網絡往返時間;
  • 認證時間;
  • 本地申請。

注意:在遠程節點上應用事務不會影響COMMIT的響應時間。

這種架構有兩個重要的後果:

  • 可以並行使用幾個應用程序。這實現了真正的並行複製。使用 wsrep_slave_threads 變量配置的線程從機可以有多個並行。
  • slave可能有一個小的時間段不同步。這是因爲master可以申請事件比slave更快。如果你從slave讀取,可以讀取尚未更改的數據。但可以通過設置 wsrep_causal_reads = ON 變量來更改。在這種情況下,在slave上讀取將等待,直到事件被應用(會明顯增加讀取的響應時間)。Slave和Master之間的差距是這種複製被稱爲虛擬同步的原因,而不是真正的同步複製。

所以如果運行寫事務到兩個不同的節點,集羣將使用樂觀鎖。事務在個別查詢期間不會檢查可能的鎖衝突,而是在COMMIT階段,可能會收到ERROR響應。

Flow Control

前面瞭解了PXC是虛擬同步,事務在本地節點提交成功時,其他節點保證執行該事務。在提交事務時,本地節點把事務複製到所有節點,之後各個節點獨立、異步地進行certification test、事務插入待執行隊列、執行事務。

然而由於不同節點之前執行事務的速度不一樣,長時間運行後,慢節點的待執行隊列可能會越積越長,最終導致事務丟失。PXC繼承了Galera的flow control機制,作用是協調各個節點,保證所有節點執行事務的速度大於隊列增長的速度。實現原理是,集羣中同時只有一個節點可以廣播消息,每個節點都會獲得廣播消息的機會,當慢節點的執行隊列超過一定長度後,它會廣播一個FC_PAUSE消息,其他節點收到消息後會暫緩廣播消息,知道該慢節點的執行隊列長度減少到一定程度後,集羣數據同步又開始恢復。

部署架構案例

PXC部署架構分本地存儲和網絡存儲兩種情況。其中,採用本地存儲的架構,其架構圖如下圖:

採用網絡存儲的架構,其架構圖如下:

介紹完PXC的原理和架構,下面看一下具體的日常運維工作。

數據庫巡檢

數據庫巡檢的內容通常涵蓋主機硬件、操作系統和MySQL巡檢項。其中,主機/os巡檢主要包括:主機的硬件配置、CPU/內存/磁盤使用率以及磁盤的I/O使用情況;MySQL巡檢項包括:數據庫配置、用戶權限、大表數據量、業務表主鍵和自增長情況、數據庫的併發性、當前和歷史連接情況統計、備份執行情況以及日誌記錄和慢SQL的分析優化等。

1、查看MySQL服務器配置信息及運行狀況

通過show variables來查看mysql服務器配置信息,例如show variables like ‘%slow%’;用於查看慢查詢,show variables like ‘max_connections’;;用於查看最大連接數。

通過ps -ef | grep mysql查看mysql進程運行狀況。

2、通過show status統計各種SQL的執行頻率

通過show status可以查看服務器狀態信息。show status可以根據需要顯示session 級別的統計結果和global級別的統計結果。

1)以下幾個參數對Myisam和Innodb存儲引擎都計數:

  • Com_select 執行select 操作的次數,一次查詢只累加1;
  • Com_insert 執行insert 操作的次數,對於批量插入的insert 操作,只累加一次;
  • Com_update 執行update 操作的次數;
  • Com_delete 執行delete 操作的次數。

2)以下幾個參數是針對Innodb存儲引擎計數的,累加的算法也略有不同:

  • Innodb_rows_read 執行select 查詢返回的行數;
  • Innodb_rows_inserted 執行insert 操作插入的行數;
  • Innodb_rows_updated 執行update 操作更新的行數;
  • Innodb_rows_deleted 執行delete 操作刪除的行數。

通過以上幾個參數,可以很容易的瞭解當前數據庫的應用是以插入更新爲主還是以查詢操作爲主,以及各種類型的SQL大致的執行比例是多少。對於更新操作的計數,是對執行次數的計數,不論提交還是回滾都會累加。

對於事務型的應用,通過Com_commit和Com_rollback可以瞭解事務提交和回滾的情況,對於回滾操作非常頻繁的數據庫,可能意味着應用編寫存在問題。

3)以下幾個參數便於我們瞭解數據庫的基本情況:

  • Connections 試圖連接Mysql 服務器的次數;
  • Uptime 服務器工作時間;
  • Slow_queries 慢查詢的次數。

3、通過show status判斷系統瓶頸

1)QPS(每秒Query量)

QPS = Questions(or Queries) / seconds
mysql > show global status like 'Question%';

2)TPS(每秒事務量)

TPS = (Com_commit + Com_rollback) / seconds
mysql > show global status like 'Com_commit';
mysql > show global status like 'Com_rollback';

3)key Buffer 命中率

mysql>show global status like 'key%';
key_buffer_read_hits = (1-key_reads / key_read_requests) * 100%
key_buffer_write_hits = (1-key_writes / key_write_requests) * 100%

4)InnoDB Buffer命中率

mysql> show status like 'innodb_buffer_pool_read%';
innodb_buffer_read_hits = (1 - innodb_buffer_pool_reads /   
innodb_buffer_pool_read_requests) * 100%

5)Query Cache命中率

mysql> show status like 'Qcache%';
Query_cache_hits = (Qcahce_hits / (Qcache_hits + Qcache_inserts )) * 100%;

6)Table Cache狀態量

mysql> show global status like 'open%';

比較open_tables與opend_tables值。

7)Thread Cache 命中率

mysql> show global status like 'Thread%';
mysql> show global status like 'Connections';
Thread_cache_hits = (1 - Threads_created / connections ) * 100%

創建用來處理連接的線程數。如果 Threads_created 較大,你可能要增加 thread_cache_size 值。緩存訪問率的計算方法 Threads_created/Connections。

8)鎖定狀態

mysql> show global status like '%lock%';

Table_locks_waited/Table_locks_immediate 如果這個比值比較大的話,說明表鎖造成的阻塞比較嚴重。

Innodb_row_lock_waits:innodb行鎖,太大可能是間隙鎖造成的。

Table_locks_waited:不能立即獲得的表的鎖的次數。如果該值較高並且有性能問題,應首先優化查詢,然後拆分表或使用複製。

9)Tmp Table 狀況(臨時表狀況)

mysql >show status like 'Created_tmp%';

Created_tmp_disk_tables/Created_tmp_tables比值最好不要超過10%,如果Created_tmp_tables值比較大,可能是排序子句過多或者是連接子句不夠優化。

10)Binlog Cache 使用狀況

mysql > show status like 'Binlog_cache%';

如果Binlog_cache_disk_use值不爲0 ,可能需要調大 binlog_cache_size大小。

11)Innodb_log_waits

mysql > show status like 'innodb_log_waits';

Innodb_log_waits值不等於0的話,表明 innodb log buffer 因爲空間不足而等待。

12)連接數大小——max_connections

mysql> show variables like 'max_connections';
+-----------------------+-------+
| Variable_name   | Value |
+----------------------+--------+
| max_connections | 500   |
+---------------------+--------+
        mysql> show global status like 'max_used_connections';
+------------------------------+--------+
| Variable_name        | Value |
+------------------------------+--------+
| Max_used_connections | 498   |
+-----------------------------+--------+

設置的最大連接數是500,而響應的連接數是498 。

max_used_connections / max_connections * 100% = 99.6% (理想值 ≈ 85%)

13)Handler_read_rnd

mysql> show status like 'Handler_read_rnd';

如果 Handler_read_rnd 太大 ,則你寫的 SQL 語句裏很多查詢都是要掃描整個表,而沒有發揮鍵的作用。

14)Key_reads

mysql> show status like 'key_read%';
+--------------------------+---------+
| Variable_name     | Value  |
+--------------------------+---------+
| Key_read_requests  | 1190  |
| Key_reads         | 2     |
+--------------------------+---------+

如果 Key_reads 太大,則應該把 my.cnf 中 key_buffer_size 變大,可以用 Key_reads/Key_read_requests計算出 cache 失敗率。

15)Handler_read_rnd

mysql> show status like 'Handler_read_rnd';

根據固定位置讀一行的請求數。如果正執行大量查詢並需要對結果進行排序該值較高。可能使用了大量需要MySQL 掃描整個表的查詢或連接沒有正確使用鍵。

16)Handler_read_rnd_next

mysql>show status like 'Handler_read_rnd_next';

在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,該值較高。通常說明你的表索引不正確或寫入的查詢沒有利用索引。

17)Select_full_join

mysql>show status like 'Select_full_join';

沒有使用索引的聯接的數量。如果該值不爲 0, 你應仔細檢查表的索引。

18)Select_range_check

mysql>show status like 'Select_range_check';

在每一行數據後對鍵值進行檢查的不帶鍵值的聯接的數量。如果不爲 0 ,你應仔細檢查表的索引。

19)Sort_merge_passes

mysql>show status like 'Sort_merge_passes';

排序算法已經執行的合併的數量。如果這個變量值較大,應考慮增加 sort_buffer_size 系統變量的值。

20)Handler_read_first

mysql>show status like 'Handler_read_first';

索引中第一條被讀的次數。如果較高,它表明服務器正執行大量全索引掃描。例如, SELECT col1 FROM foo ,假定col1 有索引。

21)Handler_read_key

mysql>show status like 'Handler_read_key';

根據鍵讀一行的請求數。如果較高,說明查詢和表的索引正確。

22)Handler_read_next

mysql>show status like 'Handler_read_next';

按照鍵順序讀下一行的請求數。如果你用範圍約束或如果執行索引掃描來查詢索引列,該值增加。

  1. Handler_read_prev
mysql>show status like 'Handler_read_prev';

按照鍵順序讀前一行的請求數。該讀方法主要用於優化 ORDER BY … DESC 。

24)Handler_read_rnd

mysql>show status like 'Handler_read_rnd';

根據固定位置讀一行的請求數。如果你正執行大量查詢並需要對結果進行排序該值較高。你可能使用了大量需要MySQL 掃描整個表的查詢或你的連接沒有正確使用鍵。

4、打開相應的監控信息

1)error log

在配置文件my.cnf中進行配置,在運行過程中不能改變。

2)打開慢查詢

set global slow_query_log='ON';

3)設置慢查詢的時間閾值

set global long_query_time = 0.1;

4)未使用索引的sql語句打開

set global log_queries_not_using_indexes='ON

早期我們通過執行自動化腳本來輸出巡檢報告,巡檢報告內容需要人工去檢查和確認,執行巡檢腳本主要包括以下步驟:

  • 首先是,在本地下載python3.5並安裝,打開後進入python解析器;
  • 通過pip方式安裝python依賴包;
  • 因爲腳本中需要獲取graphid,要登錄zabbix管理網站去獲取;
  • 然後登錄堡壘機,配置端口轉發策略;
  • 登錄每一套數據庫,創建數據庫巡檢賬號;
  • 最後在本地執行巡檢腳本,並最終合成word巡檢報告。

雖然比手工執行巡檢,要簡化很多,並且可以按資源池批量執行巡檢,但總的來說過程還是略繁瑣。目前我們在做的是採用平臺化的方式,取代傳統的腳本和工具巡檢。通過搭建數據庫管理平臺,實現巡檢管理,其中一鍵巡檢功能用於數據庫的例行巡檢。可選擇多臺數據庫批量執行巡檢。巡檢結果能在web頁面上查看。並針對每個數據庫實例會生成一份標準格式的巡檢報告,報告可以從web頁面直接下載。

另外,平臺也提供了快速巡檢的功能,用於對一些常規項巡檢進行快速排查,常規巡檢項主要是DBA根據以往故障處理的經驗,總結下來的一些常用的排查項目,例如:processlist當前連接情況覈查,並且可以保留快照,數據庫阻塞的情況覈查、流控的情況檢查、鎖爭用的核查以及一些臨時表和詢緩存等情況的檢查,下圖中的例子是對當前連接情況的排查,可以看到什麼用戶正連接數據庫在執行哪些操作。

另外,作爲對常規巡檢項的補充,也提供了自定義巡檢項的功能。可以執行一些臨時的查詢語句,並且結合了語義審覈和轉譯的功能,能夠保證sql語句的正確使用,查詢結果可以從web頁面上導出。下圖中的例子是臨時對數據庫賬號的進行查詢,並導出結果。

複製引擎監控管理

1、打開復制引擎的調試信息-wsrep_debug

在運行過程中,可以通過set global wsrep_debug = ‘ON’;來動態地打開wsrep的調試信息(調試信息會輸入到錯誤日誌中),可以幫助複製引擎定位問題。

2、Galera集羣監控

1)監控集羣的一致性

mysql>show status like 'wsrep_cluster_state_uuid';

通過檢查變量wsrep_cluster_state_uuid的值,確認此節點是否屬於正確的集羣。該變量的值在集羣的各個節點中必須相同,如果某個節點出現不同的值,說明此節點沒有連接到集羣中。

mysql>show status like 'wsrep_cluster_conf_id';

通過檢查變量wsrep_cluster_conf_id的值,用於查看集羣發生變化的總數,同時確認此節點是否屬於主集羣。該變量的值在集羣的各個節點中必須相同,如果某個節點出現不同的值,說明此節點脫離集羣了,需要檢查網絡連接等將其恢復到一致的狀態。

mysql>show status like 'wsrep_cluster_size';

通過檢查變量wsrep_cluster_size的值,查看集羣節點的總數。

mysql> show status like 'wsrep_cluster_status';

通過檢查變量wsrep_cluster_status的值,查看節點的狀態是否爲Primary,若不爲Primary,表示集羣部分節點不可用,甚至可能是集羣出現了腦裂。

如果所有節點的狀態都不爲Primary,就需要重置仲裁,如果不能重置仲裁,就需要手動重啓。

第一步,關閉所有節點

第二步,重啓各個節點,重啓過程中可以參考wsrep_last_committed的值確定主節點。

注:手動重啓的缺點是會造成緩存丟失,從而不能做IST。

2)監控節點狀態

mysql> show status like 'wsrep_ready';

通過檢查變量wsrep_ready的值,查看該節點的狀態是否可以正常使用SQL語句。如果爲ON,表示正常,若爲OFF,需進一步檢查wsrep_connected的值。

mysql> show status like 'wsrep_connected';

如果此變量的值爲OFF,說明該節點還沒有加入到任何一個集羣組件中,這很可能是因爲配置文件問題,例如wsrep_cluster_address或者wsrep_cluster_name值設置錯誤,也可以通過查看錯誤日誌進一步定位原因。

如果節點連接沒有問題,但wsrep_ready的值還爲OFF,檢查wsrep_local_state_comment的值。

mysql> show status like 'wsrep_local_state_comment';

當節點的狀態爲Primary時,wsrep_local_state_comment的值一般爲Joining, Waiting for SST, Joined, Synced或者Donor,如果wsrep_ready爲OFF,並且wsrep_local_state_comment的值爲Joining, Waiting for SST, Joined其中一個,說明此節點正在執行同步。

當節點的狀態不爲Primary時,wsrep_local_state_comment的值應該爲Initialized。任何其他狀態都是短暫的或臨時的。

3)檢測複製的健康狀態

mysql> show status like 'wsrep_flow_control_paused';

通過檢查變量wsrep_flow_control_paused的值,可以確認有多少slave延遲在拖慢整個集羣的,從而查看複製的健康狀態。這個值越接近0.0越好,優化的方法主要通過增加配置文件中wsrep_slave_threads的值,或者將複製很慢的節點剔除出集羣。wsrep_slave_threads取值可以參考wsrep_cert_deps_distance,wsrep_cert_deps_distance表示併發事務處理數的均值,wsrep_slave_threads的值不應該比wsrep_cert_deps_distance高很多。

4)檢測網絡慢的問題

mysql> show status like 'wsrep_local_send_queue_avg';

通過檢查變量wsrep_local_send_queue_avg的值,可以檢測網絡狀態。如果此變量的值偏高,說明網絡連接可能是瓶頸。造成此情況的原因可能出現在物理層或操作系統層的配置上。

5)集羣監控通知擴展

通過wsrep_notify_cmd參數調用命令腳本的二次擴展。

wsrep狀態監控

mysql> show status like '%wsrep%';
+------------------------------------------+-------------------------------------------------------+
| Variable_name                | Value                                |
+------------------------------------------+-------------------------------------------------------+
| wsrep_local_state_uuid         | e8149a5c-636a-11e5-8b4b-67b16bb666a4   |
| wsrep_protocol_version         | 7                                    |
| wsrep_last_committed          | 526498                               |
| wsrep_replicated              | 526498                               |
| wsrep_replicated_bytes         | 238196578                            |
| wsrep_repl_keys              | 1926403                              |
| wsrep_repl_keys_bytes         | 27520685                             |
| wsrep_repl_data_bytes         | 176980021                            |
| wsrep_repl_other_bytes        | 0                                    |
| wsrep_received               | 7970                                 |
| wsrep_received_bytes          | 64791                                |
| wsrep_local_commits          | 526357                               |
| wsrep_local_cert_failures       | 0                                    |
| wsrep_local_replays           | 0                                    |
| wsrep_local_send_queue       | 0                                    |
| wsrep_local_send_queue_max   | 2                                    |
| wsrep_local_send_queue_min   | 0                                    |
| wsrep_local_send_queue_avg   | 0.000041                             |
| wsrep_local_recv_queue       | 0                                    |
| wsrep_local_recv_queue_max   | 4                                    |
| wsrep_local_recv_queue_min   | 0                                    |
| wsrep_local_recv_queue_avg   | 0.034504                              |
| wsrep_local_cached_downto    | 1                                    |
| wsrep_flow_control_paused_ns  | 22690449177                          |
| wsrep_flow_control_paused     | 0.000005                             |
| wsrep_flow_control_sent       | 0                                    |
| wsrep_flow_control_recv       | 371                                  |
| wsrep_cert_deps_distance      | 74.734609                            |
| wsrep_apply_oooe            | 0.000000                             |
| wsrep_apply_oool             | 0.000000                             |
| wsrep_apply_window          | 1.000000                             |
| wsrep_commit_oooe           | 0.000000                             |
| wsrep_commit_oool           | 0.000000                             |
| wsrep_commit_window        | 1.000000                             |
| wsrep_local_state             | 4                                    |
| wsrep_local_state_comment    | Synced                               |
| wsrep_cert_index_size        | 43                                   |
| wsrep_cert_bucket_count      | 126282                               |
| wsrep_gcache_pool_size       | 261431296                            |
| wsrep_causal_reads           | 0                                    |
| wsrep_cert_interval           | 0.000002                          |
| wsrep_incoming_addresses     | 10.130.7.5:3306,,10.130.7.4:3306      |
| wsrep_evs_delayed           |                                  |
| wsrep_evs_evict_list          |                                  |
| wsrep_evs_repl_latency       | 0/0/0/0/0                           |
| wsrep_evs_state             | OPERATIONAL                    |
| wsrep_gcomm_uuid          | e813b31f-636a-11e5-90c7-0f6d378e1dfb |
| wsrep_cluster_conf_id        | 5                                  |
| wsrep_cluster_size           | 3                                  |
| wsrep_cluster_state_uuid      | e8149a5c-636a-11e5-8b4b-67b16bb666a4 |
| wsrep_cluster_status          | Primary                            |
| wsrep_connected            | ON                                |
| wsrep_local_bf_aborts        | 0                                  |
| wsrep_local_index           | 2                                  |
| wsrep_provider_name        | Galera                              |
| wsrep_provider_vendor       | Codership Oy <[email protected]>    |
| wsrep_provider_version       | 3.11(rXXXX)                       |
| wsrep_ready                | ON                                |
+----------------------------------------+---------------------------------------------------+
58 rows in set (0.12 sec)

wsrep相關參數含義介紹:

wsrep_local_state_uuid:存儲於該節點的UUID狀態 wsrep_protocol_version:wsrep協議使用的版本 wsrep_last_committed:最後提交事務的序列號 wsrep_replicated:發送到其他節點的writesets總數 wsrep_replicated_bytes:發送到其他節點的writesets總字節數 wsrep_repl_keys:複製keys總數 wsrep_repl_keys_bytes:複製keys總字節數 wsrep_repl_data_bytes:複製數據的總字節數 wsrep_repl_other_bytes:其他複製的總字節數 wsrep_received:從其他節點接收的writesets總數 wsrep_received_bytes:從其他節點接收的writesets總字節數 wsrep_local_commits:該節點提交的writesets總數 wsrep_local_cert_failures:認證測試中失敗的writesets總數 wsrep_local_replays:因非對稱鎖粒度回放的事務數 wsrep_local_send_queue:當前發送隊列的長度,表示等待被髮送的writesets數 wsrep_local_send_queue_avg:網絡瓶頸的預兆。如果這個值比較高的話,可能存在網絡瓶 wsrep_local_recv_queue:當前接收隊列的長度,表示等待被使用的writesets數 wsrep_local_recv_queue_avg:表示slave事務隊列的平均長度,slave瓶頸的預兆 wsrep_local_cached_downto:gcache的最小序列號,這個變量可以用來判斷是用IST,還是SST。如果此值爲0,表示gcache中沒有writesets wsrep_flow_control_paused_ns:表示複製停止了多長時間,以納秒爲單位 wsrep_flow_control_paused:表示複製停止了多長時間。即表明集羣因爲Slave延遲而慢的程度,值爲0~1,越靠近0越好,值爲1表示複製完全停止。可優化wsrep_slave_threads的值來改善 wsrep_flow_control_sent:表示該節點已經停止複製了多少次 wsrep_flow_control_recv:表示該節點已經停止複製了多少次 wsrep_cert_deps_distance:有多少事務可以並行應用處理。wsrep_slave_threads設置的值不應該高出該值太多 wsrep_apply_oooe:併發執行效率,writesets應用於out-of-order的頻率 wsrep_apply_oool:大序列值的writeset比小序列值的writeset多出的執行頻率 wsrep_apply_window:同時使用的最高序列值和最小序列值間的平均差值 wsrep_commit_oooe:事務脫離隊列的頻率 wsrep_commit_window:同時提交的最大序列值和最小序列值間的平均差值 wsrep_local_state:galera狀態值 1 - Joining (requesting/receiving State Transfer) –表示此節點正在加入集羣 2 - Donor/Desynced –表示正在加入的節點是donor 3 - Joined –表示節點已經加入集羣r 4 - Synced –表示節點已經和集羣同步 wsrep_local_state_comment:galera狀態,如果wsrep_connected爲On,但wsrep_ready爲OFF,則可以從該項查看原因 wsrep_cert_index_size:certification索引的entries數量 wsrep_cert_bucket_count:哈希表中certification索引的cells數 wsrep_gcache_pool_size:page pool或者爲gcache動態分配的字節數 wsrep_causal_reads:writesets處理數 wsrep_incoming_addresses:以逗號分隔顯示集羣中的節點地址 wsrep_evs_repl_latency:提供集羣節點間通信複製延遲信息 wsrep_evs_delayed:被剔除出集羣的UUID wsrep_evs_evict_list:有延遲的節點列表 wsrep_evs_state:EVS協議狀態 wsrep_gcomm_uuid:galera的view_id,不同於集羣的uuid,在gvwstate.dat可以查看到 wsrep_cluster_conf_id:集羣成員發生變化的數目,正常情況下所有節點上該值是一樣的。如果值不同,說明該節點被臨時"分區"了。當節點之間網絡連接恢復的時候應該會恢復一樣的值 wsrep_cluster_size:集羣中的節點數目,如果這個值跟預期的節點數一致,則所有的集羣節點已經連接 wsrep_cluster_state_uuid:集羣的UUID值,在集羣所有節點的值應該是相同的,有不同值的節點,說明其沒有連接入集羣 wsrep_cluster_status:集羣節點的狀態。如果不爲"Primary",說明出現"分區"或是"split-brain"狀況,可能的取值爲:Primary、Non-Primary、Disconnected wsrep_connected:節點是否連接到集羣,如果該值爲Off,且wsrep_ready的值也爲Off,則說明該節點沒有連接到集羣。(可能是wsrep_cluster_address或wsrep_cluster_name等配置錯造成的。具體錯誤需要查看錯誤日誌) wsrep_local_bf_aborts:被其他節點上的事務終止的正在執行的本地事務數 wsrep_local_index:集羣節點索引 wsrep_provider_name:wsrep程序提供者 wsrep_provider_vendor:wsrep供應商 wsrep_provider_version:wsrep程序提供者的版本 wsrep_ready:節點是否可以提供查詢。該值爲ON,則說明可以接受SQL負載。如果爲Off,則需要檢查wsrep_connected

PXC備份管理

對於我們來說,數據庫的備份和恢復是很日常的工作。因爲平時難免會遇到服務器宕機、磁盤損壞等情況,在這種情況下,要保證數據不丟失或者最小程度的丟失,平時進行有效的備份就顯得非常重要了。

因此對於DBA來說,備份工具的使用、備份策略的選擇以及備份系統的完善都是需要特別關注的,另外,像備份文件校驗通常是比較容易忽視的問題,出現故障時發現備份文件不可用,會造成很嚴重損失。

我們平時常用的備份工具是mysqldump、Percona Xtrabackup,分別用於邏輯備份和物理備份,其實大多數DBA的備份/恢復體系都是圍繞這兩個工具展開的。

早期我們通過在數據庫本地,定期執行腳本的方式進行備份,策略是:每週日凌晨2點執行一次全量備份,週一到週六每天凌晨2點執行增量備份,在本地存儲空間充足的情況下,要求至少要求保留1個月的備份數據,備份恢復測試是通過在備份恢復測試機上面執行‘測試腳本’,每週六分時段從各個數據庫拉取備份文件到本機進行恢復測試,並通過日誌記錄恢復操作是否成功。

那當面對成百上千個MySQL實例的維護,以上備份恢復方式會有哪些問題呢?我們具體來看一下。

1、首先是對本地存儲空間需求較大,並且佔用服務器系統總線,內存,CPU,磁盤IO資源,使得備份對線上業務有一定的影響。

在現網環境中,由於本地磁盤空間有限,通常本地僅保留一個月左右的備份數據,對於更早的數據如無特殊需求,到後期會自動刪除,對於較重要的數據要保留更久要通過遠程備份實現,不過在遠程備份時,備份傳輸引發的網卡流量會對線上業務造成影響,需要考慮到網卡的能力。這時可以考慮使用雙網卡,一塊用於備份,一塊用來提供線上服務。如果沒有這個條件,要通過在備份時限速來達到目的。

2、其次是,集中化管理缺失,備份節點較多,備份方式多樣化,備份完成情況、佔用空間大小、完成時間以及校驗結果等內容的記錄和呈現也不夠直觀,缺少圖形化界面。突發故障或變更前的臨時備份依然靠手工執行,存在效率低和安全性差的問題。

3、集羣備份節點選擇問題。備份或多或少對線上業務都有影響,建議備份任務在slave或只讀節點上執行, 那麼當集羣發生主備切換,如果備份節點沒有動態進行切換,導致在寫庫上進行備份,使線上業務受備份操作影響。

爲了解決上述問題,我們還是採用平臺化的方式,通過開發來實現集中化管理,包括:備份執行與恢復管理、歷史備份查看以及備份策略修改和管理等功能。

另外想要提一下的是容災,容災是指在備份的基礎上建立一個異地的數據系統,這個系統是本地關鍵應用數據的一個實時複製。

在災難發生時,可以支持自動和手工災備切換功能,保正業務的連續性。

PXC常見故障排查和處理方法

節點宕機

當集羣中出現讀寫服務節點宕機的情況時,應該按如下所述步驟進行處理,以對外提供服務。

1)讀寫服務節點宕機

① 查看集羣各節點狀態:ps –ef | grep rdb

結果:讀寫節點(RW3)進程不存在,其他節點服務正常

② 查看error log日誌,檢查宕機原因

③ 重啓RW3

④ 啓動完成後,確認RW3狀態是否正常

2)某兩個讀寫服務節點宕機

① 查看集羣各節點狀態:ps –ef | grep rdb

結果:讀寫節點(RW2、RW3)進程不存在,RW1節點服務正常

② 查看error log日誌,檢查宕機原因

③ 此時,RW1讀寫服務節點正常工作,重啓RW2和RW3

④ 啓動完成後,確認RW2和RW3狀態是否正常

3)讀寫服務節點都宕機

① 查看集羣各節點狀態:ps –ef | grep rdb

結果:讀寫節點(RW1、RW2、RW3)進程均不存在,此時集羣無法提供正常服務

② 查看error log日誌,發現最後宕機的是RW3

③ 關閉集羣:kill -15 PID

④ 重新啓動最後宕掉的讀寫服務節點RW3

⑤ RW3狀態恢復正常後,根據實際負載情況判斷是否繼續啓動RW2和RW1,預判如果可能做SST,由於做donor的節點無法提供服務,服務恢復時間比較長,可以先不起後面的節點,暫時只讓RW3提供服務,閒時再啓動其他節點,這種情況下要注意限制數據庫的連接數。啓動RW2節點

⑥ 待數據同步結束,RW2狀態恢復正常後,啓動RW1節點

⑦ 檢查各個節點的狀態,是否能正常提供服務

節點無響應

當集羣中出現任一讀寫節點無響應時,應該按如下所述步驟進行處理,以對外提供服務。

1)負載高

主要查看以下幾項:CPU使用率,內存使用率,操作系統IO,網絡IO,網絡連接數等。對應的命令和工具爲:SystemTap,LatencyTOP,vmstat, sar, iostat, top, tcpdump等。通過觀察這些指標,我們就可以定位系統的性能問題。具體檢查順序可參看下述步驟:

① 先看CPU使用率,如果CPU使用率不高,但系統的Throughput和Latency上不去,這說明應用程序並沒有忙於計算,而是忙於別的一些事,比如IO。(另外,CPU的利用率還要看內核態的和用戶態的,內核態的上去了,整個系統的性能就下來了。而對於多核CPU來說,CPU 0 是相當關鍵的,如果CPU 0的負載高,那麼會影響其它核的性能,因爲CPU各核間是需要有調度的,這靠CPU0完成)。

② 查看一下IO大不大,IO和CPU一般是反着來的,CPU利用率高則IO不大,IO大則CPU利用率就低。關於IO,我們要看三個事,一個是磁盤文件IO,一個是驅動程序的IO(如:網卡),一個是內存換頁率。這三個事都會影響系統性能。

③ 查看一下網絡帶寬使用情況,在Linux下,你可以使用iftop, iptraf, ntop, tcpdump這些命令來查看,或是用Wireshark來查看。

④如果CPU不高,IO不高,內存使用不高,網絡帶寬使用不高,但是系統的性能上不去。這說明你的程序有問題,比如,你的程序被阻塞了。可能是因爲等哪個鎖,可能是因爲等某個資源,或者是在切換上下文。

通過了解操作系統的性能,我們才知道性能的問題,比如:帶寬不夠,內存不夠,TCP緩衝區不夠等等,很多時候,不需要調整程序的,只需要調整一下硬件或操作系統的配置就可以了。

注:OS常用查看命令

cpu – vmstat、top、sar

內存– ipcs、free

io – iostat、sar

網絡– tcpdump、netstat –i、sar

預防措施

  • 合理調整數據庫的參數;
  • 應用上線前進行測試,優化後上線。防止應用大批量處理sql,insert、select等語句;
  • 監控系統資源負荷情況;
  • 限制報表併發查詢數量,參照業務吞吐量。

處理方法

① 排查執行時間較長的sql語句,步驟如下:

在各節點執行show full processlist;將執行時間較長的sql語句及執行該語句的線程ID(show full processlist顯示結果中的列Id值)記錄下來;

② 針對記錄下來的sql語句,與應用相關人員確認是否能夠終止;

說明:對於發現執行時間較長且仍在執行的select語句,爲了降低風險,在必要情況下可以直接終止;對於涉及數據更新的語句,需要根據實際情況進行相關處理(比如記錄下 SQL語句以便後續分析);

③ 終止執行時間較長且已得到終止確認的sql語句,kill QUERY ID或者KILL ID(執行sql語句的線程ID);

④ 確認集羣是否正常響應。

2)連接滿

當連接數滿時,用戶連接不上數據庫,當前正在接受讀寫的節點達到最大連接值會無法連接數據庫,按照以下方法處理:

根據歷史統計信息修改max_connections。

方法一

① 第max_connections+1連接只能由擁有super privileges用戶登錄,當連接數滿時,擁有super_priv的用戶登陸數據庫修改max_ connections值:

set GLOBAL max_connections=XXXX;修改完成後實時生效,無需重啓數據庫。

② 進入配置文件my.cnf,設置max_connections值,該值與步驟一的值相同。

方法二

進入配置文件my.cnf,設置max_connections值,並重啓該節點服務。

3)有鎖表情況

預防措施:

① 監控鎖等待數量,暫設置10個報警;

② 避免業務忙時進行批量更新操作。

當出現有鎖表情況而導致數據庫響應慢的情況時,應該按如下所述步驟進行處理:

方法一:

① 獲取鎖表情況的相關信息,步驟如下:

  • (a) 以root用戶登陸到各主節點機器上;
  • 調用客戶端連接工具;
  • 在各主節點的mysql提示符下執行use information_schema;
  • 在各主節點的mysql提示符下執行如下sql語句;
select a.trx_id ,a.trx_query , b.lock_data
from innodb_trx a ,innodb_locks bwhere a.trx_id = b.lock_trx_id;

② 針對鎖信息查詢語句的查詢結果,與應用相關人員確認是否可以kill

③ 如果應用確認可以kill,則kill對應的sql

方法二:

調整innodb_lock_wait_timeout值。

集羣分裂無法提供服務

當集羣出現分裂的情況時,應該按如下所述步驟進行處理,以對外提供服務。

1)集羣中有節點狀態是Primary

① 使用以下命令查看集羣狀態,查找狀態爲non-Primary的節點

SHOW STATUS where Variable_name="wsrep_cluster_status";
+------------------------------+----------+
| Variable_name        | Value   |
+-----------------------------+----------+
| wsrep_cluster_status |non- Primary |  
+--------------------------+---------------+

② 重啓狀態爲non-Primary的節點

③ 檢查各個節點的狀態,是否能正常提供服務

2)集羣中所有節點狀態都不是Primary

① 使用以下命令查看集羣狀態,發現集羣已經整個分裂,狀態均爲non-Primary

SHOW STATUS where Variable_name="wsrep_cluster_status";
+-----------------------------+----------------+
| Variable_name        | Value      |
+-----------------------------+----------------+
| wsrep_cluster_status   |non- Primary |     
+-----------------------------+----------------+

② 重新執行選主。選主規則:以最後提交事務的數據庫節點選爲主,作爲啓動集羣的主節點。同時,主節點對應的seqno也是最大的,可以通過以下命令查看

SHOW STATUS LIKE 'wsrep_last_committed';
+---------------------------+---------+
|Variable_name       |Value  |
+--------------------------+---------+
|wsrep_last_committed|409745|

③ 如果RW1是當前的主節點,則在RW1下執行下面的命令,重新引導RW1爲primary:

set global wsrep_provider_options =“pc.bootstrap=1”;Kill -15 pid關閉其他節點,關閉後可以通過grastate.dat裏的uuid和seqno判斷是否會做SST,kill -15關閉的一般不會有SST,只有IST,可以立即重啓其他節點

④ 首先啓動RW2節點

⑤ 待數據同步結束,RW2狀態恢復正常後,啓動RW3節點

⑥ 檢查各個節點的狀態,是否能正常提供服務

其他異常

1)集羣因斷電宕機

① ping 服務器端IP地址失敗

② 聯繫網絡管理員或系統管理員進行處理,重啓服務器

③ 重啓數據庫

通過查看各節點的日誌,假設最後關閉節點是RW1

重新啓動最後關閉的讀寫服務節點RW1

待數據同步結束,RW1狀態恢復正常後,啓動RW2節點

待數據同步結束,RW2狀態恢復正常後,啓動RW3節點

檢查各個節點的狀態,是否能正常提供服務

2)網絡交換機故障造成集羣對外連接中斷

① ping 服務器端IP地址失敗

② 查看/var/log/messages

③ 聯繫網絡管理員或系統管理員進行處理

3)網絡交換機故障造成集羣內部連接中斷

① ping 服務器端IP地址成功

② ping 集羣內部各節點IP失敗

③ 查看/var/log/messages

④ 聯繫網絡管理員或系統管理員進行處理

4)磁盤故障造成集羣無法提供服務

① 使用smartctl檢測磁盤健康狀態

② 聯繫系統管理員更換磁盤

③ 重啓數據庫

案例1

去年9月8號,晚上11點左右,研發人員對數據庫進行操作時,執行了1個事務,向用戶註冊表添加數據,這是一條insert語句,但是忘了提交,然後又執行了另一條sql,去修改同一張表的表結構,前面沒有提交的insert語句已對用戶註冊表添加了排他鎖,導致後續大量sql語句等待執行,引發數據庫阻塞,直到30min後第1個事務超時,數據庫阻塞才解除。

我們當時在11:08收到zabbix告警,顯示數據庫活躍線程數已達到139,一般活躍線程數超過32就會開始積壓,這個跟CPU能處理的線程數有關,因此告警值設置爲32。初步排查原因爲元數據索導致,11:07用戶開了一個insert語句沒有提交,導致元數據鎖。

元數據鎖產生的原因,簡單來說就是修改表數據的同時,修改表結構。爲了避免這種情況,mysql innodb 在執行寫入操作時會對錶,添加排它鎖,修改表結構,要等待鎖釋放後才能執行。這次故障處理,沒有直接kill掉阻塞線程,因爲按以往經驗,這種方式可以解決阻塞,但也有一定概率會引起數據文件損壞,所以在阻塞事務即將超時的情況下,並沒有做任何操作,而是等待事務超時後,數據庫自動恢復。

案例2

第二個例子是,去年11月1號,研發人員在數據庫執行查詢操作,因爲使用排序產生臨時表,又因爲instance表,跟關聯查詢語句中的任何表都沒有關聯關係,導致笛卡爾積,生成的臨時表文件過大最終將/目錄佔滿,從而引發故障。

案例3

第三個案例是由於網絡設備故障引起的,存儲網卡閃斷導致數據庫宕機,在去年6月1日上午10:46,數據庫進程故障,有12臺數據庫同時宕機,一線接到客戶投訴‘華北節點控制檯無法打開’,當時看了一下,由於mysqld進程屬於非正常關閉,啓動之前要登錄集羣3個節點,查看啓動位置sequence number,找出具有最完整數據的節點,作爲集羣第一個節點優先啓動,然後再啓動另外兩個節點同步數據。

但是,在嘗試執行mysqld_safe命令查看啓動位置的時候失敗了,繼續排查,最後找到故障原因:數據庫主機掛載塊存儲的目錄變成只讀狀態,重新掛載後命令可以正常執行,然後將數據庫陸續啓動。

這次故障原因,在後面回溯的時候發現是因爲數據庫主機存儲網卡與上層網絡設備互聯信息過程中發生了閃斷,導致存儲目錄變爲只讀狀態,最後引發故障。

小結

以上內容是在我們日常工作經驗的基礎之上,在平時維護MySQL的過程中,覺得需要引起注意或需要弄清楚的一些知識點,結合一些工作中的實際案例分享出來,希望能夠對大家有所幫助。

作者介紹

劉書浩,“移動雲”DBA,負責“移動雲”業務系統的數據庫運維、標準化等工作;擅長MySQL技術領域,熟悉MySQL複製結構、Cluster架構及運維優化;具有自動化運維經驗,負責“移動雲”數據庫管理平臺的搭建。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650781897&idx=1&sn=b5253cd2da3251c986b87a801fbb942e&chksm=f3f90d5cc48e844a806fac2ee626544ad01336d6119cc20203e9a958ec08f1fb6e05962e9d2e&scene=27#wechat_redirect

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