分區
分區就是把一個數據表的文件和索引分散存儲在不同的物理文件中。
mysql支持的分區類型包括Range、List、Hash、Key,其中Range比較常用:
RANGE分區:基於屬於一個給定連續區間的列值,把多行分配給分區。
LIST分區:類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
HASH分區:基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數可以包含MySQL 中有效的、產生非負整數值的任何表達式。
KEY分區:類似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含整數值。
案例:
建立一個user 表 以id進行分區 id 小於10的在user_1分區id小於20的在user_2分區
create table user( id int not null auto_increment, username varchar(10), primary key(id) )engine = innodb charset=utf8 partition by range (id)( partition user_1 values less than (10), partition user_2 values less than (20) );
建立后添加分區:
maxvalue 表示最大值 這樣大於等於20的id 都出存儲在user_3分區
alter table user add partition( partition user_3 values less than maxvalue );
刪除分區:
alter table user drop partition user_3;
現在打開mysql的數據目錄
可以看見多了user#P#user_1.ibd 和user#P#user_2.ibd 這兩個文件
如果表使用的存儲引擎是MyISAM類型,就是:
user#P#user_1.MYD,user#P#user_1.MYI和user#P#user_2.MYD,user#P#user_2.MYI
由此可見,mysql通過分區把數據保存到不同的文件里,同時索引也是分區的。相對於未分區的表來說,分區后單獨的數據庫文件索引文件的大小都明顯降低,效率則明顯的提示了。可以插入一條數據然后分析查詢語句驗證一下:
insert into user values(null,'測試'); explain partitions select * from user where id =1;
可以看見僅僅在user_1分區執行了這條查詢。
具體分區的效率是多少還需要看數據量。在分區時可以通過 DATA DIRECTORY 和 INDEX DIRECTORY 選項吧不同的分區放到不同的磁盤上進一步提高系統的I/O吞吐量。
分區類型的選擇,通常使用Range類型,不過有些情況,比如主從結構中,主服務器很少使用‘select’查詢,在主服務器上使用 Range類型分區通常沒有太大的意義,此時使用Hash類型分區更好例如:
partition by hash(id) partitions 10;
當插入數據時,根據id吧數據平均散到各個分區上,由於文件小,效率高,更新操作變得更快。
在分區時使用的字段,通常情況下按時間字段分區,具體情況以需求而定。划分應用的方式有很多種,比如按時間或用戶,哪種用的多,就選擇哪種分區。如果使用主從結構可能就更加靈活,有的從服務器使用時間,有的使用用戶。不過如此一來當執行查詢時,程序應該負責選擇真確的服務器查詢,寫個mysql proxy腳本應該可以透明的實現。
分區的限制:
1.主鍵或者唯一索引必須包含分區字段,如primary key (id,username),不過innoDB的大組建性能不好。
2.很多時候,使用分區就不要在使用主鍵了,否則可能影響性能。
3.只能通過int類型的字段或者返回int類型的表達式來分區,通常使用year或者to_days等函數(mysql 5.6 對限制開始放開了)。
4.每個表最多1024個分區,而且多分區會大量消耗內存。
5.分區的表不支持外鍵,相關的邏輯約束需要使用程序來實現。
6.分區后,可能會造成索引失效,需要驗證分區可行性。
分區模式詳解:
* Range(范圍) – 這種模式允許DBA將數據划分不同范圍。例如DBA可以將一個表通過年份划分成三個分區,80年代(1980's)的數據,90年代(1990's)的數據以及任何在2000年(包括2000年)后的數據。
CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, usersname VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (3000000), PARTITION p1 VALUES LESS THAN (6000000), PARTITION p2 VALUES LESS THAN (9000000), PARTITION p3 VALUES LESS THAN MAXVALUE );
在這里,將用戶表分成4個分區,以每300萬條記錄為界限,每個分區都有自己獨立的數據、索引文件的存放目錄。
還可以將這些分區所在的物理磁盤分開完全獨立,可以提高磁盤IO吞吐量。
CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, usersname VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY RANGE (id) ( PARTITION p0 VALUES LESS THAN (3000000) DATA DIRECTORY = '/data0/data' INDEX DIRECTORY = '/data0/index', PARTITION p1 VALUES LESS THAN (6000000) DATA DIRECTORY = '/data1/data' INDEX DIRECTORY = '/data1/index', PARTITION p2 VALUES LESS THAN (9000000) DATA DIRECTORY = '/data2/data' INDEX DIRECTORY = '/data2/index', PARTITION p3 VALUES LESS THAN MAXVALUE DATA DIRECTORY = '/data3/data' INDEX DIRECTORY = '/data3/index' );
* List(預定義列表) – 這種模式允許系統通過DBA定義的列表的值所對應的行數據進行分割。例如:DBA根據用戶的類型進行分區。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL DEFAULT '' , user_type int not null ) PARTITION BY LIST (user_type ) ( PARTITION p0 VALUES IN (0,4,8,12) , PARTITION p1 VALUES IN (1,5,9,13) , PARTITION p2 VALUES IN (2,6,10,14), PARTITION p3 VALUES IN (3,7,11,15) );
分成4個區,同樣可以將分區設置的獨立的磁盤中。
* Key(鍵值) – 上面Hash模式的一種延伸,這里的Hash Key是MySQL系統產生的。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY KEY (id) PARTITIONS 4 ( PARTITION p0, PARTITION p1, PARTITION p2, PARTITION p3 );
* Hash(哈希) – 這中模式允許DBA通過對表的一個或多個列的Hash Key進行計算,最后通過這個Hash碼不同數值對應的數據區域進行分區,。例如DBA可以建立一個對表主鍵進行分區的表。
CREATE TABLE user ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL DEFAULT '', email VARCHAR(30) NOT NULL DEFAULT '' ) PARTITION BY HASH (id) PARTITIONS 4 ( PARTITION p0 , PARTITION p1, PARTITION p2, PARTITION p3 );
分成4個區,同樣可以將分區設置的獨立的磁盤中。
= 分區管理 =
刪除分區
ALERT TABLE users DROP PARTITION p0;
刪除某個分區下的所有數據(刪除users表下的p0分區中的所有數據)
alter table users truncate partition p0;
重建分區
RANGE 分區重建
ALTER TABLE users REORGANIZE PARTITION p0,p1 INTO (PARTITION p0 VALUES LESS THAN (6000000));
將原來的 p0,p1 分區合並起來,放到新的 p0 分區中。
LIST 分區重建
ALTER TABLE users REORGANIZE PARTITION p0,p1 INTO (PARTITION p0 VALUES IN(0,1,4,5,8,9,12,13));
將原來的 p0,p1 分區合並起來,放到新的 p0 分區中。
HASH/KEY 分區重建
ALTER TABLE users REORGANIZE PARTITION COALESCE PARTITION 2;
用 REORGANIZE 方式重建分區的數量變成2,在這里數量只能減少不能增加。想要增加可以用 ADD PARTITION 方法。
新增分區
新增 RANGE 分區
alter table user add partition(partition user_3 values less than maxvalue);
新增 LIST 分區
ALTER TABLE category ADD PARTITION (PARTITION p4 VALUES IN (16,17,18,19));
新增 HASH/KEY 分區
ALTER TABLE users ADD PARTITION PARTITIONS 8;
將分區總數擴展到8個。
給已有的表加上分區
alter table results partition by RANGE (month(ttime)) (PARTITION p0 VALUES LESS THAN (1), PARTITION p1 VALUES LESS THAN (2) , PARTITION p2 VALUES LESS THAN (3) , PARTITION p3 VALUES LESS THAN (4) , PARTITION p4 VALUES LESS THAN (5) , PARTITION p5 VALUES LESS THAN (6) , PARTITION p6 VALUES LESS THAN (7) , PARTITION p7 VALUES LESS THAN (8) , PARTITION p8 VALUES LESS THAN (9) , PARTITION p9 VALUES LESS THAN (10) , PARTITION p10 VALUES LESS THAN (11), PARTITION p11 VALUES LESS THAN (12), PARTITION P12 VALUES LESS THAN (13) );
//示例
1:創建分區
DROP TABLE IF EXISTS `test`;
CREATE TABLE test (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
time datetime NOT NULL COMMENT '業務時間',
PRIMARY KEY ( id , time )
) ENGINE=InnoDB AUTO_INCREMENT=12001 DEFAULT CHARSET=utf8
PARTITION BY RANGE (TO_DAYS(time))
(PARTITION part_201501 VALUES LESS THAN (TO_DAYS('2015-02-01'))ENGINE = InnoDB,
PARTITION part_201502 VALUES LESS THAN (TO_DAYS('2015-03-01'))ENGINE = InnoDB,
PARTITION part_201503 VALUES LESS THAN (TO_DAYS('2015-04-01'))ENGINE = InnoDB,
PARTITION part_201504 VALUES LESS THAN (TO_DAYS('2015-05-01'))ENGINE = InnoDB,
PARTITION part_201505 VALUES LESS THAN (TO_DAYS('2015-06-01'))ENGINE = InnoDB,
PARTITION part_201506 VALUES LESS THAN (TO_DAYS('2015-07-01'))ENGINE = InnoDB) ;
1:如果test的表存在即刪除
2:創建test表,綁定主鍵
3:PARTITION part_201501 VALUES LESS THAN (TO_DAYS('2015-02-01'))ENGINE = InnoDB
建立分區 PARTITION后面接分區名(part_201501 ) LESS THAN (TO_DAYS('2015-02-01') 代表值小於2015- 02-01屬於該分區。
4:插入數據
5:SELECT * FROM test PARTITION (part_201501);
查看屬於part_201501分區的數據有多少條
6:select partition_name part,partition_expression expr,partition_description descr,table_rows from information_schema.partitions where TABLE_SCHEMA =schema() and TABLE_NAME='test';
查看test表的所有分區。
2:建立索引
alter table test add index test_index (id) ;
1:test代表索引建立的表 test_index 代表索引名稱 id代表索引綁定的列
2:show index from test 查看test表的索引
---------------------
作者:Say___Hello
來源:CSDN
原文:https://blog.csdn.net/Say___Hello/article/details/83274039
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
分表
分表和分區類似,區別是,分區是把一個邏輯表文件分成幾個物理文件后進行存儲,而分表則是把原先的一個表分成幾個表。進行分表查詢時可以通過union或者視圖。
分表又分垂直分割和水平分割,其中水平分分割最為常用。水平分割通常是指切分到另外一個數據庫或表中。例如對於一個會員表,按對3的模進行分割:
table = id%3
如果id%3 = 0 則將用戶數據放入到user_0表中,如id%3=1就放入user_1表中,依次類推。
在這里有個問題,這個uid應該是所有會員按序增長的,可他是怎么得到的呢?使用auto_increment是不行的,這樣就用到序列了。
對於一些流量統計系統,其數據量比較大,並且對過往數據的關注度不高,這時按年、月、日進行分表,將每日統計信息放到一個以日期命名的表中;或者按照增量進行分表,如每個表100萬數據,超過100萬就放入第二個表。還可以按Hash進行分表,但是按日期和取模余數分表最為常見,也容易擴展。
分表后可能會遇到新的問題,那就是查詢,分頁和統計。通用的方法是在程序中進行處理,輔助視圖。
使用分表案例:
案例1:
對會員數據對5取模,放在5個表中,如何查詢會員數據:
1.已知id查詢會員數據,代碼如下:
<?php //查詢單個會員數據 $customer_table = 'customer'.$id%5; $sql = 'select * from '.$customer_table.' where customer_id = '.$id; //查詢全部會員數據 $sql = ''; $tbale = ['customer0','customer1','customer2','customer3','customer4']; foreach($table as $v){ $sql .='select * from '.$v.' union'; } $sql = substr($sql,0,-5); ?>
這樣就可以查詢某一個會員的數據或者全部會員的數據了。同理,分頁的話在這個大集合中使用limit 就可以了。但是這樣做又會有一個疑問,把所有的表連起來查詢和部分表沒有什么區別,其實在實際的應用中,不可能查看所有的會員資料,一次查看20個然后分頁。完全沒有必要做union,僅查詢一個表就可以了,唯一需要考慮的是在分頁零界點時的銜接。其實,這個銜接是否那么重要?即使偶爾出現幾條數據的差異,也不會對業務有任何的影響。
2.和其它表進行關聯和1類似。
3.根據會員姓名搜索用戶信息。在這種需求下,需要搜索所有的表,並對結果進行匯總。雖然這樣做產生了多次的查詢,但並不代表效率低。好的sql語句執行10次也比差的sql語句執行一次快。
案例2:
在一個流量監控系統中,由於網絡流量巨大,統計數據很龐大,需要按天分表。先要得到任意日,周,月的數據。
1.需要任意一天的數據。直接查詢當天的數據表即可。
2.需要幾天的數據。分愛查詢這幾天的數據,然后進行匯總。
3.需要查詢一周的數據。對一周的數據定期匯總到一個week表,從這個表里面查詢。這個匯總過程可以由一個外部程序完成,也可以由定期的腳本完成。
4.查詢一個月的數據。匯總本月所有的數據到month表,在此表查詢。
5.查詢5個月內的詳細數據。不支持。僅支持最多3個月的詳細數據。數據沒3個月已歸檔一次。在大數據的處理中,必須做出一些犧牲。對於超出3個月的數據,僅提供統計數據,詳細數據需要查看歸檔。90天或者180天,給數據保存設個界限,也是大部分這類系統的常規做法,超出90天的數據就不再提供數據詳單了。比如,移動的通話記錄最多保存半年,即180天,超過這個范圍的數據不在提供查詢。如果你實在需要,可能就要聯系移動的工程師了。
分表前應該盡量按照實際業務來分表,參考依據就是哪些字段在查詢中起到作用,那就這些字段來分表,並且需要在分表前就估算好規模,也就是先確定好規則在分表。
對於分表后的操作,依然是聯合查詢,視圖等基本操作,或者使用merge引擎合並數據並在此表中查詢。復雜一些操作需要借助存儲過程來完成,借助外部工具實現對分表的管理。
由於數據量較大,對現有的表進行分區
操作方式.
可以使用ALTER TABLE來進行更改表為分區表,這個操作會創建一個分區表,然后自動進行數據copy然后刪除原表,
猜測服務器資源消耗比較大。
ALTER TABLE tbl_rtdata PARTITION BY RANGE (Month(fld_date))
(
PARTITION p_Apr VALUES LESS THAN (TO_DAYS('2012-05-01')),
PARTITION p_May VALUES LESS THAN (TO_DAYS('2012-06-01')),
PARTITION p_Dec VALUES LESS THAN MAXVALUE );
**
新建一個和原來表一樣的分區表,然后把數據從原表導出,接着倒入新表。 **
(原來的表主鍵只有id,而我的分區字段是 stsdate, 這里主鍵要修改為 id,stsdate 聯合主鍵,分區表要求分區字段要是主鍵或者是主鍵的一部分)
操作過程
采用第二種方案。先創建分區表,然后導出原表數據,新表名稱改為原表名,然后插入,最后建立普通索引。
建立分區表
CREATE TABLE `apdailysts_p` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `ap_id` INT(11) NOT NULL, `mac` VARCHAR(17) NOT NULL, `liveinfo` LONGTEXT NOT NULL, `livetime` INT(11) NOT NULL, `stsdate` DATE NOT NULL, `lastmodified` DATETIME NOT NULL, PRIMARY KEY (`id`, `stsdate`) ) PARTITION BY RANGE COLUMNS(stsdate) ( PARTITION p0 VALUES LESS THAN ('2016-06-01'), PARTITION p1 VALUES LESS THAN ('2016-07-01'), PARTITION p2 VALUES LESS THAN ('2016-08-01'), PARTITION p3 VALUES LESS THAN ('2016-09-01'), PARTITION p4 VALUES LESS THAN ('2016-10-01'), PARTITION p5 VALUES LESS THAN ('2016-11-01'), PARTITION p6 VALUES LESS THAN ('2016-12-01'), PARTITION p7 VALUES LESS THAN ('2017-01-01'), PARTITION p8 VALUES LESS THAN ('2017-02-01'), PARTITION p9 VALUES LESS THAN ('2017-03-01'), PARTITION p10 VALUES LESS THAN ('2017-05-01'), PARTITION p11 VALUES LESS THAN ('2017-06-01'), PARTITION p12 VALUES LESS THAN ('2017-07-01'), PARTITION p13 VALUES LESS THAN ('2017-08-01'), PARTITION p14 VALUES LESS THAN ('2017-09-01'), PARTITION p15 VALUES LESS THAN MAXVALUE );