3、Mycat分庫分表

  一個數據庫由很多表的構成,每個表對應着不同的業務,垂直切分是指按照業務將表進行分類,分佈到不同的數據庫上面,這樣也就將數據或者說壓力分擔到不同的庫上面。

一、分表

1.如何劃分表

問題:在兩臺主機上的兩個數據庫中的表,能否關聯查詢?

答案:不可以關聯查詢

分庫的原則:有緊密關聯關係的表應該在一個庫裏,相互沒有關聯關係的表可以分到不同的庫裏。

2.schema.xml配置

[root@host79 conf]# pwd

/usr/local/mycat/conf

[root@host79 conf]# vim schema.xml

 

<?xml version="1.0"?>

<!DOCTYPE mycat:schema SYSTEM "schema.dtd">

<mycat:schema xmlns:mycat="http://io.mycat/">

 

        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">

                <table name="customer" dataNode="dn2" ></table>

        </schema>

        <dataNode name="dn1" dataHost="host1" database="orders" />

        <dataNode name="dn2" dataHost="host2" database="orders" />

 

        <dataHost name="host1" maxCon="1000" minCon="10" balance="0"

                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">

                <heartbeat>select user()</heartbeat>

                <!-- can have multi write hosts -->

                <writeHost host="hostM1" url="192.168.188.188:3306" user="root"

                                   password="123456">

                </writeHost>

 

        </dataHost>

        <dataHost name="host2" maxCon="1000" minCon="10" balance="0"

                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">

                <heartbeat>select user()</heartbeat>

                <!-- can have multi write hosts -->

                <writeHost host="hostM2" url="192.168.188.189:3306" user="root"

                                   password="123456">

                </writeHost>  

           

        </dataHost>

              

</mycat:schema>

3.新增數據庫

  分庫操作不是在原來的老數據庫上進行操作,需要準備兩臺機器分別安裝新的數據庫

[root@host79 ~]# mysql -uroot -p123456

mysql> CREATE DATABASE orders;

[root@host80 ~]# mysql -uroot -p123456

mysql> CREATE DATABASE orders;

4.創建表

#啓動 Mycat

[root@host79 bin]# ./mycat console

 

[root@host79 ~]# mysql -umycat -p123456 -h 192.168.188.188 -P 8066

mysql> use TESTDB;

 

#客戶表 rows:20萬

CREATE TABLE customer(

 id INT AUTO_INCREMENT,

 NAME VARCHAR(200),

 PRIMARY KEY(id)

);

 

 

#訂單表 rows:600萬

CREATE TABLE orders(

 id INT AUTO_INCREMENT,

 order_type INT,

 customer_id INT,

 amount DECIMAL(10,2),

 PRIMARY KEY(id)

);

#訂單詳細表 rows:600萬

CREATE TABLE orders_detail(

 id INT AUTO_INCREMENT,

 detail VARCHAR(2000),

 order_id INT,

 PRIMARY KEY(id)

);

#訂單狀態字典表 rows:20

CREATE TABLE dict_order_type(

  id INT AUTO_INCREMENT,

  order_type VARCHAR(200),

  PRIMARY KEY(id)

);

5.測試

[root@host79 ~]# mysql -uroot -p123456

mysql> use orders;

mysql> show tables;

 

[root@host80 ~]# mysql -uroot -p123456

mysql> use orders;

mysql> show tables;

 

二、分表

  相對於垂直拆分,水平拆分不是將表做分類,而是按照某個字段的某種規則來分散到多個庫之中,每個表中 包含一部分數據。簡單來說,我們可以將數據的水平切分理解爲是按照數據行的切分,就是將表中的某些行切分 到一個數據庫,而另外的某些行又切分到其他的數據庫中。

1、 選擇要拆分的表

  MySQL 單表存儲數據條數是有瓶頸的,單表達到 1000 萬條數據就達到了瓶頸,會影響查詢效率,需要進行水平拆分(分表)進行優化。

  例如:例子中的 orders、orders_detail 都已經達到 600 萬行數據,需要進行分表優化。

2、 分表字段

   orders 表爲例,可以根據不同自字段進行分表

 

3.schema.xml配置文件

#orders 表設置數據節點爲 dn1dn2,並指定分片規則爲 mod_rule(自定義的名字)

<table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table>

 

4.rule.xml配置文件

#rule 配置文件裏新增分片規則 mod_rule,並指定規則適用字段爲 customer_id

#還有選擇分片算法 mod-long(對字段求模運算),customer_id 對兩個節點求模,根據結果分片

#配置算法 mod-long 參數 count 2,兩個節點

<tableRule name="mod_rule">

  <rule>

    <columns>customer_id</columns>

    <algorithm>mod-long</algorithm>

  </rule>

</tableRule>

 

 

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">

  <!-- how many data nodes -->

  <property name="count">2</property>

</function>

 

 

5.重啓mycat

[root@host79 bin]# ./mycat console

6.mycat實現分片

#mycat 裏向 orders 表插入數據,INSERT 字段不能省略

>mysql

INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);

INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);

INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);

INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);

INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);

INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);

#mycatdn1dn2中查看orders表數據,分表成功

 

 

7.mycat的分片"join"

  Orders 訂單表已經進行分表操作了,和它關聯的 orders_detail 訂單詳情表如何進行 join 查詢。

  我們要對 orders_detail 也要進行分片操作。Join 的原理如下圖:

 

[1].Mycat 借鑑了 NewSQL 領域的新秀 Foundation DB 的設計思路,Foundation DB 創新性的提出了 Table Group 的概念,其將子表的存儲位置依賴於主表,並且物理上緊鄰存放,因此徹底解決了JION 的效率和性能問 題,根據這一思路,提出了基於 E-R 關係的數據分片策略,子表的記錄與所關聯的父表記錄存放在同一個數據分片上。

[2].schema.xml

#修改 schema.xml 配置文件

[root@host79 conf]# pwd

/usr/local/mycat/conf

[root@host79 conf]# vim schema.xml

<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >

  <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />

</table>

 

 

#host80下創建orders_detail

#訂單詳細表 rows:600

CREATE TABLE orders_detail(

 id INT AUTO_INCREMENT,

 detail VARCHAR(2000),

 order_id INT,

 PRIMARY KEY(id)

);

 

#host79重啓mycat

#使用host79mycat進行登錄

#訪問Mycatoders_detail表插入數據

 

INSERT INTO orders_detail(id,detail,order_id) values(1,'detail1',1);

INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);

INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);

INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);

INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);

INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);

 

#mycathost79host80中運行兩個join語句

Select o.*,od.detail from orders o inner join orders_detail od on o.id=od.order_id;

 

 

1.全局表

  在分片的情況下,當業務表因爲規模而進行分片以後,業務表與這些附屬的字典表之間的關聯,就成了比較 棘手的問題,考慮到字典表具有以下幾個特性:

① 變動不頻繁

② 數據量總體變化不大

③ 數據規模不大,很少有超過數十萬條記錄

  鑑於此,Mycat 定義了一種特殊的表,稱之爲“全局表”,全局表具有以下特性:

① 全局表的插入、更新操作會實時在所有節點上執行,保持各個分片的數據一致性

② 全局表的查詢操作,只從一個節點獲取

③ 全局表可以跟任何一個表進行 JOIN 操作

  將字典表或者符合字典表特性的一些表定義爲全局表,則從另外一個方面,很好的解決了數據JOIN 的難題。通過全局表+基於 E-R 關係的分片策略,Mycat 可以滿足 80%以上的企業應用開發

2.schema.xml

[root@host79 ~]# pwd

/root

[root@host79 ~]# vim /usr/local/mycat/conf/schema.xml

<schema>

  <table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>

</schema>

 

[1].host80創建dict_order_type

mysql> use orders;

mysql> create table dict_order_type;

[2].host79/mycat下插入數據

mysql> use TESTDB;

 

INSERT INTO dict_order_type(id,order_type) VALUES(101,'type1');

INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');

[3].測試

#host79 mycat

mysql>select * from dict_order_type;

 

#host79 mysql

mysql>select * from dict_order_type;

 

#host80 mysql

mysql>select * from dict_order_type;

 

 分片規則

1.取模

  此規則爲對分片字段求摸運算。也是水平分表最常用規則。

 

2.分片枚舉

  通過在配置文件中配置可能的枚舉 id,自己配置分片,本規則適用於特定的場景,比如有些業務需要按照省份或區縣來做保存,而全國省份區縣固定的,這類業務使用本條規則。

 

#(1)修改schema.xml配置文件

<table name="orders_ware_info" dataNode="dn1,dn2" rule="sharding_by_intfile" ></table>

#(2)修改rule.xml配置文件

<tableRule name="sharding_by_intfile">

 <rule>

 <columns>areacode</columns>

 <algorithm>hash-int</algorithm>

 </rule>

 </tableRule>

<function name="hash-int"

 class="io.mycat.route.function.PartitionByFileMap">

 <property name="mapFile">partition-hash-int.txt</property>

 <property name="type">1</property>

 <property name="defaultNode">0</property>

 </function>

# columns:分片字段,algorithm:分片函數

# mapFile:標識配置文件名稱,type:0爲int型、非0爲String,

#defaultNode:默認節點:小於 0 表示不設置默認節點,大於等於 0 表示設置默認節點,

# 設置默認節點如果碰到不識別的枚舉值,就讓它路由到默認節點,如不設置不識別就報錯

#(3)修改partition-hash-int.txt配置文件

110=0

120=1

#(4)重啓 Mycat

#(5)訪問Mycat創建表

#訂單歸屬區域信息表 

CREATE TABLE orders_ware_info

(

 `id` INT AUTO_INCREMENT comment '編號',

 `order_id` INT comment '訂單編號',

 `address` VARCHAR(200) comment '地址',

`areacode` VARCHAR(20) comment '區域編號',

PRIMARY KEY(id)

);

#(6)插入數據

INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','110');

INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','120');

#(7)查詢Mycat、dn1、dn2可以看到數據分片效果

 

3.範圍約定

  此分片適用於提前規劃好分片字段某個範圍屬於哪個分片。

#1)修改schema.xml配置文件

<table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long" ></table>

#2)修改rule.xml配置文件

<tableRule name="auto_sharding_long">

 <rule>

 <columns>order_id</columns>

 <algorithm>rang-long</algorithm>

 </rule>

 </tableRule>

<function name="rang-long"

 class="io.mycat.route.function.AutoPartitionByLong">

 <property name="mapFile">autopartition-long.txt</property>

 <property name="defaultNode">0</property>

 </function>

# columns:分片字段,algorithm:分片函數

# mapFile:標識配置文件名稱

#defaultNode:默認節點:小於 0 表示不設置默認節點,大於等於 0 表示設置默認節點, # 設置默認節點如果碰到不識別的枚舉值,就讓它路由到默認節點,如不設置不識別就

報錯

#3)修改autopartition-long.txt配置文件

0-102=0

103-200=1

#4)重啓 Mycat

#5)訪問Mycat創建表

#支付信息表

CREATE TABLE payment_info

(

 `id` INT AUTO_INCREMENT comment '編號',

 `order_id` INT comment '訂單編號',

 `payment_status` INT comment '支付狀態',

 PRIMARY KEY(id)

);

#6)插入數據

INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);

INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);

INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);

INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);

#7)查詢Mycatdn1dn2可以看到數據分片效果

 

4.按日期(天)分片

 

#(1)修改schema.xml配置文件

<table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date" ></table>

#(2)修改rule.xml配置文件

<tableRule name="sharding_by_date">

 <rule>

 <columns>login_date</columns>

 <algorithm>shardingByDate</algorithm>

 </rule>

 </tableRule>

<function name="shardingByDate" class="io.mycat.route.function.PartitionByDate">

 <property name="dateFormat">yyyy-MM-dd</property>

 <property name="sBeginDate">2019-01-01</property>

 <property name="sEndDate">2019-01-04</property>

 <property name="sPartionDay">2</property>

 </function>

# columns:分片字段,algorithm:分片函數

#dateFormat :日期格式

#sBeginDate :開始日期

#sEndDate:結束日期,則代表數據達到了這個日期的分片後循環從開始分片插入

#sPartionDay :分區天數,即默認從開始日期算起,分隔 2 天一個分區

#(3)重啓 Mycat

#(4)訪問Mycat創建表

#用戶信息表 

CREATE TABLE login_info

(

 `id` INT AUTO_INCREMENT comment '編號',

 `user_id` INT comment '用戶編號',

 `login_date` date comment '登錄日期',

 PRIMARY KEY(id)

);

#(6)插入數據

INSERT INTO login_info(id,user_id,login_date) VALUES (1,101,'2019-01-01');

INSERT INTO login_info(id,user_id,login_date) VALUES (2,102,'2019-01-02');

INSERT INTO login_info(id,user_id,login_date) VALUES (3,103,'2019-01-03');

INSERT INTO login_info(id,user_id,login_date) VALUES (4,104,'2019-01-04');

INSERT INTO login_info(id,user_id,login_date) VALUES (5,103,'2019-01-05');

INSERT INTO login_info(id,user_id,login_date) VALUES (6,104,'2019-01-06');

#(7)查詢Mycat、dn1、dn2可以看到數據分片效果

 

 

全局序列

  在實現分庫分表的情況下,數據庫自增主鍵已無法保證自增主鍵的全局唯一。爲此,Mycat 提供了全局 sequence,並且提供了包含本地配置和數據庫配置等多種實現方式

1.本地文件

  此方式 Mycat 將 sequence 配置到文件中,當使用到 sequence 中的配置後,Mycat 會更下classpath 中的 sequence_conf.properties 文件中 sequence 當前的值。

① 優點:本地加載,讀取速度較快

② 缺點:抗風險能力差,Mycat 所在主機宕機後,無法讀取本地文件。

2、 數據庫方式

  利用數據庫一個表 來進行計數累加。但是並不是每次生成序列都讀寫數據庫,這樣效率太低。Mycat 會預加載一部分號段到 Mycat 的內存中,這樣大部分讀寫序列都是在內存中完成的。如果內存中的號段用完了 Mycat 會再向數據庫要一次。

 

問:那如果 Mycat 崩潰了 ,那內存中的序列豈不是都沒了?

答:是的。如果是這樣,那麼 Mycat 啓動後會向數據庫申請新的號段,原有號段會棄用。也就是說如果 Mycat 重啓,那麼損失是當前的號段沒用完的號碼,但是不會因此出現主鍵重複

① 建庫序列腳本(host79/root)

#dn1 上創建全局序列表

CREATE TABLE MYCAT_SEQUENCE (NAME VARCHAR(50) NOT NULL,current_value INT NOT

NULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(NAME)) ENGINE=INNODB;

 

#創建全局序列所需函數

DELIMITER $$

CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)

DETERMINISTIC

BEGIN

DECLARE retval VARCHAR(64);

SET retval="-999999999,null";

SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM

MYCAT_SEQUENCE WHERE NAME = seq_name;

RETURN retval;

END $$

DELIMITER ;

 

 

DELIMITER $$

CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS

VARCHAR(64)

DETERMINISTIC

BEGIN

UPDATE MYCAT_SEQUENCE

SET current_value = VALUE

WHERE NAME = seq_name;

RETURN mycat_seq_currval(seq_name);

END $$

DELIMITER ;

 

DELIMITER $$

CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)

DETERMINISTIC

BEGIN

UPDATE MYCAT_SEQUENCE

SET current_value = current_value + increment WHERE NAME = seq_name;

RETURN mycat_seq_currval(seq_name);

END $$

DELIMITER ;

 

#初始化序列表記錄

INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,100);

 

② 修改 Mycat 配置

#修改sequence_db_conf.properties

vim sequence_db_conf.properties

#意思是 ORDERS這個序列在dn1這個節點上,具體dn1節點是哪臺機子,請參考schema.xml

  

#修改server.xml

vim server.xml

#全局序列類型:0-本地文件,1-數據庫方式,2-時間戳方式。此處應該修改成1

#重啓Mycat

 

③ 驗證全局序列

#登錄 Mycat,插入數據

insert into orders(id,amount,customer_id,order_type) values(next value for MYCATSEQ_ORDERS,1000,101,102);

#查詢數據

  

#重啓Mycat後,再次插入數據,再查詢

 

3.時間戳方式

  全局序列ID= 64 位二進制 (42(毫秒)+5(機器 ID)+5(業務編碼)+12(重複累加) 換算成十進制爲 18 位數的long 類型,每毫秒可以併發 12 位二進制的累加。

① 優點:配置簡單

② 缺點:18 位 ID 過長

4、 自主生成全局序列

  可在 java 項目裏自己生成全局序列,如下:

① 根據業務邏輯組合

② 可以利用 redis 的單線程原子性 incr 來生成序列但,自主生成需要單獨在工程中用 java 代碼實現,還是推薦使用 Mycat 自帶全局序列。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章