天天看點

MySQL分區二:資料庫檔案三:插入500W條資料四:range分區五:list 分區columns分區六:hash分區七:key分區八:子分區九:管理分區

分區和手動分表對比

mysql的分區操作還比較簡單,好處是也不用自己動手建表進行分區,和水準分表有點像

分表 分區
手動分表  分區
多張資料表 一張資料表
重複資料的風險 沒有資料重複的風險
寫入多張表 寫入一張表
沒有統一的限制限制 強制的限制限制

1、mysql分區簡介?

表分區:分區是根據一定的規則,資料庫把一個表分解成多個更小的、更容易管理的部分。就通路資料庫應用而言,邏輯上就隻有一個表或者一個索引,但實際上這個表可能有N個實體分區對象組成,每個分區都是一個獨立的對象,可以獨立處理,可以作為表的一部分進行處理。

分區不影響程式員程式設計:分區對應用來說是完全透明的,不影響應用的業務邏輯。

2、mysql分區注意?

主鍵/唯一鍵:無論哪種分區,要麼你分區表上沒有主鍵/唯一鍵,要麼分區表的主鍵/唯一鍵都必須包含分區鍵,也就是說不能使用主鍵/唯一鍵字段之外的其它字段分區。

3、分區能做什麼

  • 邏輯資料分割
  • 提高單一的寫和讀應用速度
  • 提高分區範圍讀查詢的速度
  • 分割資料能夠有多個不同的實體檔案路徑
  • 高效的儲存曆史資料
  • 一個表上的限制檢查
  • 不同的主從伺服器分區政策,例如master按Hash分區,slave按range分區

 MySQL可以對索引進行分區麼?

可以:MySQL分區即可以對資料進行分區,也可以對索引進行分區。

4 分區的限制(截止5.1.44版)

•   隻能對資料表的整型列進行分區,或者資料列可以通過分區函數轉化成整型列

•   最大分區數目不能超過1024

•   如果含有唯一索引或者主鍵,則分區列必須包含在所有的唯一索引或者主鍵在内

•   不支援外鍵

•   不支援全文索引(fulltext)

  • 按日期進行分區很非常适合,因為很多日期函數可以用。但是對于字元串來說合适的分區函數不太多

5,什麼時候使用分區

•   海量資料表

•   曆史表快速的查詢,可以采用ARCHIVE+PARTITION的方式。

•   資料表索引大于伺服器有效記憶體

•   對于大表,特别是索引遠遠大于伺服器有效記憶體時,可以不用索引,此時分區效率會更有效。

6、mysql分區類型?

range分區(常用):基于一個給定的連續區間範圍(區間要求連續并且不能重疊),把資料配置設定到不同的分區

list分區:類似于range分區,差別在于list分區是居于枚舉出的值清單分區,range是基于給定的連續區間範圍分區

hash分區:基于給定的分區個數,把資料配置設定到不同的分區

key分區:類似于hash分區

7、mysql分區優勢?

更多資料:和單個磁盤或者檔案系統分區相比,可以存儲更多資料

優化查詢:在where子句中包含分區條件時,可以隻掃描必要的一個或者多個分區來提高查詢效率;同時在涉及sum()和count()這類聚合函數的查詢時,可以容易的在每個分區上并行處理,最終隻需要彙總所有分區得到的結果

快速删除:對于已經過期或者不需要儲存的資料,可以通過删除與這些資料有關的分區來快速删除資料

更大吞吐量:跨多個磁盤來分散資料查詢,以獲得更大的查詢吞吐量

8、分區和水準分表的相似和差別?

大表的資料分割到多張小表:分區和水準分表功能類似,将一個大表的資料分割到多張小表中去,由于查詢不需要全表掃描了,隻需要掃描某些分區,是以分區能提高查詢速度。

水準分表:使用者設定:需要使用者預先手動顯式建立出多張分表(如tbl_user0, tbl_user1, tbl_user2),在實體上實實在在的建立多張表,通過用戶端代理(Sharding-JDBC等)或者中間件代理(Mycat等)來實作分表邏輯。

分區:mysql預設幫你做:是MySQL的一個插件Plugin功能,将一張大表的資料在資料庫底層分成多個分區檔案(如tbl_user#P#p0.ibd, tbl_user#P#p1.ibd, tbl_user#P#p2.ibd),和水準分表不同的是分區不需要顯式的建立“分表”

分區:mysql預設幫你做,是MySQL的一個插件Plugin功能,将一張大表的資料在資料庫底層分成多個分區檔案(如tbl_user#P#p0.ibd, tbl_user#P#p1.ibd, tbl_user#P#p2.ibd),和水準分表不同的是分區不需要顯式的建立“分表”,資料庫會自動建立分區檔案的,使用者看到的隻是一張普通的表,其實是對應的是多個分區,這個是對使用者是屏蔽的、透明的,在使用上和使用一張表完全一樣,不需要借助任何功能來實作。分區是一種邏輯上的水準分表,在實體層面還是一張表。

7、mysql什麼指令檢視mysql的data存放目錄?

show variables like ‘%datadir%’:通過show variables like ‘%datadir%’;指令檢視mysql的data存放目錄,進入所在的資料庫目錄(如test),不同的引擎資料庫檔案格式不同

總結:show variables like "" 的功能好像啥都可以做,也很容易了解

8、mysql的range分區執行個體?

PARTITION BY RANGE (age) (
    PARTITION p0 VALUES LESS THAN (20),
    PARTITION p1 VALUES LESS THAN (40),
    PARTITION p2 VALUES LESS THAN (60),
    PARTITION p3 VALUES LESS THAN (80),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);
           

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

2、分區鍵值必須是數字值:range 對應的分區鍵值必須是數字值,可以使用range columns(分區字段) 對非int型做分區,如字元串,對于日期類型的可以使用year()、to_days()、to_seconds()等函數

3、分區可以在建立表的時候進行分區,也可以在建立表之後進行分區

9、mysql中range分區 注意?

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

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

二:資料庫檔案

CREATE TABLE `tbl_user_innodb` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `username` varchar(255) DEFAULT NULL,
   `email` varchar(20) DEFAULT NULL,
   `age` tinyint(4) DEFAULT NULL,
   `type` int(11) DEFAULT NULL,
   `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=5100002 DEFAULT CHARSET=utf8;

 CREATE TABLE `tbl_user_myisam` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `username` varchar(255) DEFAULT NULL,
   `email` varchar(20) DEFAULT NULL,
   `age` tinyint(4) DEFAULT NULL,
   `type` int(11) DEFAULT NULL,
   `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
 ) ENGINE=myisam AUTO_INCREMENT=5100002 DEFAULT CHARSET=utf8;
           

通過show variables like ‘%datadir%’;指令檢視mysql的data存放目錄,進入所在的資料庫目錄(如test),不同的引擎資料庫檔案格式不同

  • myisam
    • .frm : 存儲表結構
    • .MYD : 存儲表資料
    • .MYI : 存儲索引檔案
  • innodb: 隻有設定成獨立表空間才能做成功表分區
    • .frm : 表結構
    • .ibd : 資料 + 索引
MySQL分區二:資料庫檔案三:插入500W條資料四:range分區五:list 分區columns分區六:hash分區七:key分區八:子分區九:管理分區

三:插入500W條資料

CREATE TABLE `tbl_user_no_part` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `username` varchar(255) DEFAULT NULL,
   `email` varchar(20) DEFAULT NULL,
   `age` tinyint(4) DEFAULT NULL,
   `type` int(11) DEFAULT NULL,
   `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- 修改mysql預設的結束符号,預設是分号;但是在函數和存儲過程中會使用到分号導緻解析不正确
delimiter $$

-- 随機生成一個指定長度的字元串
create function rand_string(n int) returns varchar(255) 
begin 
 # 定義三個變量
 declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
 declare return_str varchar(255) default '';
 declare i int default 0;

 while i < n do 
   set return_str = concat(return_str, substring(chars_str, floor(1+rand()*52), 1));
   set i = i + 1;
 end while;
 return return_str;
end $$

-- 建立插入的存儲過程
create procedure insert_user(in start int(10), in max_num int(10))
begin
    declare i int default 0; 
    set autocommit = 0;  
    repeat
        set i = i + 1;
        insert into tbl_user_no_part values ((start+i) ,rand_string(8), concat(rand_string(6), '@random.com'), 1+FLOOR(RAND()*100), 3, now());
        until i = max_num
    end repeat;
   commit;
end $$

-- 将指令結束符修改回來
delimiter ;

-- 調用存儲過程,插入500萬資料,需要等待一會時間,等待執行完成
call insert_user(100001,5000000);
-- Query OK, 0 rows affected (7 min 49.89 sec) 我的Macbook Pro i5 8G記憶體用了8分鐘才執行完

select count(*) from tbl_user_no_part;
           

四: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檔案
innodb_file_per_table=1
           

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

-- 文法
create table <table> (
    // 字段
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 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 這樣的分區名稱,

less than : 表示小于

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) (
    partiontion p0 values less than ('1990-01-01'),
    partiontion p0 values less than ('2001-01-01'),
    partiontion p0 values less than ('2018-01-01')
);
           

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

alter table <table> partition by RANGE(id) (
    PARTITION p0 VALUES LESS THAN (1000000),
    PARTITION p1 VALUES LESS THAN (2000000),
    PARTITION p2 VALUES LESS THAN (3000000),
    PARTITION p3 VALUES LESS THAN (4000000),
    PARTITION p4 VALUES LESS THAN MAXVALUE 
);
           
-- 建立分區表
CREATE TABLE `tbl_user_part` (
   `id` int(11) NOT NULL ,
   `username` varchar(255) DEFAULT NULL,
   `email` varchar(20)     DEFAULT NULL,
   `age` tinyint(4)        DEFAULT NULL,
   `type` int(11)          DEFAULT NULL,
   `create_time` datetime  DEFAULT CURRENT_TIMESTAMP
   -- PRIMARY KEY (`id`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE (age) (
    PARTITION p0 VALUES LESS THAN (20),
    PARTITION p1 VALUES LESS THAN (40),
    PARTITION p2 VALUES LESS THAN (60),
    PARTITION p3 VALUES LESS THAN (80),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);
           
MySQL分區二:資料庫檔案三:插入500W條資料四:range分區五:list 分區columns分區六:hash分區七:key分區八:子分區九:管理分區

在建立分區的時候經常會遇到這個錯誤:A PRIMARY KEY must include all columns in the table’s partitioning function。意思是說分區的字段必須是要包含在主鍵當中。 可以使用PRIMARY KEY (

id

,

xxx

)來将多個字段作為主鍵。在做分區表時,選擇分區的依據字段時要謹慎,需要仔細斟酌這個字段拿來做為分區依據是否合适,這個字段加入到主鍵中做為複合主鍵是否适合。

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

-- 将tbl_user_no_part表中的資料複制到tbl_user_part表中(資料量比較多,可能要等幾分鐘)
INSERT INTO tbl_user_part SELECT * FROM tbl_user_no_part;

SELECT count(*) FROM tbl_user_no_part WHERE age > 25 AND age < 30;
SELECT count(*) FROM tbl_user_part WHERE age > 25 AND age < 30;
           
MySQL分區二:資料庫檔案三:插入500W條資料四:range分區五:list 分區columns分區六:hash分區七:key分區八:子分區九:管理分區

從查詢結果看,當查詢條件中包括分區字段時,分區确實能提高查詢效率

五:list 分區

設定若幹個固定值進行分區,如果某個字段的值在這個設定的值清單中就會被配置設定到該分區。适用于字段的值區分度不高的,或者值是有限的,特别是像枚舉這樣特點的列。list分區使用in表示一些固定的值的清單

-- 文法
create table <table> (
    // 字段
) ENGINE=資料庫引擎  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
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 talbe 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),
);

insert into rc3(a, b) values(1, 10);

select (1, 10) < (10, 10) from dual;

-- 根據結果存放到p02分區上了
select
    partition_name,
    partition_expression,
    partition_description,
    table_rows
from information_schema.partitions
where table_schema = schema() and table_name = 'rc3';   
           

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 表示分區數量

-- HASH
create table <table> (
    // 字段
) ENGINE=資料庫引擎  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
PARTITION BY HASH(expr)
PARTITIONS <num>;
           

正常hash分區方式看上去挺不錯的,通過取模的方式來資料盡可能平均分布在每個分區,讓每個分區管理的資料都減少,提高查詢效率,可是當我們要增加分區時或者合并分區,問題就來了,假設原來是5個正常hash分區,現在需要增加一個正常分區,原來的取模算法是MOD(expr, 5), 根據餘數0~4分布在5個分區中,現在新增一個分區後,取模算法變成MOD(expr, 6),根據餘數0~6分區在6個分區中,原來5個分區的資料大部分都需要通過重新計算進行重新分區。

正常hash分區在管理上帶來了的代價太大,不适合需要靈活變動分區的需求。為了降低分區管理上的代價,mysql提供了線性hash分區,分區函數是一個線性的2的幂的運算法則。同樣線性hash分區的記錄被存在那個分區也是能被計算出來的。線性hash分區的優點是在分區維護(增加、删除、合并、拆分分區)時,mysql能夠處理的更加迅速,缺點是:對比正常hash分區,線性hash各個分區之間資料的分布不太均衡。

-- LINEAR HASH
create table <table> (
    // 字段
) ENGINE=資料庫引擎  DEFAULT CHARSET=utf8 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)
           
create table <table> (
    // 字段
) ENGINE=資料庫引擎  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
PARTITION BY HASH(分區字段名)
PARTITIONS <count>;
           

八:子分區

子分區(subpartition):是分區表中對每個分區的再次分割,又被稱為複合分區,支援對range和list進行子分區,子分區即可以使用hash分區也可以使用key分區。複合分區适用于儲存非常大量的資料記錄。

-- 根據年進行分區
-- 再根據天數分區
-- 3個range分區(p0,p1,p2)又被進一步分成2個子分區,實際上整個分區被分成了 3 x 2 = 6個分區
create table ts (
    id int, 
    purchased date
) 
partition by range(year(purchased))
subpartition by hash(to_days(purchased)) subpartitions 2 
(
    partition p0 values less than (1990),
    partition p0 values less than (2000),
    partition p0 values less than maxvalue
);
           
CREATE TABLE IF NOT EXISTS `sub_part` (
  `news_id` int(11) NOT NULL  COMMENT '新聞ID',
  `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新聞内容',
  `u_id`  int(11) NOT NULL DEFAULT 0s COMMENT '來源IP',
  `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '時間'
) ENGINE=INNODB  DEFAULT CHARSET=utf8
PARTITION BY RANGE(YEAR(create_time))
SUBPARTITION BY HASH(TO_DAYS(create_time))
(
PARTITION p0 VALUES LESS THAN (1990) (SUBPARTITION s0, SUBPARTITION s1, SUBPARTITION s2),
PARTITION p1 VALUES LESS THAN (2000) (SUBPARTITION s3, SUBPARTITION s4, SUBPARTITION good),
PARTITION p2 VALUES LESS THAN MAXVALUE (SUBPARTITION tank0, SUBPARTITION tank1, SUBPARTITION tank3)
);
           

九:管理分區

mysql不禁止在分區鍵值上使用null,分區鍵可能是一個字段或者一個使用者定義的表達式,一般情況下,mysql的分區把null值當做零值或者一個最小值進行處理。range分區中,null值會被當做最小值來處理;list分區中null值必須出現在枚舉清單中,否則不被接受;hash/key分區中,null值會被當做領值來處理。

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 partitions 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,根據查找條件,也就是where後面的條件,查找隻查找相應的分區不用全部查找了

3,進行大資料搜尋時可以進行并行處理。

4,跨多個磁盤來分散資料查詢,來獲得更大的查詢吞吐量

繼續閱讀