Mysql優化-分區

 

分區簡介mysql

  分區是根據必定的規則,數據庫把一個表分解成多個更小的、更容易管理的部分。就訪問數據庫應用而言,邏輯上就只有一個表或者一個索引,但實際上這個表可能有N個物理分區對象組成,每一個分區都是一個獨立的對象,能夠獨立處理,能夠做爲表的一部分進行處理。分區對應用來講是徹底透明的,不影響應用的業務邏輯。算法

  分區有利於管理很是大的表,它採用分而治之的邏輯,分區引入了分區鍵的概念,分區鍵用於根據某個區間值(或者範圍值)、特定值列表或者 hash函數值執行數據的彙集,讓數據根據規則分佈在不一樣的分區中,讓一個大對象變成多個小對象。  
sql

  Mysql分區既能夠對數據進行分區也能夠對索引進行分區。數據庫

 

MySQL分區表對分區字段的限制服務器

  分區的字段,必須是表上全部的惟一索引(或者主鍵索引)包含的字段的子集less

  換句話說:(全部的)字段必須出如今(全部的)惟一索引或者主鍵索引的字段中,或者說:一個表上有一個或者多個惟一索引的狀況下,分區的字段必須被包含在全部的主鍵或者惟一索引字段中。函數

 

分區類型大數據

  range分區:基於一個給定的連續區間範圍(區間要求連續而且不能重疊),把數據分配到不一樣的分區優化

  list分區:相似於range分區,區別在於list分區是基於枚舉出的值列表分區,range是基於給定的連續區間範圍分區spa

  hash分區:基於給定的分區個數,把數據分配到不一樣的分區

  key分區:相似於hash分區

  

MySQL分區的優勢主要包括如下4個方面

  1)和單個磁盤或者文件系統分區相比,能夠存儲更多數據

  2)優化查詢。在where子句中含有分區條件時,能夠只掃描必要的一個或者多個分區來提升查詢效率;同時,在設計sum()和count() 這類聚合函數查詢時,能夠容易的在每一個分區上並行處理,最終只須要彙總全部分區獲得的結果

  3)對於已通過期或者不須要保存的數據,能夠經過刪除與這些數據有關的分區來快速刪除數據

  4)跨多個磁盤來分散數據查詢,以得到更大的查詢吞吐量

 

  分區和水平分表功能相似,將一個大表的數據分割到多張小表中去,因爲查詢不須要全表掃描了,只要掃描某些分區,因此分區能提升查詢速度。

 

  水平分表須要用戶預先手動顯式建立出多張分表(如 tbl_user0,tbl_user1,tbl_user2),在物理上實實在在的建立多張表,經過客戶端代理(Sharding-JDBC等)或者中間件代理(mycat等)來實現分表邏輯。

  分區是Mysql的一個插件Plugin功能,將一張大表的數據在數據庫底層分紅多個分區文件(如 tbl_user#P#p0.ibd,tbl_user#P#p1.ibd,tbl_user#P#p2.ibd),和水平分表不一樣的是分區不須要顯示的建立「分表」,數據庫會自動建立分區文件,用戶看到的只是一張普通的表,實際上是對應的多個分區,這個是對用戶是屏蔽、透明的,在使用上和使用一張表徹底同樣,不須要藉助任何功能來實現。分區是一種邏輯上的水平分表,在物理層面仍是一張表。

 

數據庫文件

create table tbl_user_innodb(

  id int(11) not null auto_increment,

  username varchar(255),

  email varchar(20),

  age tinyint(4),

  type int(11),

  create_time datetime default current_timestamp,

  primary key (id)

) engine=InnoDB auto_increment=5100002;

 

create table tbl_user_myisam(

  id int(11) not null auto_increment,

  username varchar(255),

  email varchar(20),

  age tinyint(4),

  type int(11),

  create_time datetime default current_timestamp,

  primary key (id)

) engine=myisam auto_increment=5100002;

 

  經過show variables like '%datadir%'; 命令查看mysql的data存放目錄,進入所在的數據庫目錄,不一樣的引擎數據庫文件格式不一樣

 

  myisan: .frm 存儲表結構 + .MYD 存儲表數據 + .MYI 存儲索引文件

  innodb: .frm 存儲表結構 + .ibd 存儲數據和索引 (其只有設置成獨立表空間才能成功表分區)

 

 插入500w條數據

 

range分區

  Mysql有五種分區類型,range、list、hash、key、子分區,其中最經常使用的是range 和 list 分區

  

  -- 查看mysql版本

  select version();

 

  -- 查看分區插件是否激活 partition active

  show plugins;

 

  對於低版本的 mysql,若是InnoDB引擎要想分區成功,須要在 my.conf 中設置 innodb_file_per_table=1 設置獨立表空間(即,每張表都有對應的.ibd文件)

 

  range 分區:給定一個連續區間的範圍值進行分區,某個字段的值知足這個範圍就會被分配到該區。適用於字段的值是連續的區間的字段,如,日期範圍,連續的數字

  -- 語法

  create table <tablename> (

  // 字段

  )ENGIN=MyISAM DEFAULT CHARSET=UTF-8 AUTO_INCREMENT=1

  partition by range (分區字段) (

  partition <分區名稱> values less than (value),

  partition <分區名稱> values less than (value),

  ...

  partition <分區名稱> values less than maxvalue

  );

  range 表示按範圍分區

  分區字段:表示要按照哪一個字段進行分區,能夠是一個字段名,也能夠是對某個字段進行表達式運算,如 year(create_time),使用 range 最終的值必須是數字

  分區名稱:要保證不一樣,也能夠採用 p0,p1,p2 這樣的分區名稱

  value:表示要小於某個具體的值,如 less than (10),那麼分區字段的值小於10的都會被分到這個分區

  maxvalue:表示一個最大值

  注意:range 對應的分區鍵值必須是數字值,可使用 range columns(分區字段) 對非 int 型作分區,如字符串,對於日期類型的可使用year()、to_days()、to_seconds() 等函數

  

  create table emp_date(

    id int not null,

    separated date not null default '9999-12-31'

  )  partition by range columns(separated) (

    partition p0 values less than ('1990-01-01'),

    partition p1 values less than ('2001-01-01'),

    parttion p2 values less than ('2018-01-01')

  )

 

  分區可在建立表的時候進行分區,也能夠在建立表以後進行分區

  alter table <table> partition by range(id) (

    partition p0 values less than (100000),

    partition p1 values less than (200000),

    partition p2 values less than (300000)

  );

  在建立分區的時候,常常會遇到這個錯誤:A PRIMARY KEY must include all columns in the table's partition function。意思是分區的字段必需要包含在主鍵當中。可使用 PRIMARY KEY(id, xxx) 來將多個字段做爲主鍵。在作分區表時,選擇分區的依據字段時要謹慎,須要仔細斟酌這個字段拿來做爲分區依據是否合適,這個字段加入到主鍵中做爲複合主鍵是否適合。

 

  使用 range 分區時,表結構要麼沒有主鍵,要麼分區字段必須是主鍵。

 

 

list分區

  設置若干個固定值進行分區,若是某個字段的值在這個設置的值列表中就會被分配到該分區。適用於字段的值區分度不高的,或者值有限的,特別像枚舉這樣特色的列。list 分區使用 in 表示一些固定的值的列表

  -- 語法

  create table <table> (

  //字段

  ) partition by list (分區字段或者基於該字段的返回的整數值的表達式) (

  partition <分區名稱> values IN (value1, value2,value3),

  ...

  partition <分區名稱> values IN (value4, value5)

  )

 

columns分區

  在mysql5.5以前 range分區和list分區只支持整數分區,能夠經過額外的函數運算或額外的轉換從而獲得一個整數。columns 分區分爲 range columns 和 list columns 兩種,支持整數(tinyint 到 bigint,不支持 decimal 和 float)、日期(date、datetime)、字符串(char、varchar、binary、varbinary)三大數據類型。

  columns 分區支持一個或者多個字段做爲分區鍵,不支持表達式做爲分區鍵,這點區別於 range 和 list 分區。須要注意的是 range columns 分區鍵的比較是基於元組的比較,也就是基於字段組的比較,這和 range 分區有差別。

  create table rc3(

  a int,

  b int

  ) partition by range columns(a, b) (

  partition p01 values less than (0, 10),

  partition p02 values less than (10, 10),

  partition p03 values less than (10, 20),

  partition p04 values less than (10, 35),

  partition p05 values less than (10, maxvalue),

  partition p06 values less than (maxvalue, maxvalue)

  )

  range columns 分區鍵的比較(元組的比較)其實就是多列排序,先根據 a 字段排序,再根據 b 字段排序,根據排序結果來分區存放數據,和 range 單字段的分區排序規則其實是同樣的。

 

hash分區

 

  hash分區主要用來分散熱點讀,確保數據在預先肯定個數的分區中可能的平均分佈。對一個表執行 hash 分區時,mysql 會對分區鍵應用一個散列函數,以此肯定數據應當放在N個分區中的哪一個分區。

  mysql支持兩種 hash 分區,常規hash分區和線性hash分區(linear hash分區)  

  常規hash分區使用的是取模算法,對應一個表達式 expr 是能夠計算出它被保存在哪一個分區中,N=MOD(expr, num)

  線性 hash 分區使用的是一個線性的2的冪運算法則。

  對指定的字段(整數字段)進行哈希,將記錄平均的分配到分區中,使得全部的分區的數據比較平均。hash 分區只須要指定分區的字段和要分紅幾個分區

  expr是一個字段值或者基於某列值運算返回的一個整數,expr能夠是 mysql 中有效的任何函數或者其餘表達式,只要他們返回一個即很是熟也非隨機數的整數。

  num 表示分區的數量(看得出來,他是模運算的基數)

  create table <table> (

  // 字段

  ) engine=數據庫引擎 default charset=utf8 auto_increment=1

  partition by hash(expr)

  partitions <num>

 

  常規 hash 分區方式看上去不錯,經過取模的方式來讓數據儘量平均分佈在每一個分區,讓每一個分區管理的數據都減小,提升查詢效率,但是當咱們要增長分區或者合併分區,問題就來了,假設原來是5個分區,如今須要增長一個分區,原來的取模算法是MOD(expr, 5),根據餘數 0-4 分佈在5個分區中,如今新增一個分區後,取模算法變成 MOD(expr, 6),根據餘數 0-5 分區在6個分區中,原來5個分區的數據大部分都須要從新計算進行從新分區。

  常規 hash 分區在管理上帶來的代價太大,不適合須要靈活變更分區的需求。爲了下降分區管理上的代價,mysql提供了線性 hash 分區,分區函數是一個線性的 2 的冪的運算法則。一樣線性 hash 分區的記錄被存在哪一個分區也能被計算出來。線性hash分區的優勢是在分區維護(增長、刪除、合併、拆分分區)時,mysql可以處理的更加迅速,缺點是:對比常規hash分區,線性hash 各個分區之間數據的分佈不太均衡。

  create table <tablename> (

  // 字段

  ) engine=數據引擎 default charset = utf-8 auto_increment=1

  partition by linear hash(expr)

  partitions <num>;

 

 

key分區  

 

  按照key進行分區很是相似於按照 hash 進行分區,只不過 hash 分區容許使用用戶自定義的表達式,而key分區不容許使用自定義的表達式,須要使用 mysql 服務器提供的 hash 函數,同事 hash 分區只支持整數分區,而 key 分區支持使除了 blob or text 類型外的其餘類型做爲分區鍵。

  和 hash 功能同樣,不一樣的是分區的字段能夠是非 int 類型,如字符串、日期類型。

  可使用 partition by key(expr) 子句來建立一個 key 分區表,expr 是零個或者多個字段名的列表。key 分區也支持線性分區 linear key

 

  partition by key(expr) partitions num;

  -- 不指定默認首選主鍵做爲分區鍵,在沒有主鍵的狀況下會選擇非空惟一鍵做爲分區鍵

  partition by key() partitions num;

  -- linear key

  partition by linear key(expr);

 

 

子分區

  

  子分區是分區表中對每一個分區的再次分割,又被稱爲複合分區,支持對 range 和 list 進行子分區,子分區既可使用 hash分區,也可使用 key分區。符合分區適用於保存很是大量數據記錄。

 

管理分區

 

  mysql 不由止在分區鍵值上使用 null,分區鍵多是一個字段或者一個用戶的定義的表達式,通常狀況下,mysql 的分區把 null 值當作零值或者一個最小值進行處理。range 分區中,null 值會被當作最小值來處理;list分區中 null 值必須出如今枚舉列表中,不然不被接受;hash/key 分區中,null 值會被當作 0 值來處理。

 

  mysql 提供了添加、刪除、重定義、合併、拆分分區的命令,這些操做均可以經過 alter table 命令來實現

 

  -- 刪除 list 或者 range 分區(同時刪除分區對應的數據)

  alter table <table> drop partition <分區名稱>;

 

  -- 新增分區

  -- range 添加新分區

  alter table <table> add partition(partition p4 values less than MAXVALUE);

 

  -- list 添加新分區

  alter table <table> add partition(partition p4 values in (25,26,28));

 

  -- hash 從新分區

  alter table <table> add partition partitions 4;

 

  -- key 從新分區

  alter table <table> add partition partition 4;

  

  -- 子分區添加新分區,雖然沒有指定子分區,可是系統會給子分區命名

  alter table <table> add partition(partition p3 values less than MAXVALUE)

 

  -- range 從新分區

  alter table user reorganize partition p0,p1,p2,p3,p4 into (partition p0 values less than maxvalue);

 

  -- list 從新分區

  alter table <table> reorganize partition p0,p1,p2,p3,p4 into (partition p0 values in (1,2,3,4,5))

 

分區優勢

  1. 分區能夠分在多個磁盤,存儲更大一些

  2. 根據查找條件,只查找相應的分區,不用所有查找

  3. 進行大數據搜索時,能夠並行處理

  4. 跨多個磁盤來分散數據查詢,來得到更大的查詢吞吐量

  

 https://blog.csdn.net/vbirdbest/article/details/82461109

相關文章
相關標籤/搜索