RDS 高可用保障之 – 隱式主鍵
在構建穩定可靠的應用架構時, 數據庫是最底層、最穩定的組件之一;而在雲環境中,RDS 提供一個7*24小時不間接訪問的雲服務,可用性達到99.95%.
RDS 采用主備複製架構,用戶購買一個實例,RDS都會提供一個性能對等的備庫用於保證高可用。 高可用性組件(AURORA)會每3秒檢查主庫(Master)狀態,當發現 Master 出現Down機時可以將用戶的SQL請求快速轉移到備庫(Slave)上麵。
圖1 – RDS 架構圖
在這樣的架構設計下, RDS需要保證主備數據一致性並且延時不超過10秒,以快速完成主備切換;否則,RDS會保證一致性而犧牲可用性,必須等待數據同步一致再進行切換;所以主備延時會直接影響服務的高可用性;
數據複製可靠性
MySQL複製模式可以通過參數:BINLOG_FORMAT進行配置 ;在MySQL5.1以前,MySQL默認采用 Statement 模式進行數據複製,這種模式下有可能會讓主備數據產生不一致情況,比如使用UUID等函數;MySQL在5.1版本以後,提供了基於ROW 模式的複製模式,從而大大提高了數據複製的可靠性;但這種模式在以下場景下會讓備庫的數據延時很大;
1) 存在沒有主鍵的表,導致備庫應用每個Event 都需要全表掃描 ;
2) 主庫執行了大表DDL 或大事務,導致備庫也要相同時間執行完 ;
RDS在實際的運行過程中發現,99%以上的主備延時,都是因為用戶在建表的時候沒有指定主鍵;RDS 曾經嚐試過臨時解決方案, 把有延遲的實例日誌格式改為MIXED,無主鍵表的操作用STATEMENT 格式記錄,但這種方案還是有可能產生主備數據不一致;
ROW模式數據複製
ROW 模式之所以能保證複製可靠性,是其在BINLOG裏記錄每一行完整記錄,包括所有列的值;在備庫應用日誌時,MySQL 會先嚐試用行裏的主鍵去匹配自身的記錄,如果沒有主鍵, 則進行全表掃描所有的行,每一行都與日誌進行匹配,直到發現完全匹配的行;
圖2- ROW模式日誌匹配處理流程
方案設計
在保證主備數據複製可靠性的前提下,減小主備延時;
方案一: 提醒用戶去加上主鍵,問題迎刃而解;但在實際的實施過程中,這根本不現實,用戶的學習成本、應用兼容、實施成本遠遠超過我們的想像;
方案二: 這也是雲平台自身要解決的問題,用戶不應該去關注這些問題;讓MySQL 在底層能智能的處理,對用戶透明,兼大歡喜;
對於方案二,有兩個解決思路:
1. 為什麼ROW格式日誌一定要用主鍵定位記錄,如果用二級索引行不行?雖然沒有主鍵那麼精準,但至少可以避免全表掃描
2. InnoDB 引擎也是嚴重依賴主鍵,它對於沒有主鍵的表,就自己強製加進去一個主鍵對用戶隱藏,MySQL Server層可否也這樣實現?
思路一,需要考慮的問題主要是成本開銷:
圖3-利用二級索引處理無主鍵的ROW格式Event
如果像執行SQL一樣,每一行都走一遍執行計劃看哪個二級索引比較好,那麼速度一樣快不起來.主庫隻對每個SQL走一次執行計劃選擇一個索引,備庫需要對這個SQL影響的所有行記錄都重新生成一次執行計劃。
因為ROW格式中的行包含了所有列,所以更合理的方案是,選擇一個固定規則的二級索引即可,總是有列可以被用上進行過濾。例如總是利用第一個二級索引,這樣不需要走執行計劃,可以大大節省生成執行計劃的時間,而且有這個規則,也可以調整二級索引的位置,來匹配這個規則,讓過濾性好的二級索引調整到可以被利用的位置。
幸好,MariaDB開發了一個這樣的補丁,對於有二級索引而沒有主鍵的表來說,效果還不錯。
思路二,要解決的情況是:完全沒有任何索引、以及二級索引過濾性都不好的情況(比如,性別字段)。這裏我們考慮過把InnoDB的二級索引直接引用到Server層來,但是如此一來,對於使用MyISAM表的用戶,還是沒有效果,所以需要一個更通用的設計方案:MySQL可以自動會用戶添加主鍵而對應用透明 – 隱式主鍵(Implict Primary Key)。 最終,我們采取了這樣的設計方案:
1 打開RDS 特有的參數implict_primary_key,讓隱式主鍵功能生效 ;
2 當用戶建表(CREATE TABLE)時,判斷表結構
2.1 如果表上有主鍵,則pass
2.2 如果表上沒有主鍵,有唯一鍵,則把唯一鍵放在索引的第一個位置,可以利用二級索引補丁 ;
2.3 如果表上沒有主鍵,也沒有唯一鍵,則為用戶建立一個特定名稱的自增主鍵 ;
3 當用戶修改表結構(ALTER TABLE)時,判斷新表結構
3.1 如果用戶自己添加了主鍵或唯一鍵,則刪除係統添加的主鍵
3.2 如果用戶刪除了原有的主鍵和唯一鍵,則為用戶建立一個特定名稱的自增主鍵
4 用戶做DML操作時,屏蔽這個隱式主鍵的存在
4.1 INSERT INTO table VALUES (…),用戶不需要在VALUES中填寫主鍵的值,係統會自動填充NULL,從而在寫入數據庫時自動填入自增值
4.2 SELECT * FROM table,行數據返回給用戶前,自動過濾了隱式主鍵列
4.3 LOAD DATA INFILE,用戶不會感知到表中存在主鍵,係統會自動填充NULL來使用自增主鍵值
4.4 SHOW CREATE TABLE/SHOW COLUMNS等SHOW語句,生成結構語句時自動過濾隱式主鍵列,用戶不會看到有主鍵列
5 對於係統用戶(root)需要查看真實情況的,提供show_ipk_info參數,SET show_ipk_info=1,則可以查看隱式主鍵,不會進行任何隱藏操作
6 如果implict_primary_key參數關閉,則隱式主鍵功能不再發揮作用,即當用戶進行DDL操作時,如果原來表上有隱式主鍵,則會趁用戶DDL之機一起刪除。但是原有的沒有刪除的隱式主鍵列並不會顯示給用戶,會一直隱藏。
圖4 – 隱含主鍵操作展示;
基於思路2,RDS MySQL 源代碼團隊已經完成開發適應於RDS場景的MySQL Patch,並全麵覆蓋到RDS所有MySQL5.1和 MySQL 5.5服務器中,目前運行穩定 ;
最後更新:2017-04-03 12:56:03