【mysql優化系列】分割槽和索引介紹和優化案例
1. 索引和分割槽
分割槽和索引作為一個常見的資料庫效率提高手段。本文基於實際場景進行簡單的分析和整理。
索引和分割槽的關係
mysql分割槽後每個分割槽成了獨立的檔案,雖然從邏輯上還是一張表其實已經分成了多張獨立的表,由於Innodb資料和索引都是儲存在".ibd"檔案當中(從INNODB_SYS_INDEXES系統表中也可以得到每個索引都是對應各自的分割槽(primary key和unique也不例外)),所以分割槽表的索引也是隨著各個分割槽單獨儲存。
在INNODB_SYS_INDEXES系統表中:
type代表索引的型別,當我們在分割槽表中建立索引時其實也是在每個分割槽中建立索引,每個分割槽維護各自的索引(其實也就是local index)
0:一般的索引(非主鍵或者唯一):
1:(GEN_CLUST_INDEX)不存在主鍵索引的表,會自動生成一個6個位元組的標示值
2:unique索引
3:primary索引;
當具有索引的表進行分割槽的時候
對於一般的索引(非主鍵或者唯一)沒什麼問題,由於索引樹中只保留了索引key和主鍵key(如果存在主鍵則是主鍵的key否則就是系統自動生成的6個的key)不受分割槽的影響;但是如果表中存在主鍵就不一樣了,雖然在每個分割槽檔案中都存在主鍵索引但是主鍵索引需要保證全域性的唯一性就是所有分割槽中的主鍵的值都必須唯一(唯一鍵也是一樣的道理),所以在建立分割槽時如果表中存在主鍵或者唯一鍵那麼分割槽列必須包含主鍵或者唯一鍵的部分或者全部列
2. 案例1(正常分割槽)
場景描述
有一構建記錄表,由於資料量較大,每年資料量已經達到300萬+,在時間查詢中由於特殊原因,還需要關聯其他表進行查詢,因此查詢效率很低,尤其在多條件查詢的時候,就算給查詢條件增加索引,也會出現查詢秒級的情況,因此按照時間進行分割槽
分割槽策略
每年記錄分割槽一次,沒季度記錄子分割槽一次(如果後面資料量更大,考慮按照月份進行子分割槽的可能性)
表結構
DROP TABLE IF EXISTS `t_record`;
CREATE TABLE `t_record` (
`name` VARCHAR (255) NOT NULL,
`type` VARCHAR (255) NOT NULL,
`create_time` DATETIME NOT NULL,
INDEX idx_create_time(`create_time`)
) ;
示例資料
分割槽sql
分割槽前
(1) 只有一個.idb檔案
(2)查詢
分割槽後
(1)分割槽sql
ALTER TABLE t_record
PARTITION BY RANGE(YEAR(create_time))
SUBPARTITION BY HASH(QUARTER(create_time ))
SUBPARTITIONS 4 (
PARTITION p2019 VALUES LESS THAN (2020),
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN maxvalue
);
(2)按照分割槽分為多個idb了
SELECT
partition_name part,
partition_expression expr,
partition_description descr,
table_rows
FROM
information_schema.partitions
WHERE table_schema = SCHEMA()
AND table_name = 't_record' ;
(3)檢視執行分割槽
3. 案例2(特殊場景)
當我們對一個有主鍵(自增id)的記錄表按照時間進行分割槽的時候,面臨的問題就比較尷尬,時間不可能作為唯一索引或者主鍵(比如儲存時間或者建立時間,都有可能重複),我們也很少看到將時間作為獨立主鍵的(作為普通索引進行區間查詢還是比較場景),所以的話這種情況就比較頭疼
這種場景我們可以把id和時間作為一個聯合主鍵
表結構
DROP TABLE IF EXISTS `t_record_id` ;
CREATE TABLE `t_record_id` (
`id` INT (8) NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) NOT NULL,
`type` VARCHAR (255) NOT NULL,
`create_time` DATETIME NOT NULL,
PRIMARY KEY (`id`, `create_time`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;
分割槽
ALTER TABLE t_record_id
PARTITION BY RANGE(YEAR(create_time))
SUBPARTITION BY HASH(QUARTER(create_time ))
SUBPARTITIONS 4 (
PARTITION p2019 VALUES LESS THAN (2020),
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN maxvalue
);