mysql —— 分表分區

面對當今大數據存儲,設想當mysql中一個表的總記錄超過1000W,會出現性能的大幅度降低嗎?前端

答案是確定的,一個表的總記錄超過1000W,在操做系統層面檢索也是效率很是低的mysql

 

解決方案:算法

目前針對海量數據的優化有兩種方法:sql

一、大表拆小表的方式(主要有分表和分區二者技術)數據庫

(1)分表技術服務器

垂直分割併發

094806715.png

優點:下降高併發狀況下,對於表的鎖定。less

不足:對於單表來講,隨着數據庫的記錄增多,讀寫壓力將進一步增大。函數

 

 

水平分割高併發

094821338.png

 

若是單表的IO壓力大,能夠考慮用水平分割,其原理就是經過hash算法,將一張表分爲N多頁,並經過一個新的表(總表),記錄着每一個頁的的位置。假如一 個門戶網站,它的數據庫表已經達到了1000萬條記錄,那麼此時若是經過select去查詢,一定會效率低下(不作索引的前提下)。爲了下降單表的讀寫 IO壓力,經過水平分割,將這個表分紅10個頁,同時生成一個總表,記錄各個頁的信息,那麼假如我查詢一條id=100的記錄,它再也不須要全表掃描,而是 經過總表找到該記錄在哪一個對應的頁上,而後再去相應的頁作檢索,這樣就下降了IO壓力。

 

水平分表技術就是將一個表拆成多個表,比較常見的方式就是將表中的記錄按照某種HASH算法進行拆分,同時,這種分區方法也必須對前端的應用程序中的 SQL進行修改方能使用,並且對於一個SQL語句,可能會修改兩個表,那麼你必需要修改兩個SQL語句來完成你這個邏輯的事務,會使得邏輯判斷愈來愈復 雜,這樣會增長程序的維護代價,因此咱們要避免這樣的狀況出現。

 

 

 

二、SQL語句的優化(索引)

SQL語句優化:能夠經過增長索引等來調整,但同時數據量的增大會致使索引的維護代價增大。

 

 

分區優勢:

一、減小IO

二、提升讀寫

三、方便數據管理

 

分區與分表的區別:

分區是邏輯層面進行了水平分割,對於應用程序來講,它還是一張表。

分區就是把一張表的數據分紅N多個區塊,這些區塊能夠在同一個磁盤上,也能夠在不一樣的磁盤上

 

1. 實現方式上

(1)mysql的分表是真正的分表,一張表分紅不少表後,每個小表都是完整的一張表,都對應三個文件,一個.MYD數據文件,.MYI索引文件,.frm表結構文件。

[root@BlackGhost test]# ls |grep user

alluser.MRG

alluser.frm

user1.MYD

user1.MYI

user1.frm

user2.MYD

user2.MYI

user2.frm

 

簡單說明一下,上面的分表是利用了merge存儲引擎(分表的一種),alluser是總表,下面有二個分表,user1,user2。他們二個都是獨立 的表,取數據的時候,咱們能夠經過總表來取。這裏總表是沒有.MYD,.MYI這二個文件的,也就是說,總表他不是一張表,沒有數據,數據都放在分表裏 面。咱們來看看.MRG究竟是什麼東西

 

[root@BlackGhost test]# cat alluser.MRG |more

user1

user2

 

#INSERT_METHOD=LAST

從上面咱們能夠看出,alluser.MRG裏面就存了一些分表的關係,以及插入數據的方式。能夠把總表理解成一個外殼,或者是鏈接池。

 

 

(2)分區不同,一張大表進行分區後,他仍是一張表,不會變成二張表,可是他存放數據的區塊變多了。

[root@BlackGhost test]# ls |grep aa

aa#P#p1.MYD

aa#P#p1.MYI

aa#P#p2.MYD

aa#P#p2.MYI

aa#P#p3.MYD

aa#P#p3.MYI

aa.frm

aa.par

 

從上面咱們能夠看出,aa這張表,分爲3個區。咱們都知道一張表對應三個文件.MYD,.MYI,.frm。分區根據必定的規則把數據文件和索引文件進行 了分割,還多出了一個.par文件,打開.par文件後你能夠看出他記錄了,這張表的分區信息,跟分表中的.MRG有點像。分區後,仍是一張,而不是多張 表。

 

2. 數據處理上

(1)分表後,數據都是存放在分表裏,總表只是一個外殼,存取數據發生在一個一個的分表裏面。看下面的例子:

 

select * from user1 user2 where id='12'表面上看,是對錶alluser進行操做的,其實不是的。是對alluser裏面的分表進行了操做。

 

(2)分區,不存在分表的概念,分區只不過把存放數據的文件分紅了許多小塊,分區後的表,仍是一張表。數據處理仍是由本身來完成。

select * from alluser where id='12'

 

3. 提升性能上

(1) 分表後,單表的併發能力提升了,磁盤I/O性能也提升了。由於查詢一次所花的時間變短了,若是出現高併發的話,總表能夠根據不一樣的查詢,將併發壓力分到不 同的小表裏面。原本一個很是大的.MYD文件如今也分攤到各個小表的.MYD中去了,所以對於磁盤IO壓力也下降了。

 

(2)mysql提出了分區的概念,我以爲就想突破磁盤I/O瓶頸,想提升磁盤的讀寫能力,來增長mysql性能。

在這一點上,分區和分表的側重點不一樣,分表重點是存取數據時,如何提升mysql併發能力上;而分區呢,則是如何突破磁盤的讀寫能力,從而達到提升mysql性能的目的。

 

4. 實現的難易度上

(1)分表的方法有不少,用merge來分表,是最簡單的一種方式。這種方式根分區難易度差很少,而且對程序代碼來講能夠作到透明的。若是是用其餘分表方式就比分區麻煩了。

 

(2)分區實現是比較簡單的,創建分區表,跟建日常的表沒什麼區別,而且對開代碼端來講是透明的。

 

 

 

分區類型

hash、range、list、key

  • RANGE分區:基於一個給定連續區間的列值,把多行分配給分區。

  • LIST分區:相似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。

  • HASH分區:基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數能夠包含MySQL 中有效的、產生非負整數值的任何表達式。

hash用在數據相對比較隨機的狀況下。它是根據表中的內容進行hash運算後隨機平均分配,假設這個列是性別,則不適合用hash分區,由於內容要麼是男,要麼是女,沒有隨機性。

  • KEY分區:相似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL 服務器提供其自身的哈希函數。必須有一列或多列包含整數值。 ----不多用到

 

如何查看數據庫是否支持分區技術?

094848116.png

 

建立分區:

mysql> create table t1(id int)partition by hash(id)partitions 3;

Query OK, 0 rows affected (0.03 sec)

 

 

【實驗】

分別建立一個分區的表和非分區的表,進行性能測試

 

建立分區表

mysql> create table part_tab ( c1 int default NULL, c2 varchar(30) default null, c3 date default null) engine=myisam
-> partition by range(year(c3))(
-> partition p0 values less than (1995),
-> partition p1 values less than (1996),
-> partition p2 values less than (1997),
-> partition p3 values less than (1998),
-> partition p4 values less than (1999),
-> partition p5 values less than (2000),
-> partition p6 values less than (2001),
-> partition p7 values less than (2002),
-> partition p8 values less than (2003),
-> partition p9 values less than (2004),
-> partition p10 values less than (2010),
-> partition p11 values less than MAXVALUE);
Query OK, 0 rows affected (0.14 sec)

建立非分區表
mysql> create table no_part_tab ( c1 int default NULL, c2 varchar(30) default null, c3 date default null) engine=myisam;
Query OK, 0 rows affected (0.11 sec)

mysql> \d // #因爲下面要用到存儲過程,這裏須要修改結束符爲「//」。所謂的存儲過程其實也就是衆多sql語句的集合。
mysql> create procedure load_part_tab()
-> begin
-> declare v int default 0;
-> while v < 8000000
-> do
-> insert into part_tab
-> values (v,'testing partitions',adddate('1995-01-01',(rand(v)*36520)mod 3652));
-> set v = v+1;
-> end while;
-> end
-> // 
Query OK, 0 rows affected (0.04 sec)

mysql> \d ; // 執行完這個存儲過程後,須要將結束符修改回去

上面的存儲過程其實是爲了建立大量的數據(800萬條)

mysql> call load_part_tab(); // 調用load_part_tab這個存儲過程
Query OK, 1 row affected (9 min 18.95 sec)
 

快速將part_tab裏面的數據插入到no_part_tab裏面
mysql> insert no_part_tab select * from part_tab;
Query OK, 8000000 rows affected (8.97 sec)
Records: 8000000 Duplicates: 0 Warnings: 0
 

 

測試一:

實驗以前確保兩個表裏面的數據是一致的!保證明驗的可比性

mysql> select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (0.49 sec)
 

mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (3.94 sec)
 

mysql> desc select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: part_tab
type: ALL //全表掃描
possible_keys: NULL 
key: NULL
key_len: NULL
ref: NULL
rows: 798458
Extra: Using where
1 row in set (0.09 sec)

ERROR: 
No query specified

mysql> desc select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: no_part_tab
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8000000
Extra: Using where
1 row in set (0.00 sec)

ERROR: 
No query specified
 

結論:能夠看到,作了分區以後,只須要掃描79萬條語句,而不作分區的,則須要進行全表掃描,故能夠看出,作了分區技術後,能夠提升讀寫效率。

 

測試2:
建立索引,查看語句執行狀況

mysql> create index idx_c3 on no_part_tab(c3);
Query OK, 8000000 rows affected (32.68 sec)
Records: 8000000 Duplicates: 0 Warnings: 0

結果分析:

mysql> desc select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: NO_part_tab
type: range
possible_keys: idx_c3
key: idx_c3
key_len: 4
ref: NULL
rows: 785678
Extra: Using where; Using index
1 row in set (0.16 sec)

ERROR: 
No query specified

結論:爲未分區的表建立了索引以後,再次執行相同的語句,能夠看到該SQL語句是根據range索引進行檢索,而不是全表掃描了。明顯效率也提升了。

 

測試3:

測試作索引與未做索引的讀寫效率。

mysql> create index idx_c3 on part_tab(c3);
Query OK, 8000000 rows affected (31.85 sec)
Records: 8000000 Duplicates: 0 Warnings: 0

mysql> desc select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: part_tab
type: index
possible_keys: idx_c3
key: idx_c3
key_len: 4
ref: NULL
rows: 798458
Extra: Using where; Using index
1 row in set (0.14 sec)

ERROR: 
No query specified
 


測試未建立索引字段

mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31' and c2='hello';

+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (4.90 sec)

結論:能夠看到若是沒經過索引進行檢索所耗費的時間將長於經過索引進行檢索。




測試4:刪除
mysql> delete from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';
Query OK, 795181 rows affected (14.02 sec)

mysql> delete from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';
Query OK, 795181 rows affected (15.21 sec)

結論:能夠看到,在刪除方面,有分區的仍是比沒分區的快一點。從而體現了其便於數據管理的特色 方 便數據管理這點,我經過下面的例子來講明:好比數據庫的表t1記錄的是今年一全年(12個月)公司的營業額,在未分區的狀況下,也就是說數據文件都存放在 同一個文件裏面,那麼假如如今要刪除第一個季度的記錄,那麼須要全表掃描才能得出結果。但若是t1這個表事先作了分區,那麼我只須要分別刪除1,2,3這 三個文件便可。因此從必定程度上,仍是方便了管理。

相關文章
相關標籤/搜索