MySQL分庫分表

一、分表

在日常開發或維護中經常會遇到大表的情況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,導致數據庫在查詢和插入的時候耗時太長,性能低下,如果涉及聯合查詢的情況,性能會更加糟糕。分表和表分區的目的就是減少數據庫的負擔,提高數據庫的效率,通常點來講就是提高表的增刪改查效率。

1. 什麼是分表

分表是將一個大表按照一定的規則分解成多張的實體表,我們可以稱爲子表。對於myisam引擎,每個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。根據分表技術對海量數據的優化方式目前有2種方法。

1.1 垂直分割

把一個數據量很大的表,可以把不同的列劃分到不同的子表中,子表的結構各不相同。垂直分割的理由一般是根據數據的活躍度進行分離,不同活躍的數據,處理方式是不同的。

垂直分割

【案例】

  • 對於一個博客系統,文章標題,作者,分類,創建時間等,是變化頻率慢,查詢次數多,我們把它叫做冷數據
  • 而博客的瀏覽量,回覆數等,類似的統計信息,或者別的變化頻率比較高的數據,我們把它叫做活躍數據/熱數據

所以,在進行數據庫結構設計的時候,就應該考慮分表,首先是縱向分表的處理。

(1)數據庫引擎的選擇

這樣縱向分表後:首先存儲引擎的使用不同,冷數據使用MyIsam 可以有更好的查詢效果;活躍數據,可以使用Innodb ,可以有更好的更新速度。

(2)讀寫優化

其次,對冷數據進行更多的從庫配置,因爲更多的操作是查詢,這樣來加快查詢速度。對熱數據,可以相對有更多的主庫的橫向分表處理。

(3)熱數據加緩存

其實,對於一些特殊的活躍數據,也可以考慮使用memcache、redis之類的緩存,等累計到一定量再去更新數據庫。或者mongodb 一類的nosql 數據庫,這裏只是舉例,就先不說這個。


1.2 水平分割

根據一列或者多列的值把數據行放到多個獨立的子表裏,字表的結構相同。水平分表方式可以通過多個低配置主機整合起來,實現高性能。分表理由:保證單表的容量不會太大,從而來保證單表的查詢等處理能力。

水平分割

下面要介紹的“分表”主要指水平分表。


2. 利用merge存儲引擎實現分表

2.1 步驟

假如我有一張用戶表user,有50W條數據,現在要拆成二張表user1和user2,每張表25W條數據。

(1)新建子表

mysql> CREATE TABLE IF NOT EXISTS `user1` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   PRIMARY KEY (`id`)  
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected (0.05 sec)  

mysql> CREATE TABLE IF NOT EXISTS `user2` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   PRIMARY KEY (`id`)  
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected (0.01 sec)  

(2)往子表裏插入數據

INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000

INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000

(3)建立merge表

mysql> CREATE TABLE IF NOT EXISTS `alluser` (  
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,  
 ->   `name` varchar(50) DEFAULT NULL,  
 ->   `sex` int(1) NOT NULL DEFAULT '0',  
 ->   INDEX(id)  
 -> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ;  
Query OK, 0 rows affected, 1 warning (0.00 sec)  

(4)備份user表,然後刪除它

(5)把這個alluser表的表名改成user


2.2 注意事項

(1)一個 merge 表不能在整個表上維持 unique 約束。當你執行一個 insert,數據進入第一個或者最後一個 myisam 表(取決於 insert_method 選項的值)。mysql 確保唯一鍵值在那個 myisam 表裏保持唯一,但不是跨集合裏所有的表。

(2)當你創建一個 merge 表之時,沒有檢查去確保底層表的存在以及有相同的機構。當 merge 表被使用之時,mysql 檢查每個被映射的表的記錄長度是否相等,但這並不十分可靠。如果你從不相似的 myisam 表創建一個 merge 表,你非常有可能撞見奇怪的問題。


3. 常用的分表策略

上面介紹的分表其實是對錶結構進行了更改,對大表進行拆分。實際上我們在設計數據庫表結構和業務邏輯的時候,就應該計算業務需求,提前做好分表,避免大表的出現。下面是基於業務層面的一些常用分表策略。下面以新聞發佈系統舉例。下面分離出的子表可以位於一個數據實例上,如果位於不同的實例上那麼就存在分庫了,也就多了表與庫的映射這一步。

(1)按時間結構

類似:

article_201701
article_201702
article_201703

在這個系統中,主鍵是13位帶毫秒的時間戳

(2)歸檔式

類似:

article_old
article_new

一張是舊文章表,一張是新文章表,新文章表放2個月的信息,每天定期把2個月中的最早一天的文章歸入舊錶中。

(3)按版塊結構

news_category
news_article
sports_category
sports_article

(4)按哈希結構

md5取前兩位哈希可以達到1296張表,如果覺得不夠,那就再加一位,總數可達46656張表。


二、分區

1. 什麼是分區?

分表是邏輯層面的分割,分區是物理層面、存儲層面的分割。準確來說,分區是將數據分段劃分在多個位置存放,可以是同一塊磁盤也可以在不同的機器。分區後,表面上還是一張表。

mysql分表和分區有什麼聯繫呢?
(1)都能提高mysql的性高,在高併發狀態下都有一個良好的表現。
(2)分表和分區不矛盾,可以相互配合的,對於那些大訪問量,並且表數據比較多的表,我們可以採取分表和分區結合的方式。

(3)merge這種分表方式,不能和分區配合。
(4)表分區相對於分表,操作方便,不需要創建子表。


2. 分區的類型

2.1 Range

把連續區間按範圍劃分:

create table user(
    id int(11),
    money int(11) unsigned not null,
    date datetime
)
partition by range(YEAR(date))(
    partition p2014 values less than (2015),
    partition p2015 values less than (2016),
    partition p2016 values less than (2017),
    partition p2017 values less than maxvalue
);

2.2 List

把離散值分成集合,按集合劃分,適合有固定取值列的表

create table user(
    a int(11),
    b int(11)
)
partition by list(b)(
    partition p0 values in (1,3,5,7,9),
    partition p1 values in (2,4,6,8,0)
);

2.3 Hash

隨機分配,分區數固定:

create table user(
    a int(11),
    b datetime
)
partition by hash(YEAR(b))
partitions 4;

2.4 Key

類似Hash,區別是隻支持1列或多列,且mysql提供自身的Hash函數

create table user(
    a int(11),
    b datetime
)
partition by key(b)
partitions 4;

3. 分區管理

(1)新增分區

ALTER TABLE sale_data
ADD PARTITION (PARTITION p201710 VALUES LESS THAN (201711));

(2)刪除分區

當刪除了一個分區,也同時刪除了該分區中所有的數據。

ALTER TABLE sale_data DROP PARTITION p201710;

(3)分區的合併
下面的SQL,將p201701 - p201709 合併爲3個分區p2017Q1 - p2017Q3:

ALTER TABLE sale_data
REORGANIZE PARTITION p201701,p201702,p201703,
p201704,p201705,p201706,
p201707,p201708,p201709 INTO
(
PARTITION p2017Q1 VALUES LESS THAN (201704),
PARTITION p2017Q2 VALUES LESS THAN (201707),
PARTITION p2017Q3 VALUES LESS THAN (201710)
);
發佈了94 篇原創文章 · 獲贊 43 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章