一.InnoDB邏輯儲存結構
首先要先介紹一下InnoDB邏輯儲存結構和區的概念,它的所有資料都被邏輯地存放在表空間,表空間又由段,區,頁組成。
段
段就是上圖的segment區域,常見的段有資料段、索引段、回滾段等,在InnoDB儲存引擎中,對段的管理都是由引擎自身所完成的。
區
區就是上圖的extent區域,區是由連續的頁組成的空間,無論頁的大小怎麼變,區的大小預設總是為1MB。為了保證區中的頁的連續性,InnoDB儲存引擎一次從磁碟申請4-5個區,InnoDB頁的大小預設為16kb,即一個區一共有64(1MB/16kb=16)個連續的頁。每個段開始,先用32頁(page)大小的碎片頁來存放資料,在使用完這些頁之後才是64個連續頁的申請。這樣做的目的是,對於一些小表或者是undo類的段,可以開始申請較小的空間,節約磁碟開銷。
頁
頁就是上圖的page區域,也可以叫塊。頁是InnoDB磁碟管理的最小單位。預設大小為16KB,可以通過引數innodb_page_size來設定。常見的頁型別有:資料頁,undo頁,系統頁,事務資料頁,插入緩衝點陣圖頁,插入緩衝空閒列表頁,未壓縮的二進位制大物件頁,壓縮的二進位制大物件頁等。
二.分割槽概述
分割槽
這裡講的分割槽,此“區”非彼“區”,這裡講的分割槽的意思是指將同一表中不同行的記錄分配到不同的物理檔案中,幾個分割槽就有幾個.idb檔案,不是我們剛剛說的區。MySQL在5.1時新增了對水平分割槽的支援。分割槽是將一個表或索引分解成多個更小,更可管理的部分。每個區都是獨立的,可以獨立處理,也可以作為一個更大物件的一部分進行處理。這個是MySQL支援的功能,業務程式碼無需改動。要知道MySQL是面向OLTP的資料,它不像TIDB等其他DB。那麼對於分割槽的使用應該非常小心,如果不清楚如何使用分割槽可能會對效能產生負面的影響。
MySQL資料庫的分割槽是區域性分割槽索引,一個分割槽中既存了資料,又放了索引。也就是說,每個區的聚集索引和非聚集索引都放在各自區的(不同的物理檔案)。目前MySQL資料庫還不支援全域性分割槽。
無論哪種型別的分割槽,如果表中存在主鍵或唯一索引時,分割槽列必須是唯一索引的一個組成部分。
三.分割槽型別
目前MySQL支援一下幾種型別的分割槽,RANGE分割槽,LIST分割槽,HASH分割槽,KEY分割槽。如果表存在主鍵或者唯一索引時,分割槽列必須是唯一索引的一個組成部分。實戰十有八九都是用RANGE分割槽。
RANGE分割槽
RANGE分割槽是實戰最常用的一種分割槽型別,行資料基於屬於一個給定的連續區間的列值被放入分割槽。但是記住,當插入的資料不在一個分割槽中定義的值的時候,會拋異常。RANGE分割槽主要用於日期列的分割槽,比如交易表啊,銷售表啊等。可以根據年月來存放資料。如果你分割槽走的唯一索引中date型別的資料,那麼注意了,優化器只能對YEAR(),TO_DAYS(),TO_SECONDS(),UNIX_TIMESTAMP()這類函式進行優化選擇。實戰中可以用int型別,那麼只用存yyyyMM就好了。也不用關心函式了。
CREATE TABLE `m_test_db`.`Order` ( `id` INT NOT NULL AUTO_INCREMENT, `partition_key` INT NOT NULL, `amt` DECIMAL(5) NULL, PRIMARY KEY (`id`, `partition_key`)) PARTITION BY RANGE(partition_key) PARTITIONS 5( PARTITION part0 VALUES LESS THAN (201901), PARTITION part1 VALUES LESS THAN (201902), PARTITION part2 VALUES LESS THAN (201903), PARTITION part3 VALUES LESS THAN (201904), PARTITION part4 VALUES LESS THAN (201905)) ;
這時候我們先插入一些資料
INSERT INTO `m_test_db`.`Order` (`id`, `partition_key`, `amt`) VALUES ('1', '201901', '1000'); INSERT INTO `m_test_db`.`Order` (`id`, `partition_key`, `amt`) VALUES ('2', '201902', '800'); INSERT INTO `m_test_db`.`Order` (`id`, `partition_key`, `amt`) VALUES ('3', '201903', '1200');
現在我們查詢一下,通過EXPLAIN PARTITION命令發現SQL優化器只需搜對應的區,不會搜尋所有分割槽
如果sql語句有問題,那麼會走所有區。會很危險。所以分割槽表後,select語句必須走分割槽鍵。
以下3種不是太常用,就一筆帶過了。
LIST分割槽
LIST分割槽和RANGE分割槽很相似,只是分割槽列的值是離散的,不是連續的。LIST分割槽使用VALUES IN,因為每個分割槽的值是離散的,因此只能定義值。
HASH分割槽
說到雜湊,那麼目的很明顯了,將資料均勻的分佈到預先定義的各個分割槽中,保證每個分割槽的數量大致相同。
KEY分割槽
KEY分割槽和HASH分割槽相似,不同之處在於HASH分割槽使用使用者定義的函式進行分割槽,KEY分割槽使用資料庫提供的函式進行分割槽。
四.分割槽和效能
一項技術,不是用了就一定帶來益處。比如顯式鎖功能比內建鎖強大,你沒玩好可能導致很不好的情況。分割槽也是一樣,不是啟動了分割槽資料庫就會執行的更快,分割槽可能會給某些sql語句效能提高,但是分割槽主要用於資料庫高可用性的管理。資料庫應用分為2類,一類是OLTP(線上事務處理),一類是OLAP(線上分析處理)。對於OLAP應用分割槽的確可以很好的提高查詢效能,因為一般分析都需要返回大量的資料,如果按時間分割槽,比如一個月使用者行為等資料,則只需掃描響應的分割槽即可。在OLTP應用中,分割槽更加要小心,通常不會獲取一張大表的10%的資料,大部分是通過索引返回幾條資料即可。
比如一張表1000w資料量,如果一句select語句走輔助索引,但是沒有走分割槽鍵。那麼結果會很尷尬。如果1000w的B+樹的高度是3,現在有10個分割槽。那麼不是要(3+3)*10次的邏輯IO?(3次聚集索引,3次輔助索引,10個分割槽)。所以在OLTP應用中請小心使用分割槽表。
在日常開發中,如果想檢視sql語句的分割槽查詢結果可以使用explain partitions + select sql來獲取,partitions標識走了哪幾個分割槽。
mysql> explain partitions select * from TxnList where startTime>'2016-08-25 00:00:00' and startTime<'2016-08-25 23:59:00'; +----+-------------+-------------------+------------+------+---------------+------+---------+------+-------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------------------+------------+------+---------------+------+---------+------+-------+-------------+ | 1 | SIMPLE | ClientActionTrack | p20160825 | ALL | NULL | NULL | NULL | NULL | 33868 | Using where | +----+-------------+-------------------+------------+------+---------------+------+---------+------+-------+-------------+ 1 row in set (0.00 sec)
參考:
《MySQL技術內幕》
第一張圖是從哪裡偷來的忘記了- -!