Mysql通過Merge引擎進行分表


Mysql通過Merge引擎進行分表

使用場景

  • 數據表記錄很大,單表記錄會超過1000W,比如用戶表等。

測試環境

  • Mysql5.7

注意

分表的id不能是自增(auto increment)的; 分表必須使用MyISAM存儲引擎; 每個分表的表結構必須相同; MySQL必須具有存儲分表數據文件和索引文件的目錄的讀寫權限; 必須啟用MySQL的符號鏈接支持功能。 總表的表結構必須與各個分表相同; 總表必須使用MRG_MyISAM存儲引擎; 總表不會創建任何數據文件和索引文件; MRG文件存儲總表需要映射的子表的表名; 總表本身不存儲任何數據和索引; INSERT_METHOD需要設置為NO,或者不配置; 總表的id不能是自增(auto increment)的。 

MERGE分表的優點

  • MERGE分表可以解決下面的問題:
適用於存儲日志數據。例如,可以將不同月份的數據存入不同的表,然后使用myisampack工具壓縮數據,最后通過一張MERGE表來查詢這些數據。

可以獲得更快的速度。可以根據某種指標,將一張只讀的大表分割成若干張小表,然后將這些小表分別放在不同的磁盤上存儲。當需要讀取數據時,MERGE表可以將這些小表的數據組織起來,就好像使用先前的大表一樣,但是速度會快很多。

可以提高搜索效率。可以根據某種指標將一張只讀的大數據表分割為若干個小表,然后根據不同的查詢維度,可以得到若干種小表的組合,然后再為這些組合分別創建不同的MERGE表。例如,有一張只讀的大數據表T,分割為T1、T2、T3、T4,共4張小表,有兩種查詢維度A和B,A可以得到小表組合T1、T2和T3,B可以得到小表組合T2、T3和T4,分別為A和B創建兩個MERGE表,也就是M1和M2,這兩個MERGE表分別關聯的小表是存在交疊的。

可以更加有效的修復表。修復單個的小表要比修復大數據表更加容易。

多個子表映射至一個總表的速度極快。因為MERGE表本身不會存儲和維護任何索引,索引都是由各個關聯的子表存儲和維護的,所以創建和重新映射MERGE表的速度非常快。

不受操作系統的文件大小限制。單個表會受到文件大小的限制,但是拆分成多個表,則可以無限擴容。

MERGE表還可以用來給單個表創建別名,並且幾乎不會影響性能。

MERGE分表的問題

總表(MERGE表)必須使用MRG_MyISAM存儲引擎,子表必須使用MyISAM存儲引擎,不可避免會受到MyISAM存儲引擎的限制。

MERGE表不能使用某些MyISAM特性。例如,雖然可以為子表創建全文索引,但是卻不能使用全文索引,通過MERGE表查詢數據。

MERGE表會使用更多的文件描述符。如果有10個客戶端使用1張MERGE表,那么就需要消耗(10×10)+10個文件描述符(其中,10個客戶端分別有10個數據文件描述符,並且會共享使用10個索引文件描述符)。 若使用ALTER TABLE語句修改總表的存儲引擎,那么會立即丟失總表和子表的映射關系,並且會將所有子表的數據拷貝至修改后的新表。 總表和子表的主鍵都不能使用自動增長(auto increment)。 子表之間不能保證唯一鍵約束,只能保證單個子表內部的唯一性約束。 由於不能保證唯一鍵約束,導致REPLACE語句的行為會不可預期,INSERT ... ON DUPLICATE KEY UPDATE語句也有類似問題。因此,只能使用路由策略,對子表使用這些語句,而不能對總表使用。 子表不支持分區(Partition)。 當正在使用總表時,不能對任何子表執行ANALYZE TABLE、REPAIR TABLE、OPTIMIZE TABLE、ALTER TABLE、DROP TABLE、DELETE或TRUNCATE TABLE語句,否則會導致不可預期的結果。 總表和子表的表結構必須完全一致。 總表可以映射的所有子表的總行數上限為 264 行。 不支持INSERT DELAYED語句。 

建立數據庫

    CREATE database `test` DEFAULT CHARACTER SET utf8 ;

建立子表

  • 分表必須使用MyISAM存儲引擎,而MyISAM表的數據文件(.MYD文件)和索引文件(.MYI文件)是可以分散在不同的磁盤或目錄上存儲的。
  • 創建數據表的時候可以制定存儲目錄,如:指定 DATA DIRECTORY = '/home/user3' INDEX DIRECTORY = '/home/user3';
  • 在Shell中執行以下命令,創建存儲子表數據和索引的目錄:
# 創建存儲user1表數據和索引的目錄
mkdir -p /home/user1
chown -R mysql:mysql /home/user1
# 創建存儲user2表數據和索引的目錄
mkdir -p /home/user2
chown -R mysql:mysql /home/user2
# 創建存儲user3表數據和索引的目錄
mkdir -p /home/user3
chown -R mysql:mysql /home/user3
  • 啟用MySQL的have_symlink選項,使得MySQL支持符號鏈接,否則就不能指定MyISAM表的數據文件和索引文件的存儲路徑。在Shell中執行以下命令,編輯my.cnf文件:
vi /usr/local/MySQL/etc/my.cnf
  • 在my.cnf文件的[mysqld]分段中添加:
symbolic-links
  • 保存my.cnf文件之后,重新啟動mysql服務:
service mysqld restart
  • 創建user1、user2、user3分表,執行以下SQL:
CREATE TABLE `user1` (
 `id` INT NOT NULL,
 `user_name` VARCHAR(45) NOT NULL,
 `password` VARCHAR(45) NOT NULL,
 `create_time` TIMESTAMP NULL,
 `update_time` TIMESTAMP NULL,
  PRIMARY KEY (`id`),
  KEY `user_name` (`user_name`),
  KEY `create_time` (`create_time`)
)ENGINE = MyISAM;
CREATE TABLE `user2` like user1;
CREATE TABLE `user3` like user1;
  • 建立user總表
CREATE TABLE `users` (
 `id` INT NOT NULL,
 `user_name` VARCHAR(45) NOT NULL,
 `password` VARCHAR(45) NOT NULL,
 `create_time` TIMESTAMP NULL,
 `update_time` TIMESTAMP NULL,
  PRIMARY KEY (`id`),
  KEY `user_name` (`user_name`),
  KEY `create_time` (`create_time`)
)ENGINE = MERGE UNION = (`user1`,`user2`,`user3`);
  • or
CREATE TABLE users like user1;
ALTER TABLE users ENGINE=MERGE UNION(`user1`,`user2`,`user3`) insert_method=no;

可通過查看users表文件查看是否關聯成功,如:

cat /var/lib/mysql/test/users.MRG

如何操作MERGE表的數據

  • 插入(INSERT)數據時,需要根據給定的路由策略將新數據分別插入不同的子表,此處采用對id進行模3計算(可能結果為0、1、2)來決定插入哪個子表。
  • 首先,應當獲取id,這個id應當在各個子表中都是唯一的,我們需要一張表來專門創建id,執行如下SQL語句:
CREATE TABLE `test`.`user_ids` (
 `id` INT NOT NULL,
 PRIMARY KEY (`id`))
ENGINE = MyISAM;
  • 當需要插入數據時,必須由這個表來產生id值,PHP示例代碼如下所示:
/** * 獲取唯一的id */ function getUserID() { $sql = "insert into user_ids (id) values('')"; $this->db->query($sql); return $this->db->insertID(); } 
  • 插入一條新數據的PHP代碼如下所示:
/** * 插入一條新數據 */ function testInsert() { $id = $this->getUserID(); $table_name = $this->get_Table_Name($id); $sql = "INSERT INTO {$table_name} (id, user_name, password, create_time, update_time) VALUES ('{$id}', 'ghoulich', 'password', '2016-01-01 00:00:00', '2016-01-01 00:00:00')"; $this->db->query($sql); } /** * 根據id獲取表名 */ function get_Table_Name($id) { return 'user'.intval($id)%3+1; } 
  • 其他的刪除(DELETE)、查詢(SELECT)、修改(UPDATE)、清空(TRUNCATE)等操作都可以通過總表users完成。
  • 基於MERGE存儲引擎實現的分表機制,比較適用於插入和查詢頻率較高的場景。由於MyISAM具有表級別的鎖機制,所以不適用於更新頻率較高的場景。
 

一. 什么是MERGE引擎
MERGE存儲引擎把一組MyISAM數據表當做一個邏輯單元來對待,讓我們可以同時對他們進行查詢。

二. 應用場景
如果需要把日志紀錄不停的錄入MySQL數據庫,並且每天、每周或者每個月都創建一個單一的表,而且要時常進行來自多個表的合計查詢,MERGE表這時會非常簡單有效。

 

三. 舉例
假設有如下兩表

1 CREATE TABLE `t1` (   
2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   
3 `log` varchar(45) ,   
4 PRIMARY KEY (`id`) 
5 ) ENGINE=MyISAM;
 
1 CREATE TABLE `t2`(   
2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   
3 `log` varchar(45) ,   
4 PRIMARY KEY (`id`) 
5 ) ENGINE=MyISAM;

假設t1,t2中都有如下記錄

+----+-------+

| id | log   |

+----+-------+

|  1 | test1 |

|  2 | test2 |

|  3 | test3 |

+----+-------+

建立MERGE表

1 CREATE TABLE `t` (   
2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   
3 `log` varchar(45) NOT NULL,   
4 PRIMARY KEY (`id`) 
5 ) ENGINE=MERGE UNION=(t1, t2) INSERT_METHOD=LAST;

執行select * from t;將會得到如下結果
+----+-------+
| id | log   |
+----+-------+
|  1 | test1 |
|  2 | test2 |
|  3 | test3 |
|  1 | test1 |
|  2 | test2 |
|  3 | test3|
+----+-------+

從效果上看,t1,t2的記錄如同在一張表里一樣被羅列了出來。當然,看了這個結果你一定會有一些疑問,在下一節里我們會慢慢解釋。現在我們主要來解釋一下上面MERGE表的建表語句。
1)ENGINE=MERGE
指明使用MERGE引擎,有些同學可能見到過ENGINE=MRG_MyISAM的例子,也是對的,它們是一回事。
2)UNION=(t1, t2)
指明了MERGE表中掛接了些哪表,可以通過alter table的方式修改UNION的值,以實現增刪MERGE表子表的功能。
3)INSERT_METHOD=LAST
INSERT_METHOD指明插入方式,取值可以是:0 不允許插入;FIRST 插入到UNION中的第一個表; LAST 插入到UNION中的最后一個表。
4)MERGE表及構成MERGE數據表結構的各成員數據表必須具有完全一樣的結構。每一個成員數據表的數據列必須按照同樣的順序定義同樣的名字和類型,索引也必須按照同樣的順序和同樣的方式定義。

四. Cookie問答
1)建表時UNION指明的子表如果存在相同主鍵的記錄會怎么樣?
相同主鍵的記錄會同時存在於MERGE中,就像第三節中的例子所示。但如果繼續向MERGE表中插入數據,若數據主鍵已存在則無法插入。換言之,MERGE表只對建表之后的操作負責。

2)若MREGE后存在重復主鍵,按主鍵查詢會是什么結果?
順序查詢,只出現一條查詢記錄即停止。比如第三節中的例子,如果執行

1 select * from t where id=1;

只會得到結果
+----+--------+
| id | log    |
+----+--------+
|  1 | test1  |
+----+--------+

 

3)直接刪除一個子表會出現什么情況,正確刪除的方式是怎樣的?
MERGE表會被破壞,正確方式是用alter table方式先將子表從MERGE表中去除,再刪除子表。
以第三節中的例子為例,執行如下操作

1 alter table t ENGINE=MRG_MyISAM UNION=(t1) INSERT_METHOD=LAST;

可以從MERGE表中去除t2,這里你可以安全的對t2進行任何操作了。

 

4)誤刪子表時,如何恢復MERGE表?
誤刪子表時,MERGE表上將無法進行任何操作。
方法1,drop MERGE表,重建。重建時注意在UNION部分去掉誤刪的子表。
方法2,建立MERGE表時,會在數據庫目錄下生成一個.MRG文件,比如設表名為t,則文件名為t.MRG。
文件內容類似:
t1
t2
#INSERT_METHOD=LAST
指明了MGEGE表的子表構成及插入方式。
可以直接修改此文件,去掉誤刪表的表名。然后執行flush tables即可修復MERGE表。

 

5)MERGE的子表中之前有記錄,且有自增主鍵,則MERGE表創建后,向其插入記錄時主鍵以什么規則自增?
以各表中的AUTO_INCREMENT最大值做為下一次插入記錄的主鍵值。
比如t1的自增ID至6,t2至4,則創建MERGE表后,插入的下一條記錄ID將會是7

 

6)兩個結構完全相同的但已存在數據的表,是否一定可以合成一個MEREGE表?
從實驗的結果看,不是這樣的,有時創建出的表,無法進行任何操作。
所以,推薦的使用方法是先有一個MERGE表,里面只包含一張表,當一個這個表的的大小增長到一定程度(比如200w)時,創建另一張空表,將其掛入MERGE表,然后繼續插入記錄。

 

7)刪除MERGE表是否會對子表產生影響?
不會

 

8)MREGE表的子表的ENGIN是否有要求?
有的,必須是MyISAM表

 

附:
官方給出的關於MERGE表存在的一些問題
http://dev.mysql.com/doc/refman/5.1/zh/storage-engines.html#merge-table-problems
如果需要把日志紀錄不停的錄入MySQL數據庫,並且每天、每周或者每個月都創建一個單一的表,而且要時常進行來自多個表的合計查詢,MERGE表這時會非常簡單有效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM