介紹
mysql分區后每個分區成了獨立的文件,雖然從邏輯上還是一張表其實已經分成了多張獨立的表,從“information_schema.INNODB_SYS_TABLES”系統表可以看到每個分區都存在獨立的TABLE_ID,由於Innodb數據和索引都是保存在".ibd"文件當中(從INNODB_SYS_INDEXES系統表中也可以得到每個索引都是對應各自的分區(primary key和unique也不例外)),所以分區表的索引也是隨着各個分區單獨存儲。
在INNODB_SYS_INDEXES系統表中type代表索引的類型;
0:一般的索引,
1:(GEN_CLUST_INDEX)不存在主鍵索引的表,會自動生成一個6個字節的標示值,
2:unique索引,
3:primary索引;
所以當我們在分區表中創建索引時其實也是在每個分區中創建索引,每個分區維護各自的索引(其實也就是local index);對於一般的索引(非主鍵或者唯一)沒什么問題由於索引樹中只保留了索引key和主鍵key(如果存在主鍵則是主鍵的key否則就是系統自動生成的6個的key)不受分區的影響;但是如果表中存在主鍵就不一樣了,雖然在每個分區文件中都存在主鍵索引但是主鍵索引需要保證全局的唯一性就是所有分區中的主鍵的值都必須唯一(唯一鍵也是一樣的道理),所以在創建分區時如果表中存在主鍵或者唯一鍵那么分區列必須包含主鍵或者唯一鍵的部分或者全部列(全部列還好理解,部分列也可以個人猜測是為了各個分區和主鍵建立關系),由於需要保證全局性又要保證插入數據更新數據到具體的分區所以就需要將分區和主鍵建立關系,由於通過一般的索引進行查找其它非索引字段需要通過主鍵如果主鍵不能保證全局唯一性的話那么就需要去每個分區查找了,這樣性能可想而知。
To enforce the uniqueness we only allow mapping of each unique/primary key value to one partition.If we removed this limitation it would mean that for every insert/update we need to check in every partition to verify that it is unique. Also PK-only lookups would need to look into every partition.
索引方式:
性能依次降低
1.主鍵分區
主鍵分區即字段是主鍵同時也是分區字段,性能最好
2. 部分主鍵+分區索引
使用組合主鍵里面的部分字段作為分區字段,同時將分區字段建索引(見下面詳細說明)
3.分區索引
沒有主鍵,只有分區字段且分區字段建索引
4.分區+分區字段沒有索引
只建了分區,但是分區字段沒有建索引
總結
因為每一個表都需要有主鍵這樣可以減少很多鎖的問題,由於上面講過主鍵需要解決全局唯一性並且在插入和更新時可以不需要去掃描全部分區,造成主鍵和分區列必須存在關系;所以最好的分區效果是使用主鍵作為分區字段其次是使用部分主鍵作為分區字段且創建分區字段的索引,其它分區方式都建議不采取。
MYSQL的分區字段,必須包含在主鍵字段內
在對表進行分區時,如果分區字段沒有包含在主鍵字段內,如表A的主鍵為ID,分區字段為createtime ,按時間范圍分區,代碼如下:
CREATE TABLE T1 (
id int(8) NOT NULL AUTO_INCREMENT,
createtime datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE(TO_DAYS (createtime))
(
PARTITION p0 VALUES LESS THAN (TO_DAYS('2010-04-15')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('2010-05-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('2010-05-15')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('2010-05-31')),
PARTITION p4 VALUES LESS THAN (TO_DAYS('2010-06-15')),
PARTITION p19 VALUES LESS ThAN MAXVALUE);
錯誤提示:#1503
MySQL主鍵的限制,每一個分區表中的公式中的列,必須在主鍵/unique key 中包括,在MYSQL的官方文檔里是這么說明的
18.5.1. Partitioning Keys, Primary Keys, and Unique Keys
This section discusses the relationship of partitioning keys with primary keys and unique keys. The rule governing this relationship can be expressed as follows: All columns used in the partitioning expression for a partitioned table must be part of every unique key that the table may have.
In other words,every unique key on the table must use every columnin the table's partitioning expression. (This also includes the table's primary key, since it is by definition a unique key. This particular case is discussed later in this section.) For example, each of the following table creation statements is invalid:
分區字段必須包含在主鍵字段內,至於為什么MYSQL會這樣考慮,CSDN的斑竹是這么解釋的:
為了確保主鍵的效率。否則同一主鍵區的東西一個在A分區,一個在B分區,顯然會比較麻煩。
下面討論解決辦法,畢竟在一張表里,日期做主鍵的還是不常見。
方法1:
順應MYSQL的要求,就把分區字段加入到主鍵中,組成復合主鍵
CREATE TABLE T1 (
id int(8) NOT NULL AUTO_INCREMENT,
createtime datetime NOT NULL,
PRIMARY KEY (id,createtime)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE(TO_DAYS (createtime))
(
PARTITION p0 VALUES LESS THAN (TO_DAYS('2010-04-15')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('2010-05-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('2010-05-15')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('2010-05-31')),
PARTITION p4 VALUES LESS THAN (TO_DAYS('2010-06-15')),
PARTITION p19 VALUES LESS ThAN MAXVALUE);
測試通過,分區成功。
方法2:
既然MYSQL要把分區字段包含在主鍵內才能創建分區,那么在創建表的時候,先不指定主鍵字段,是否可以呢??
測試如下:
CREATE TABLE T1 (
id int(8) NOT NULL ,
createtime datetime NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE(TO_DAYS (createtime))
(
PARTITION p0 VALUES LESS THAN (TO_DAYS('2010-04-15')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('2010-05-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('2010-05-15')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('2010-05-31')),
PARTITION p4 VALUES LESS THAN (TO_DAYS('2010-06-15')),
PARTITION p19 VALUES LESS ThAN MAXVALUE);
測試通過,分區成功。OK
繼續添加上主鍵
alter table t1 add PRIMARY KEY(ID)
錯誤1503,和前面一樣的錯誤。
alter table t1 add PRIMARY KEY(ID,createtime)
創建主鍵成功,但還是復合主鍵,看來是沒辦法了,必須聽指揮了。
主鍵創建成功,把ID加上自增字段設置
alter table t1 change id id int not null auto_increment;
alter table t1 auto_increment=1;
最后結論,MYSQL的分區字段,必須包含在主鍵字段內。
分區表中創建唯一索引:
例如,按create_time進行月分區的表里,唯一索引可能是orderNo,按照上面的要求,唯一索引就成為(order_no,create_time)了。但這樣不滿足業務需求。
解決辦法:
為分區表增加一個before insert觸發器,在插入前查詢下是否已存在即可。
CREATE TRIGGER `trig_insert_t_order` BEFORE INSERT ON `t_order` FOR EACH ROW BEGIN
DECLARE v_count TINYINT UNSIGNED;
DECLARE v_mess_str varchar(100);
SELECT COUNT(1) INTO @v_count
FROM t_order
WHERE order_no = new.order_no
and new.create_time>=date_sub(SYSDATE(),INTERVAL 2 DAY); ## 2天是否足夠
IF (@v_count > 0) THEN
SELECT concat('Duplicate entry ',new.order_no) INTO @v_mess_str;
SIGNAL SQLSTATE '23000' SET MESSAGE_TEXT = @v_mess_str, MYSQL_ERRNO = 1022;
END IF;
END;