MySql第四章,MyCat讀寫分離、分區分片

MySql第四章,MyCat讀寫分離、分區分片

一、讀寫分離

Redis是從 頭 開始複製
MySql是從 接入點  開始複製

1、一主一從

1.1、MySql主從複製搭建

1>、主從配置

Master

修改配置文件: vim /etc/my.cnf
#主服務器唯-ID
server-id=l
#啓用二進制日誌。
log-bin=mysql-bin
#設置logbin格式
binlog_format=STATEMENT
#設置不要複製的數據庫(可設置多個)。
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#設置需要複製的數據庫
binlog-do-db=mycatdb    #該數據庫一定要 master 還未創建,等開啓主從複製後  再在主機上創建

解析logbin三種格式

statement:(默認)
	記錄SQL語句,但是遇到update xxx set xxx time = now() where xxx;時   主從的time就會出現不一致
	
	[用statement效率高,但是不可以在 代碼 中使用 函數]

row:
	記錄行變化, 1 time->1.5   2 time->2  3 time->1;  如果全表更新的話,效率會很慢

MIXED
	mixed是statement和row的混合,解決了一部分數據不一致或效率慢的問題。
	但是遇到 @@host name 獲取當前主機名稱時,會識別不了

Slave

修改配置文件: vim /etc/my.cnf
#從服務器唯- ID
server-id=2
#啓用中繼日誌。
relay-log=mysql-relay

2>、關閉主從防火牆

systemctl stop firewalld
##查看防火牆狀態  systemctl status firewalld

3>、Master上創建賬戶並賦權Slave

##授權
GRANT REPLICATION SLAVE ON *.* TO 'ren'@'192.168.0.107' IDENTIFIED BY 'admin123';
或
GRANT REPLICATION SLAVE ON *.* TO 'ren'@'%' IDENTIFIED BY'admin123";

##刷新權限
flush privileges;

4>、Slave上配置需要複製的主機

CHANGE MASTER TO MASTER_HOST='192.168.0.111',
MASTER_USER='ren',MASTER_PASSWORD='admin123',
MASTER_LOG_FILE='mysql-bin.000006',
MASTER_LOG_POS=154;

##啓動從服務器複製功能
start slave;


##檢測--下面兩個參數都是YES,則說明主從配置成功  show slave status \G
show slave status \G

1.2、MyCat讀寫分離配置

1>、server.xml

##mycat對外暴露的邏輯MySQL,name爲用戶名、password爲用戶密碼、schemas爲邏輯數據庫名稱

<user name="mycat">
    <property name="password">mycat</property>
    <property name="schemas">TESTDB</property>
    
    <!-- 表級 DML 權限設置 -->
    <!--
       <privileges check="false">
            <schema name="TESTDB" dml="0110" >
                  <table name="tb01" dml="0000"></table>
                  <table name="tb02" dml="1111"></table>
            </schema>
       </privileges>
    -->
</user>

2>、schema.xml

##schema:mycat對外暴露的邏輯庫
##dataNode:MySQL對應的數據庫
##dataHost:MySql對應的主從名稱

##############balance設置爲3#############

<?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">
        </schema>

    	<!--指定庫名-->
        <dataNode name="dn1" dataHost="host1" database="mycatdb" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
           writeType="0" dbType="mysql" dbDriver="native" switchType="1" 
           slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
            	<!--寫節點-->
                <writeHost host="hostM1" url="192.168.0.111:3306" user="root"
                                   password="admin123">
                    <!--讀節點-->    
                    <readHost host="hostS1" url="192.168.0.107:3306" user="root" password="admin123" />
                </writeHost>
        </dataHost>

</mycat:schema>

balance參數解析說明:

修改dataHost的balance屬性,通過此屬性配置  讀寫分離  的類型

負載均衡類型,目前的取值有4種:。
1、balance="0",不開啓讀寫分離機制,所有讀操作都發送到當前可用的writeHost上。。

2、balance="1", 全部的readHost與stand by writeHost參與select 語句的負載均衡,簡單的說,
  當雙主雙從模式(M1->S1,M2->S2, 並且M1與M2互爲主備),正常情況下,M2,S1,S2 都參與select 
  語句的負載均衡。。

2、balance="2", 所有讀操作都隨機的在writeHost. readhost. 上分發。

4、balance="3", 所有讀請求隨機的分發到readhost 執行,writerHost 不負擔讀壓力。

switchType參數解析說明:

switchType指的是切換的模式,目前的取值也有4種:

1. switchType='-1' 表示不自動切換

2. switchType='1' 默認值,表示自動切換

3. switchType='2' 基於MySQL主從同步的狀態決定是否切換,心跳語句爲 show slave status

4. switchType='3'基於MySQL galary cluster的切換機制(適合集羣)(1.4.1),心跳語句爲 show status like 'wsrep%'。

WriteType參數解析說明:

writeType負載均衡類型,目前的取值有3種:

1. writeType="0", 所有寫操作發送到配置的第一個writeHost,第一個掛了切到還生存的第二個writeHost,重新啓
動後已切換後的爲準,切換記錄在配置文件中:dnindex.properties

2. writeType="1",所有寫操作都隨機的發送到配置的writeHost

3. writeType="2",沒實現。

1.3、測試

1、在master111上插入數據
create database mycatdb;
use mycatdb;
create table user(id int,name varchar(100));
insert into user values(1,@@hostname);

##查詢Master111上數據和Slave107上的數據
##111
mysql> select * from user;
+------+---------+
| id   | name    |
+------+---------+
|    1 | master1 |
+------+---------+


##107
mysql> select * from user;
+------+--------+
| id   | name   |
+------+--------+
|    1 | slave1 |
+------+--------+


2、啓動mycat
./mycat console
mysql -umycat -pmycat -h 192.168.0.111 -P 8066

mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |
+----------+
1 row in set (0.01 sec)
mysql> use TESTDB;
mysql> select * from user;
+------+--------+
| id   | name   |
+------+--------+
|    1 | slave1 |
+------+--------+
1 row in set (0.00 sec)

2、雙主雙從

一個主機m1用於處理所有寫請求,它的從機s1和另一臺主機m2還有它的從機s2負貴所有讀請求。

當ml主機宕機後,m2主機負責寫請求,ml、m2互爲備機。

[master1 master2 互備主從,master1宕機後master2作爲寫主機,master1重啓後作爲master2的備機]

在這裏插入圖片描述

編號 角色 IP host_name
1 Master1 192.168.0.111 Master1
2 Slave1 192.168.0.107 Slave1
3 Master2 192.168.0.112 Master2
4 Slave2 192.168.0.108 Slave2

2.1、MySql雙主雙從搭建

1>、Master1-111機

配置 vi /etc/my.cnf

#主服務器唯一ID
server-id=1
#啓用二進制日誌
log-bin-mysql-bin
#設置不要複製的數據庫(可設置多個)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#設置需要複製的數據庫
binlog-do-db=需要複製的主數據庫名字
#設置logbin格式
binlog_format=STATEMENT
#在作爲從數據庫的時候,有寫入操作也要更新二進制日誌文件
log-slave-updates
#表示自增長字段從哪個數開始,取值範圍是1.. 65535
auto-increment-offset=1
#表示自增長字段每次遞增的量,取值範圍是1 .. 65535
auto-increment-increment=2

創建賬戶並授權slave1

##授權
GRANT REPLICATION SLAVE ON *.* TO 'ren'@'%' IDENTIFIED BY'admin123";

##刷新權限
flush privileges;

##查看master1狀態
show master status;

兩臺主機互相複製,master1複製master2

CHANGE MASTER TO MASTER_HOST='192.168.0.112',
MASTER_USER='ren',MASTER_PASSWORD='admin123',
MASTER_LOG_FILE='mysql-bin.000006',
MASTER_LOG_POS=154;

##啓動從服務器複製功能
start slave;


##檢測--下面兩個參數都是YES,則說明主從配置成功  show slave status \G
show slave status \G

2>、Master2-112機

配置 vi /etc/my.cnf

#主服務器唯一ID
server-id=3
#啓用二進制日誌
log-bin-mysql-bin
#設置不要複製的數據庫(可設置多個)
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
#設置需要複製的數據庫
binlog-do-db=需要複製的主數據庫名字
#設置logbin格式
binlog_format=STATEMENT
#在作爲從數據庫的時候,有寫入操作也要更新二進制日誌文件
log-slave-updates
#表示自增長字段從哪個數開始,取值範圍是1.. 65535
auto-increment-offset=2
#表示自增長字段每次遞增的量,取值範圍是1 .. 65535
auto-increment-increment=2

創建賬戶並授權slave2

##授權
GRANT REPLICATION SLAVE ON *.* TO 'ren'@'%' IDENTIFIED BY'admin123";

##刷新權限
flush privileges;

##查看master1狀態
show master status;

兩臺主機互相複製,master2複製master1

CHANGE MASTER TO MASTER_HOST='192.168.0.111',
MASTER_USER='ren',MASTER_PASSWORD='admin123',
MASTER_LOG_FILE='mysql-bin.000006',
MASTER_LOG_POS=154;

##啓動從服務器複製功能
start slave;


##檢測--下面兩個參數都是YES,則說明主從配置成功  show slave status \G
show slave status \G

3>、Slave1-107機

配置 vi /etc/my.cnf

#從服務器唯- ID
server-id=2
#啓用中繼日誌。
relay-log=mysql-relay

連接需要複製的主機master1

CHANGE MASTER TO MASTER_HOST='192.168.0.111',
MASTER_USER='ren',MASTER_PASSWORD='admin123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;

##啓動從服務器複製功能
start slave;


##檢測--下面兩個參數都是YES,則說明主從配置成功  show slave status \G
show slave status \G

4>、Slave2-108機

配置 vi /etc/my.cnf

#從服務器唯- ID
server-id=4
#啓用中繼日誌。
relay-log=mysql-relay

連接需要複製的主機master2

CHANGE MASTER TO MASTER_HOST='192.168.0.112',
MASTER_USER='ren',MASTER_PASSWORD='admin123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;

##啓動從服務器複製功能
start slave;


##檢測--下面兩個參數都是YES,則說明主從配置成功  show slave status \G
show slave status \G

2.2、MyCat讀寫分離配置

配置:schema.xml

##balance設置爲1

<?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">
        </schema>

    	<!--指定庫名-->
        <dataNode name="dn1" dataHost="host1" database="mycatdb" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
           writeType="0" dbType="mysql" dbDriver="native" switchType="1" 
           slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
            	<!--寫節點-->
                <writeHost host="hostM1" url="192.168.0.111:3306" user="root"
                                   password="admin123">
                    <!--讀節點-->    
                    <readHost host="hostS1" url="192.168.0.107:3306" user="root" password="admin123" />
                </writeHost>
            
                <!--寫節點-->
                <writeHost host="hostM1" url="192.168.0.112:3306" user="root"
                                   password="admin123">
                    <!--讀節點-->    
                    <readHost host="hostS1" url="192.168.0.108:3306" user="root" password="admin123" />
                </writeHost>
            
        </dataHost>

</mycat:schema>

2.3、測試

##這裏就不再寫測試方法了,參考1.3一主一從的測試步驟即可

二、垂直拆分–分庫

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

注:不同庫中的表無法做關聯查詢(left join、right join)

[垂直分庫的主要作用是減輕磁盤的壓力,單表的數據量並未減少]

在這裏插入圖片描述

修改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="users" dataNode="dn2"></table>
            <table name="comments" dataNode="dn2"></table>
        </schema>

    	<!--指定庫名-->
        <dataNode name="dn1" dataHost="host1" database="shopping" />
        <dataNode name="dn2" dataHost="host2" database="shopping" />
    
    
        <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
           writeType="0" dbType="mysql" dbDriver="native" switchType="1" 
           slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
            	<!--寫節點-->
                <writeHost host="hostM1" url="192.168.0.111:3306" user="root"
                                   password="admin123">
                    <!--讀節點-->    
                    <readHost host="hostS1" url="192.168.0.107:3306" user="root" password="admin123" />
                </writeHost>
        </dataHost>
    
    
        <dataHost name="host2" maxCon="1000" minCon="10" balance="3"
           writeType="0" dbType="mysql" dbDriver="native" switchType="1" 
           slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
            	<!--寫節點-->
                <writeHost host="hostM1" url="192.168.0.112:3306" user="root"
                                   password="admin123">
                    <!--讀節點-->    
                    <readHost host="hostS1" url="192.168.0.108:3306" user="root" password="admin123" />
                </writeHost>
        </dataHost>

</mycat:schema>

測試:

1、分別啓動四個MySQL服務和mycat服務

2、在mycat上創建庫和四張表

##111上創建庫
create database shopping;

##112上創建庫
create database shopping;

##mycat上創建表
create table orders(......);

##mycat上創建表
create table goods(......);

##mycat上創建表
create table users(.......);

##mycat上創建表
create table comments(.......);

3、結果:
orders和goods表被分配到了 dn1即 111和107數據庫上了
users和comments表被分配到了 dn2即 112和108數據庫上了

三、水平拆分–分表

相對於垂直拆分,水平拆分不是將表做分類,而是按照某個字段的某種規則來分散到多個庫之中,每個表中包含一部分數據。

簡單來說,我們可以將數據的水平切分理解爲是按照數據行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其他的數據庫中。

在這裏插入圖片描述

1、選擇要拆分的表

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

2、確定分表的字段

根據字段進行拆分,如所屬區域  所屬年份  所屬類型  或  客戶ID
如:600W數據量的orders訂單表,我們可以根據客戶ID進行劃分,同一個客戶的訂單劃分到同一張表裏

1、單表的拆分

拿order表做例子:

schema.xml

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

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
    <table name="users" dataNode="dn2"></table>
    <table name="orders" dataNode="dn1,dn2" rule="mod_rule"></table>
</schema>

rule.xml

#在rule配置文件裏新增分片規則mod_ _rule, 並指定規則適用字段爲customer_ jid,。
#還有選擇分片算法mod-long (對字段求模運算),customer_id對兩個節點求模,根據結果分片。
#配置算法mod-long參數count 爲2,兩個節點。


<tableRule name="mod_rule">
    <rule>
        <columns>customer_id</columns>  <!--根據customer_id字段劃分-->
        <algorithm>mod-long</algorithm> <!--算法-->
    </rule>
</tableRule>


<!--這個本身mycat自帶了,只需要設置count就好-->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <!-- how many data nodes -->
    <property name="count">2</property>
</function>


測試:

1、分別在111和112上創建orders表
CREATE TABLE orders(
    id INT AUTO INCREMEN,
    order_type INT,
    customer_ id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY(id)
}

2、mycat向orders表插入數據
[注意:customer_id必須指定,不能寫成insert into order values (xxxxxxxx)]
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);


3、結果如圖:3、4、5在一張表中  1、2、6在另一張表中

在這裏插入圖片描述

上圖的結果,其實是對兩個庫中的兩張表進行了union,即345和126合併了,如果想按順序可以order by
select * from orders order by id;

2、分片後的 join

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

我們要對orders_detail 也要進行分片操作。

CREATE TABLE orders_ detail(
    id INT AUTO INCREMENT,
    detail VARCHAR(2000),
    order_id INT,
    PRIMARY KEY(id)
);

由於orders表進行的拆分,所以如果orders表在和沒有拆分的orders_detail表進行 關聯查詢的時候,就會報錯。

即一個庫裏邊有orders_detail,另一個庫裏沒有orders_detail。orders關聯不到,所以orders_detail表也要進行拆分。

如何拆分orders_detail表呢,應該根據order_id進行拆分,即將和orders表有關聯的orders_detail行拆分到一個庫中。

2.1、ER表分表

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

修改schema.xml配置文件

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
    <table name="users" dataNode="dn2"></table>
    <table name="orders" dataNode="dn1,dn2" rule="mod_rule">
        <!--ER表-->
    	<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
    </table>
</schema>

##joinKey爲orders_detail表的外鍵
##parentKey爲joinKey外鍵所關聯的orders表的主鍵
##適用於1:1或1:N的表關係中

在這裏插入圖片描述

2.2、全局表

##訂單狀態字典表
CREATE TABLE dict_order_type(
    id INT AUTO INCREMENT,
    order_type VARCHAR(200),
    PRIMARY KEY(id)
);

dict_order_type表作爲一個字典表,order表無論是在dn1庫還是在dn2庫中都會需要用到。

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

①、變動不頻繁。
②、數據量總體變化不大。
③、數據規模不大,很少有超過數十萬條記錄。

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

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

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

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

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

修改schema.xml:

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
    <table name="users" dataNode="dn2"></table>
    <table name="orders" dataNode="dn1,dn2" rule="mod_rule">
        <!--ER表-->
    	<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
    </table>
    
    <!--全局表-->
    <table name="dict_order_type" dataNode- ="dn1,dn2" type="global" > </table>
</schema>

3、常用的分片規則

mycat的rule.xml中定義了以下常用分片規則:

<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
    <property name="seed">0</property>
    <property name="count">2</property>
    <property name="virtualBucketTimes">160</property>
</function>

<function name="crc32slot" class="io.mycat.route.function.PartitionByCRC32PreSlot">
    <property name="count">2</property>
</function>

<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>

<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
</function>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">3</property>
</function>

<function name="func1" class="io.mycat.route.function.PartitionByLong">
    <property name="partitionCount">8</property>
    <property name="partitionLength">128</property>
</function>

<function name="latestMonth" class="io.mycat.route.function.LatestMonthPartion">
    <property name="splitOneDay">24</property>
</function>

<function name="partbymonth" class="io.mycat.route.function.PartitionByMonth">
    <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sBeginDate">2015-01-01</property>
</function>

<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
    <property name="mapFile">partition-range-mod.txt</property>
</function>

<function name="jump-consistent-hash"  class="io.mycat.route.function.PartitionByJumpConsistentHash">
    <property name="totalBuckets">3</property>
</function>

3.1、取模:mod-long

此規則爲對分片字段求摸運算。也是水平分表最常用規則。上面orders 表就採用了此規則。

【如:一般根據庫的個數進行取模】

<tableRule name="mod_rule">
    <rule>
        <columns>customer_id</columns>  <!--根據customer_id字段劃分-->
        <algorithm>mod-long</algorithm> <!--算法-->
    </rule>
</tableRule>

3.2、分片枚舉:hash-int

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

【如:根據省份prov劃分】

修改schema.xml

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

修改rule.xml

<tableRule name-"sharding_by_intfile">
    <rule>
        <columns>areacode</columns>  <!--劃分字段-->
        <algorithm>hash-int</algorithm>  <!--劃分算法-->
    </rule>
</tableRule>

<function name="hash-int" class="io.mycatroute.function.PartitionByFileMap">
    <property name="mapFile">partition-hash-int.txt</property>
    <property name="type">1</property>
    <property name="defaultNode">0</property>
</function>

##mapFile標識配置文件名稱
##type 0爲int型、非0爲String型
##defaultNode: 默認節點:小於0表示不設置默認節點,大於等於0表示設置默認節點,設置默認節點如果碰到不識別的
枚舉值,就讓它路由到默認節點,如不設置不識別就報錯

修改partition-hash-int.txt

##areacode爲110放在node0節點,120放在node1節點
110=0
120=1

創建數據表 且 插入數據:

CREATE TABLE orders_are_info(
    'id' InT AUTO_INCREMENT comment'編號',
    'order_id' INT comment '訂單編號',
    'address' VARCHAR(200) comment '地址',
    'areacode' VARCHAR(20) comment '區域編號',
    PRIMARY KEY(id)
);INSERT INTO orders_are_info(id, order_id,address,areacode) VALUES (1,1,北京,110);
INSERT INTO orders_are_info(id, order_id,address,areacode) VALUES (2,2,天津,120);

3.3、範圍約定:rang-long

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

修改schemax.xml

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

修改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>
##defaultNode: 默認節點:小於0表示不設置默認節點,大於等於0表示設置默認節點,設置默認節點如果碰到不識別的
枚舉值,就讓它路由到默認節點,如不設置不識別就報錯

修改autopartition-long.txt:

##order_id在0-102寫入node0  103-200寫入node1
0-102=0
103-200=1

創建數據表 並 插入數據:

CREATE TABLE payment_info(
    'id' INT AUTO_INCREMENT comment '編號',
    'orde_id' INT comment '訂單編號',
    'payment_status'INT comment '支付狀態',
    PRIMARY KEY(id)
);

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);

3.4、按日期(天)分片

修改schema.xml

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

修改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>
    <prpperty 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天一一個分區。

##如果循環完成則返回第一個節點,以上01、02爲node1,03、04爲node2.當05、06分配時,則會循環到第一個分片即分
配到node1上

創建數據表 並 插入數據:

CREATE TABLE login_info(
    'id' INT AUTO_INCREMENT comment '編號''user_id' INT comment '用戶編號',
    'login_date' DATE comment '登錄日期'PRIMARY KEY(id)
);
    
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');

4、全局序列號

在實現分庫分表的情況下,數據庫自增主鍵已無法保證自增主鍵的全局唯一

爲此,Mycat 提供了全局sequence, 並且提供了包含本地配置和數據庫配置等多種實現方式。

4.1、本地文件–不推薦

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

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

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

#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=20000
GLOBAL.CURID=10000

其中HISIDS表示使用過的歷史分段(一般無特殊需要可不配置),MINID表示最小ID值,MAXID表示最大ID值,CURID表示當前ID值。

使用方式:

在server.xml中配置如下:

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

測試:

mysql> create table test(id int,name varchar(20));
Query OK, 0 rows affected (0.13 sec)

mysql> insert into test(id,name) values(next value for MYCATSEQ_GLOBAL,@@hostname);
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> select * from test;
+-------+--------------+
| id    | name         |
+-------+--------------+
| 10001 | mysql-slave2 |
+-------+--------------+
1 row in set (0.02 sec)

此時,sequence_conf.properties中GLOBAL.CURID值爲10001。當然,可以使用sequence_conf.properties中定義的任何規則,譬如:

# self define sequence
COMPANY.HISIDS=
COMPANY.MINID=1001
COMPANY.MAXID=2000
COMPANY.CURID=1000


mysql> insert into test(id,name) values(next value for MYCATSEQ_COMPANY,@@hostname);
Query OK, 1 row affected, 1 warning (0.02 sec)

mysql> select * from test;
+-------+--------------+
| id    | name         |
+-------+--------------+
| 10001 | mysql-slave1 |
|  1001 | mysql-slave1 |
+-------+--------------+
2 rows in set (0.10 sec)

4.2、時間戳方式–雪花算法

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

①優點:配置簡單

②缺點: 18位ID過長

1>、server.xml指定時間戳方式生成序列方式

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

2>、schema.xml中配置分片表

注意分片表的主鍵爲自增長,分片表的主鍵類型不能爲int類型

<schema name="TESTTIME" checkSQLschema="false" sqlMaxLimit="100">
    <table name="test" dataNode="dn1,dn2" primaryKey="id" autoIncrement="true" rule="mod-long" />
</schema>
 
<dataNode name="dn1" dataHost="localhost1" database="testtimeseq1" />
<dataNode name="dn2" dataHost="localhost1" database="testtimeseq2" />

3>、配置sequence_time_conf.properties

##WORKID=0-31 任意整數
##DATAACENTERID=0-31 任意整數
##多個 mycat 節點下每個 mycat 配置的 WORKID,DATAACENTERID 不同,
##組成唯一標識,總共支持 32*32=1024 種組合。

#sequence depend on TIME
WORKID=05
DATAACENTERID=05

測試:

insert into test(id,xm) values(next value for MYCATSEQ_GLOBAL,'lisi') ; 
insert into test(id,xm) values(next value for MYCATSEQ_GLOBAL,'lisi') ;

4.3、數據庫方式

原理

在數據庫中建立一張表,存放sequence名稱(name),sequence當前值(current_value),
步長(increment int類型每次讀取多少個sequence,假設爲K)等信息

Sequence獲取步驟:

1)、第一次使用該sequence時,根據傳入的sequence名稱,從數據庫這張表中讀取current_value,和increment到MyCat中,並將數據庫中的current_value設置爲原current_value值+increment值(實現方式是基於後續的存儲函數)

2)、MyCat將讀取到current_value+increment作爲本次要使用的sequence值,下次使用時,自動加1,當使用increment次後,執行步驟1)相同的操作.

MyCat負責維護這張表,用到哪些sequence,只需要在這張表中插入一條記錄即可。若某次讀取的sequence沒有用完,系統就停掉了,則這次讀取的sequence剩餘值不會再使用。

使用步驟:

1>、配置server.xml文件

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

2>、配置數據庫

在其中一個分片點對應的數據庫中創建表和存儲過程

①、我在schema.xml中配置信息如下:

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
    <table name="users" dataNode="dn2"></table>
    <table name="orders" dataNode="dn1,dn2" rule="mod_rule"></table>
</schema>

<!--指定庫名-->
<dataNode name="dn1" dataHost="host1" database="shopping" />
<dataNode name="dn2" dataHost="host2" database="shopping" />

譬如我在dn1中創建,對應的數據庫名爲shopping(爲什麼這裏會涉及到datanode,因爲後續的sequence_db_conf.properties文件會使用到),

注意,是登錄到數據庫中創建,而不是在mycat中創建

②、創建MYCAT_SEQUENCE表

##注意:MYCAT_SEQUENCE必須大寫。

DROP TABLE IF EXISTS MYCAT_SEQUENCE;

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;

##name sequence名稱
##current_value 當前value
##increment 增長步長! 可理解爲mycat在數據庫中一次讀取多少個sequence. 當這些用完後, 下次再從數據庫中讀取.

③、插入sequence記錄

##代表插入了一個名爲mycat的sequence,當前值爲1,步長爲100。

INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('ORDERS', 1, 100);

④、創建存儲函數:

##必須在同一個數據庫中創建,在本例中,是db2。一共要創建三個。

##獲取當前sequence的值(返回當前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER $
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
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 ;


##設置sequence值
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER $
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS     varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END $
DELIMITER ;


##獲取下一個sequence值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER $
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END $
DELIMITER ;

3>、在mycat conf目錄下的sequence_db_conf.properties文件中添加如下內容:

##ORDERS這個序列在dn1上,具體dn1節點在哪臺機器上,參考schema.xml
##注意:ORDERS必須爲大寫,這個與表的數據是否大寫無關,
##事實上,MYCAT_SEQUENCE中name是否大小寫對結果沒有影響。

GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1

測試:

INSERT INTO orders(id,order_type,customer_id,amount) VALUES 
(next value for MYCATSEQ_ORDERS,101,100,100100);

4.4、自主生成全局序列–推薦

可在java項目裏自己生成全局序列

①根據業務邏輯組合

②可以利用redis 的單線程原子性incr 來生成序列。

③可以使用zk

但,自主生成需要單獨在工程中用java代碼實現,還是推薦使用Mycat自帶全局序列。

//TODO 這個以後再單獨拿出來寫

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