生活随笔
收集整理的這篇文章主要介紹了
mysql技术分享--表分区实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
分區表
分區概念
分區功能并不是在存儲引擎層完成的,因此不止有InnoDB存儲引擎支持分區,常見的存儲引擎MyISAM,NDB等都支持。但是也并不是所有存儲引擎都支持,比如CSV,FEDERATED,MERGE等就不支持,在使用分區功能之前應該先了解選擇的存儲引擎是否支持分區操作。 MySQL是在5.1版本之后添加了對于分區的支持。功能是將表或者索引分解成更小的多個,更可管理的部分。就訪問數據庫的應用而言,邏輯上還是一個表或者一個索引,但是物理上這個表或者索引可能由數十個物理分區組成,每個分區都是獨立的對象,可以獨立出來,也可以作為一個更大的對象的一部分進行處理。 MySQL數據庫支持的分區類型是水平分區(指同一個表中不同行的記錄分配到不同的物理文件),不支持垂直分區(指將同一個表中不同的列分配到不同的物理文件) MySQL數據庫的分區是局部分區索引,一個分區中即存放了數據又存放了索引。 如下命令查看是否開啟分區功能:
show variables
like '%partition%' show plugins
;
分區功能
我們分區的作用一般是為了讓數據庫變得更快,但是啟用分區后,并不一定會變快。通過經驗來看分區對某些SQL語句的性能會帶來提高,但是分區主要用于高可用,更利于數據庫的管理。 在OLTP應用中,對于分區的使用應該更小心。因為OLTP類型的數據庫對性能要求特別高,也行,分表分區能夠對某一批查詢效率提升,但是可能對其他某些查詢是致命的,導致他需要遍歷N個庫的全量數據,大大增加了IO次數。 先了解當前MySQL數據庫支持的以下幾種類型的分區: RANGE分區:行數據基于屬于一個給定連續區間的值放入分區。MySQL數據庫5.5開始支持RANGE COLUMNS的分區 LIST分區:和RANGE分區類似,只是LIST分區面向的是離散的值。同樣5.5開始支持LIST COLUMNS的分區 HASH分區:根據用戶自定義的表達式的返回值來進行分區,返回值不能為負數。 KEY分區:更具MySQL數據庫提供的哈希函數進行分區
錯誤案例
注意,不論任何類型分區,如果表中存在主鍵或者唯一索引時候,分區必須是唯一索引的一個組成部分,因此如下案例會報錯:
create table t1
(
coll1
int not null ,
coll2
date not null ,
coll3
int not null ,
coll4
int not null ,
UNIQUE key ( coll1
, coll2
)
)
PARTITION by Hash ( coll3
)
PARTITIONS
4 ; [ Err
] 1503 - A
PRIMARY KEY must include
all columns in the
table 's partitioning
function
將PARTITION by Hash(coll3) 修改成PARTITION by Hash(coll1)或者coll2 就能正確創建
唯一索引情況
唯一索引可以允許是NULL值的,并且分區列只要是唯一索引的一個組成部分,不需要整個唯一索引列都是分區列
create table t1
(
coll1
int null ,
coll2
date null ,
coll3
int null ,
coll4
int null ,
UNIQUE key ( coll1
, coll2
, coll3
, coll4
)
)
PARTITION by Hash ( coll3
)
PARTITIONS
4 ;
沒有主鍵或唯一索引情況
當建表時沒有指定主鍵,唯一索引,可以指定任何一個列為分區列,如下兩種情況都可以正確創建案例:
create table t1
(
coll1
int not null ,
coll2
date not null ,
coll3
int not null ,
coll4
int not null
)
PARTITION by Hash ( coll3
)
PARTITIONS
4 ;
create table t1
(
coll1
int not null ,
coll2
date not null ,
coll3
int not null ,
coll4
int not null ,
key ( coll4
)
)
PARTITION by Hash ( coll3
)
PARTITIONS
4 ;
RANGE分區
RANGE分區是常用的一種分區類型。如下案例,我們create table語句創建了一個id列的區間分區表。當id小于10 時候,數據插入P0分區。當大于等于10 小于等于20 插入p1:
create table t
(
id
int
) ENGINE = INNODB
PARTITION by RANGE
( id
) (
PARTITION p0
VALUES less than
( 10 ) ,
PARTITION p1
VALUES less than
( 20 )
) ;
insert into t
values ( 9 ) , ( 10 ) , ( 15 )
插入數據后,因為t根據id進行分區,因此數據是根據id列的值的范圍存放在不同的物理文件中,可以通過查information_schema 架構在的 PARTITIONS 表來查看每個分區的具體信息,在數據庫中有一個 information_schema數據庫
select * from information_schema
. PARTITIONS
where TABLE_SCHEMA
= DATABASE ( ) and TABLE_NAME
= 't' ;
TABLE_ROWS列說明了每個分區記錄中記錄的數量,由于之前項表中插入了9,10,15三條記錄,因此可以看到,當前分區P0有一條記錄,分區p1 有2條記錄,PARTITION_METHOD 標識分區的類型,這里顯示的是RANGE 對于表t,因為我們定義了分區,因此對于插入的值都嚴格遵循分區的定義,當插入不屬于分區的數據時候,會拋出異常:
[ SQL ]
insert INTO t
select 30 ;
[ Err
] 1526 - Table has
no partition for value 30
應該添加新的范圍,使得數據庫分區能夠兼容所有的數據清空,如下:
alter table t
add PARTITION ( PARTITION p2
values less than MAXVALUE
) ;
insert INTO t
select 30 ;
RANGE日期分區
RANGE分區主要用于日期列的分區,如對于銷售類的表,可以根據年份來分區存儲銷售數據,如下案例:
create table sales
(
money
int UNSIGNED not null ,
date datetime
) ENGINE = INNODB
PARTITION by range
( YEAR ( date ) ) (
PARTITION p2008
values less than
( 2009 ) ,
PARTITION p2009
values less than
( 2010 ) ,
PARTITION p2010
values less than
( 2011 )
) ;
insert into sales
values ( 100 , '2008-01-01' ) ,
( 100 , '2008-02-01' ) ,
( 200 , '2008-01-02' ) ,
( 100 , '2009-03-01' ) ,
( 200 , '2010-03-01' ) select * from information_schema
. PARTITIONS
where TABLE_SCHEMA
= DATABASE ( ) and TABLE_NAME
= 'sales' ;
如上創建好,可以對sales這張表按時間處理賬本,如果我們需要刪除18年的數據,就不需要去delete from sales where date >=‘2008-01-01’ and date < ‘2009-01-01’ ,我們可以直接刪掉p2008分區即可,如下操作
alter table sales
drop PARTITION p2008
;
分區操作還有另外一個好處,可以加快某些查詢的操作,如果我們只需要查詢19年的銷售額,如下:
EXPLAIN partitions
select * from sales
where date >= '2009-01-01' and date < '2009-12-31'
’
如上通過explain partitions 命令,上述語句sql優化器只需要搜索p2009分區,不用去全表掃描數據,大大提高執行速度,但是如上語句則不同:
EXPLAIN partitions
select * from sales
where date >= '2009-01-01' and date < '2020-01-01'
條件修改成了< 2020-01-01,這個時候優化器選擇p2009,p2010兩個分區,這是不希望看到的。因此對于啟用分區應該更具分區的特點編寫sql語句
如果我們需要更細分,按月來分區,如下分區方式第查詢來說是無效的,如下:
create table sales
(
money
int UNSIGNED not null ,
date datetime
) ENGINE = INNODB
PARTITION by range
( YEAR ( date ) * 100 + MONTH ( date ) ) (
PARTITION p201001
values less than
( 201002 ) ,
PARTITION p201002
values less than
( 201003 ) ,
PARTITION p201003
values less than
( 201004 )
) ;
insert into sales
values ( 100 , '2008-01-01' ) ,
( 100 , '2008-02-01' ) ,
( 200 , '2008-01-02' ) ,
( 100 , '2009-03-01' ) ,
( 200 , '2010-03-01' ) EXPLAIN partitions
select * from sales
where date >= '2010-01-01' and date < '2010-01-31'
如上查詢語句掃描的全分區,但是之前的邏輯應該只掃p2020001分區,這個問題原因在于RANGE分區的查詢,優化器只能對YEAR(),TO_DAYS(),TO_SECONDS(),UNIX_TIMESTAMP()這類函數進行優化選擇,因此對于上述要求,需要將分區函數改為TO_DAY,如下:
create table sales
(
money
int UNSIGNED not null ,
date datetime
) ENGINE = INNODB
PARTITION by range
( TO_DAYS
( date ) ) (
PARTITION p201001
values less than
( TO_DAYS
( '2010-02-01' ) ) ,
PARTITION p201002
values less than
( TO_DAYS
( '2010-03-01' ) ) ,
PARTITION p201003
values less than
( TO_DAYS
( '2010-04-01' ) )
) ; EXPLAIN partitions
select * from sales
where date >= '2010-01-01' and date < '2010-01-31'
LIST分區
LIST分區和RANGE分區類似,只是分區列的值是離散的,不連續,如下:
create table t
(
a
int ,
b
int
) engine = INNODB
PARTITION by list
( b
) (
PARTITION p0
values in ( 1 , 3 , 5 , 7 , 9 ) ,
PARTITION p1
values in ( 0 , 2 , 4 , 6 , 8 )
) ; insert into t
values ( 1 , 1 ) ,
( 1 , 2 ) ,
( 1 , 3 ) ,
( 1 , 4 ) select * from information_schema
. PARTITIONS
where TABLE_SCHEMA
= DATABASE ( ) and TABLE_NAME
= 't' ;
如上,按奇數偶數分區,如果插入的值不值分區的定義,MySQL會拋出異常:
[ SQL ] insert into t
values ( 1 , 100 ) [ Err
] 1526 - Table has
no partition for value 100
而且List有一個特殊地方,在insert插入多行時候,遇到未分區定義的值時候,MyISAM和InnoDB存儲引擎的處理完全不同,MyISAM會將之前的行都insert,之后的不成功,InnoDB存儲引擎會將他看成是一個事務,因此都會失敗,
[ SQL ] insert into t
values ( 1 , 1 ) ,
( 1 , 2 ) ,
( 1 , 3 ) ,
( 1 , 4 ) ,
( 1 , 100 ) [ Err
] 1526 - Table has
no partition for value 100
如上是InnoDB存儲引擎,返回的是沒有對應的分區值,并且數據全失敗,
create table t
(
a
int ,
b
int
) engine = MyISAM
PARTITION by list
( b
) (
PARTITION p0
values in ( 1 , 3 , 5 , 7 , 9 ) ,
PARTITION p1
values in ( 0 , 2 , 4 , 6 , 8 )
) ;
insert into t
values ( 1 , 1 ) ,
( 1 , 2 ) ,
( 1 , 3 ) ,
( 1 , 4 ) ,
( 1 , 100 )
select * from information_schema
. PARTITIONS
where TABLE_SCHEMA
= DATABASE ( ) and TABLE_NAME
= 't' ;
可以看到,插入的1,10,記錄是沒有成功的,之前的2,3,4都是已經成功insert。
HASH分區
HASH分區目的是將數據均由的分布的預定的各個分區中,保證個分區的數據大致一樣。在RANGE和LIST分區中,必須明確知道一個列值或者列值的集合。而在HASH分區中MySQL自動完成了這些工作,你需要做的是基于要被hash的列值指定一個列值或者表達式,以及指定備份區的表要被分割的分區數量,如下案例
create table t_hash
(
a
int ,
b
datetime
) ENGINE = INNODB
PARTITION by hash ( YEAR ( b
) )
PARTITIONS
4 ;
如上hash規則,如果b列的值2010-04-01,那么這個記錄在t_hash中,保存的確認值如下
MOD ( YEAR ( '2010-04-01' ) , 4 ) = MOD ( 2010 , 4 ) = 2
insert into t_hash
values ( 1 , '2010-04-01' ) select * from information_schema
. PARTITIONS
where TABLE_SCHEMA
= DATABASE ( ) and TABLE_NAME
= 't_hash' ;
如上數據落在了P2分區 MySQL數據庫還支持一種LINEAR HASH的分區,更負責的分區算法,
create table t_hash
(
a
int ,
b
datetime
) ENGINE = INNODB
PARTITION by LINEAR
HASH ( YEAR ( b
) )
PARTITIONS
4 ;
MySQL數據庫用一下方式判斷分區 取大于分區數量4 的下一個2 的冪值V, V = POWRE(2, CEILING(LOG(2,num))) = 4 所在分區N= YEAR(‘2010-04-01’) & (V-1) = 2 得到的分區還是2,但是HASH算法與之前的完全不同, LINEAR HASH分區的有點在于,增加,刪除,合并和拆分分區變得更加快速,這有利于處理含有大量數據的表,缺點在于與使用HASH分區得到的數據分布相比,各個分區數據的分布不均勻。
KEY分區
上一篇:mysql技術分享-- 視圖是什么
總結
以上是生活随笔 為你收集整理的mysql技术分享--表分区实现 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。