MySQL——RDS下的分割槽表實踐
實踐背景
專案中有的表空間太大,且行數太多,故決定對一些表進行分庫分表。再研究選型方案的時候發現常用的一些分庫分表的解決方案對業務程式碼修改較多,故決定採用MySQL的分割槽方案。
其實在我個人看來,分割槽表就是MySQL幫我們實現了底層的分庫分表,不需要涉及業務程式碼的修改,不需要關注分散式事務。因為就訪問資料庫而言,邏輯上還是隻有一個表,但是實際上確有多個物理分割槽物件組成,會根據具體的分割槽規則查詢具體的分割槽。
介紹一下這次實踐的表,表空間大小172G,1億2千萬條記錄。
資料庫版本:RDS MySQL 5.6
工具:阿里雲DTS
一、為什麼分割槽?
優點:
- 對已過期或者不需要儲存的資料,可以通過刪除與這些資料有關的分割槽來快速刪除資料,它的效率遠比DELETE高
- 在where子句中包含分割槽條件時,可以只掃描必要的一個或者多個分割槽來提高查詢效率
例如下面語句:
SELECT * FROM t PARTITION(p0,p1)WHERE c <5 僅選擇與WHERE條件匹配的分割槽p0和p1中的記錄
- 涉及聚合函式SUM()、COUNT()的查詢時,會在每個分割槽上並行處理
- 分割槽把原本一個表的資料儲存在多個物理磁碟上,實現了更高的IOPS
缺點:
- 無法使用外來鍵,不支援全文索引(現在應該也沒什麼公司還在用外來鍵吧)
- 分割槽鍵設計不太靈活,如果不走分割槽鍵,很容易出現全表鎖
- 開發寫一個SQL,不清楚mysql是怎麼玩的
二、RANGE分割槽
目前MySQL支援範圍分割槽(RANGE),列表分割槽(LIST),雜湊分割槽(HASH)以及KEY分割槽四種。
本文是以範圍分割槽(RANGE)對時間進行的分割槽的,故我就簡單介紹一下RANGE分割槽。更多分割槽型別詳見官方文件MySQL 5.6 分割槽型別
基於一個給定連續區間的列值,根據區間分配分割槽。最常見的是基於時間欄位。其實基於分割槽的列最好是整型,如果日期型的可以使用函式轉換為整型。MySQL 5.6支援的分割槽函式
本例中使用TO_DAYS函式
CREATE TABLE members (
id VARCHAR(25) NOT NULL,
firstname VARCHAR(25) NOT NULL,
lastname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
joindate DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (id,joindate) USING BTREE,
KEY idx_joindate (joindate) USING BTREE
)ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT
PARTITION BY RANGE (TO_DAYS(joindate)) (
PARTITION p0 VALUES LESS THAN (TO_DAYS('1960-01-01')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('1970-01-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('1980-01-01')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('1990-01-01')),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
PS:像例子中的如果你有主鍵或唯一索引,你必須把你的分割槽鍵也加上,其中joindate就是分割槽鍵,要不建立會失敗!
PS:像上面加了LESS THAN MAXVALUE,後面就不能新加分割槽了!!!
示例:
如下查詢就會落在定義的p2分割槽內的索引上。故在查詢的時候帶上你的分割槽鍵就會走對應分割槽查詢資料,如果你的條件跨越多個分割槽進行聚合函式SUM()、COUNT()的查詢時,它會在每個分割槽上並行處理。如果沒有帶分割槽鍵查詢就會全表查詢。
explain partitions select * from members WHERE joindate BETWEEN '1970-02-03' AND '1970-02-04';
我在遷移完資料進行查詢的時候發現一個特別有意思的現象,同一條SQL如果分割槽鍵的時間區間不一樣,它會根據rows行數少的走不同的範圍索引。至於它底層是怎麼實現的我就沒去研究了
三、分割槽管理
簡單介紹了下範圍分割槽,接下來說一下對分割槽常用的一下操作。
分割槽管理包括對於分割槽的增加,刪除,以及查詢。更多詳見官方文件MySQL 分割槽管理
1.增加分割槽
對於RANGE和LIST分割槽:
alter table table_name add partition (partition p0 values ...(exp))
#例
ALTER TABLE members ADD PARTITION (TO_DAYS('2021-03-01'));
2.刪除分割槽
刪除了分割槽,同時也將刪除該分割槽中的所有資料。如果刪除了分割槽導致分割槽不能覆蓋所有值,那麼插入資料的時候會報錯。
alter table table_name drop partition p0;
3.查詢有多少個分割槽
SELECT * FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'members';
四、資料遷移
前面說了那麼多概念,我說一下本次把大表資料遷移到分割槽表的過程。
為什麼會選擇DTS呢?因為它可以不停機遷移資料,支援全量遷移和增量遷移,對原表影響不大。
遷移過程如下:
- 首先在RDS的同一個例項裡面新建了一張同結構的分割槽表
- 使用DTS建立遷移任務,遷移時候不要選擇結構錢謙益,只選擇全量+增量遷移
- 然後還需要編輯下目標庫表名,也就是做下對映從A->B的遷移
- 停掉寫入資料的任務,當任務佇列為空時,等待幾分鐘暫停並結束遷移任務
- 最後修改表名,完成資料遷移和切換(我在測試環境修改分割槽表名要一些時間,但RDS修改表名是秒改)
參考官方文件:MySQL 5.6 分割槽
以上純屬個人觀點,如有