MyCat實現讀寫分離+分庫分表+全局表

前言

mycat功能強大,配置簡單,做爲數據庫中間件,是一個很是優秀的開源產品,關於MyCat的部署安裝,能夠參考博文:MyCat部署安裝node

本博文將展現mycat的以下功能:mysql

  • 讀寫分離;
  • 分庫分表;
  • 全局表;

環境以下:sql

系統 IP 主機名 服務
Centos 7.5 192.168.20.2 mysql01 MySQL 5.7.24
Centos 7.5 192.168.20.3 mysql02 MySQL 5.7.24
Centos 7.5 192.168.20.3 mysql03 MySQL 5.7.24
Centos 7.5 192.168.20.4 mycat Mycat

注:主機mysql01和mysql02爲主從複製關係,能夠參考博文MySQL高可用方案——雙主(注:只須要參考博文作出主從效果便可,並不須要雙主,也不須要keepalived來作高可用)來搭建。mycat服務請參考博文MyCat部署安裝來搭建,這裏新增的主機mysql03爲一個剛剛搭建好的mysql服務器,能夠參考博文Centos部署MySQL 5.7來搭建。數據庫

一、mycat實現讀寫分離

這裏沒有用到mysql03主機,讀寫分離的實現比較簡單,只須要修改mycat的下面兩個配置文件便可。以下:vim

1)修改server.xml文件

[root@mycat mycat]# pwd
/usr/local/mycat   <!--此處爲mycat的家目錄-->
[root@mycat mycat]# vim conf/server.xml
<!--指定client鏈接mycat的用戶名及密碼,此處的帳號密碼與MySQL數據庫無關-->
[root@mycat mycat]# vim conf/server.xml    <!--定位到80行左右,修改以下-->
        <user name="mycat" defaultAccount="true">  <!--mycat爲用戶名-->
                <property name="password">pwd@123</property>   <!--此處爲用戶mycat的登陸密碼-->
                <property name="schemas">test_mycat</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文件

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--下面的schema必須和第一個文件中的schema name(邏輯庫名)一致。-->
        <schema name="test_mycat" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="test" />    <!--這裏的database是指定要鏈接後端的哪一個數據庫,這裏鏈接的是test庫-->
                <!--下面是指定後端真實的MySQL主機,關於下面的balance值,有三個可選值,將在下面寫下來-->
        <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
                          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.20.2:3306" user="root"
                                   password="123.com">
                        <!-- can have multi read hosts -->
                                                <!--指定後端哪臺主機用來讀數據-->
                        <readHost host="hostS2" url="192.168.20.3:3306" user="root" password="123.com" />
                </writeHost>
        </dataHost>
</mycat:schema>

注:上面提到的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 語句的負載均衡。
  3. balance="2",全部讀操做都隨機的在 writeHost、readhost 上分發。
  4. balance="3",全部讀請求隨機的分發到 wiriterHost 對應的 readhost 執行,writerHost 不負擔讀壓力,注意 balance=3 只在 1.4 及其之後版本有,1.3 沒有。

writeTyep字段有如下3中取值:服務器

一、writeType="0", 全部寫操做發送到配置的第一個 writeHost,第一個掛了切到還生存的第二個 writeHost,從新啓動後已切換後的爲準,切換記錄在配置文件中:dnindex.properties 。
二、writeType="1",全部寫操做都隨機的發送到配置的 writeHost,1.5 之後廢棄不推薦。switchType 屬性負載均衡

  • -1 表示不自動切換。
  • 1 默認值,自動切換。
  • 2 基於 MySQL 主從同步的狀態決定是否切換。

修改後的完整配置文件以下(爲避免冗餘,刪除了部分註釋):

server.xml配置文件內容以下:

[root@mycat mycat]# vim conf/server.xml 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
        <system>
        <property name="useSqlStat">0</property>  <!-- 1爲開啓實時統計、0爲關閉 -->
        <property name="useGlobleTableCheck">0</property>  <!-- 1爲開啓全加班一致性檢測、0爲關閉 -->

                <property name="sequnceHandlerType">2</property>
                <property name="processorBufferPoolType">0</property>
                <property name="handleDistributedTransactions">0</property>

                <property name="useOffHeapForMerge">1</property>
                <property name="memoryPageSize">1m</property>
                <property name="spillsFileBufferSize">1k</property>

                <property name="useStreamOutput">0</property>
                <property name="systemReserveMemorySize">384m</property>

                <property name="useZKSwitch">true</property>

        </system>
        <user name="mycat" >
                <property name="password">pwd@123</property>
                <property name="schemas">test_mycat</property>
        </user>
</mycat:server>

schema.xml配置文件內容以下:

[root@mycat mycat]# vim conf/schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="test_mycat" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        </schema>
        <dataNode name="dn1" dataHost="host1" database="test" />
        <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
                          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.20.2:3306" user="root"
                                   password="123.com">
                        <!-- can have multi read hosts -->
                        <readHost host="hostS2" url="192.168.20.3:3306" user="root" password="123.com" />
                </writeHost>
        </dataHost>
</mycat:schema>

注:至此,後端數據庫上自行建立有相關權限的用戶,好比我在上面的datahost字段中指定的writehost和readhost字段的user值爲root,則數據庫的root帳號要有對應的登陸權限(默認root用戶只容許在localhost登陸,因此要自行修改root權限或換成其餘有權限的用戶)。ide

總結:
1.指定的後端數據庫登陸用戶,必須能夠有相應的權限;
2.在上面指定的後端真實數據庫爲test,因此後端必需要有這個test庫;測試

3)啓動mycat並測試讀寫分離效果

[root@mycat mycat]# mycat start       #啓動mycat
[root@mycat mycat]# mysql -umycat -ppwd@123 -h 192.168.20.4 -P 8066   #登陸到mycat
#192.168.20.4爲mycat主機IP,mycat監聽端口爲8066,使用的登陸用戶爲server.xml文件中建立的用戶
#建立表並插入一些測試數據
mysql> use test_mycat;
mysql> create table t1 (id int,name varchar(4));
mysql> insert into t1 values(1,'張三'),(2,'李四'),(3,'王五');
#此時,去後端數據庫上查看,應該是有上面這些數據的。
#如今能夠暫時關閉後端數據庫的主從複製,而後再mycat主機上繼續插入數據,測試是否能夠讀到
#關閉主從複製命令:stop slave;
#如下操做仍是在mycat主機進行
mysql> insert into t1 values(4,'趙六');    #插入一條數據
Query OK, 1 row affected (0.01 sec)

mysql> select * from t1;      #查詢不到剛插入的數據,說明讀寫操做沒有在同一臺主機上進行
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
#能夠去後端兩個數據庫分別進行查看數據
#mysql01主機的數據以下(說明寫操做是在mysql01):
mysql> select * from t1;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
|    4 | 趙六   |
+------+--------+
4 rows in set (0.00 sec)
#mysql02主機的數據以下:
mysql> select * from t1(說明讀操做是在mysql02);
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
3 rows in set (0.00 sec)

讀寫分離效果至此結束。(爲不影響後面的測試進行,最好恢復mysql01和mysql02主機的主從複製)。

二、mycat實現分庫

一個數據庫由不少表組成,每一個表對應着不一樣的業務,所謂分庫,就是按照業務將表進行分類,分不到不一樣的數據庫上面,這樣也就將數據或者說壓力分擔到不一樣的庫上面,分庫的原則:有緊密關聯關係的表應該在一個庫裏,相互沒有或者關聯關係不大的表能夠分到不一樣的庫裏。

分庫舉例:

假設如今有四張表: customer, orders, orders_detail, dict_order_type,每張表都有數百萬條數據,那麼這四張表如若要實現分庫,則能夠將customer表單獨分離到一個數據庫,另外三張表單獨在另外一個數據庫。

1)修改server.xml文件(就改了一下邏輯庫名)

[root@mycat mycat]# cat conf/server.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
    <system>
    <property name="useSqlStat">0</property>  <!-- 1爲開啓實時統計、0爲關閉 -->
    <property name="useGlobleTableCheck">0</property>  <!-- 1爲開啓全加班一致性檢測、0爲關閉 -->

        <property name="sequnceHandlerType">2</property>
        <property name="processorBufferPoolType">0</property>
        <property name="handleDistributedTransactions">0</property>

        <property name="useOffHeapForMerge">1</property>
        <property name="memoryPageSize">1m</property>
        <property name="spillsFileBufferSize">1k</property>

        <property name="useStreamOutput">0</property>
        <property name="systemReserveMemorySize">384m</property>

        <property name="useZKSwitch">true</property>

    </system>
    <user name="mycat" >
        <property name="password">pwd@123</property>
        <property name="schemas">test_db</property>
    </user>
</mycat:server>

2)修改schema.xml文件

[root@mycat mycat]# cat conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

    <schema name="test_db" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        <table name="customer" dataNode="dn2"/>   <!--這裏指定customer在dn2節點上-->
    </schema>
    <dataNode name="dn1" dataHost="host1" database="test" />
    <dataNode name="dn2" dataHost="host2" database="test" />
    <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
              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.20.2:3306" user="root"
                   password="123.com">
            <!-- can have multi read hosts -->
            <readHost host="hostS2" url="192.168.20.3:3306" user="root" password="123.com" />
        </writeHost>
    </dataHost>
    <!--下面添加host2主機字段,指定的主機就是mysql03-->
    <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.20.21:3306" user="root"
                   password="123.com">
        </writeHost>
    </dataHost>
</mycat:schema>

3)重啓mycat

[root@mycat mycat]# mycat restart

注:後端指定的真實數據庫必需要有test數據庫,由於在上面schema.xml文件中的dataNode字段指定的是鏈接後端的test數據庫。

4)測試分庫效果

#登陸到mycat
[root@mycat mycat]# mysql -umycat -ppwd@123 -h 192.168.20.4 -P 8066
#建立相應的表
mysql> use test_db;
mysql> CREATE TABLE customer(
    -> id_a INT AUTO_INCREMENT,
    -> NAME VARCHAR(200),
    -> PRIMARY KEY(id_a)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE TABLE orders(
    -> id_b INT AUTO_INCREMENT,
    -> order_type INT,
    -> customer_id INT,
    -> amount DECIMAL(10,2),
    -> PRIMARY KEY(id_b)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE orders_detail(
    -> id_c INT AUTO_INCREMENT,
    -> detail VARCHAR(2000),
    -> order_id INT,
    -> PRIMARY KEY(id_c)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> CREATE TABLE dict_order_type(
    -> id_d INT AUTO_INCREMENT,
    -> order_type VARCHAR(200),
    -> PRIMARY KEY(id_d)
    -> );
Query OK, 0 rows affected (0.01 sec)

#登陸到後端數據庫上,查看錶是否按照預計的效果拆分
[root@mycat mycat]# mysql -uroot -p123.com -h 192.168.20.2
mysql> use test;

mysql> show tables;             #能夠看到主機mysql01這裏有三個表
+-----------------+
| Tables_in_test  |
+-----------------+
| dict_order_type |
| orders          |
| orders_detail   |
+-----------------+
3 rows in set (0.00 sec)

#登陸到mysql03上查看
[root@mycat mycat]# mysql -uroot -p123.com -h 192.168.20.21
mysql> use test;

mysql> show tables;     #能夠看到這裏只有customer表
+----------------+
| Tables_in_test |
+----------------+
| customer       |
+----------------+
1 row in set (0.00 sec)

至此,已經實現了分庫,多個表放在了不一樣的庫中(在上面的栗子中,實現的是多個表放在了不一樣的主機上),但對於經過mycat登陸的客戶來講,仍是一個庫,庫中有四張表。

三、mycat實現分表

這裏對一個新表emp進行拆分。

1)schema.xml文件修改以下(完整配置文件)

[root@mycat mycat]# cat conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

    <schema name="test_db" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        <table name="customer" dataNode="dn2"/>
        <!--只須要在原來的配置文件中增長下面一行便可,表示將emp表分在了dn1和dn2這兩個節點上-->
        <table name="emp" dataNode="dn1,dn2" rule="mod_rule"/>
    </schema>
    <dataNode name="dn1" dataHost="host1" database="test" />
    <dataNode name="dn2" dataHost="host2" database="test" />
    <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
              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.20.2:3306" user="root"
                   password="123.com">
            <!-- can have multi read hosts -->
            <readHost host="hostS2" url="192.168.20.3:3306" user="root" password="123.com" />
        </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.20.21:3306" user="root"
                   password="123.com">
        </writeHost>
    </dataHost>
</mycat:schema>

2)rule.xml文件添加如下內容

[root@mycat mycat]# vim conf/rule.xml      #修改rule.xml文件
        <tableRule name="mod_rule">    <!--這裏指定的name必須和schema.xml中指定的name一致-->
                <rule>
                        <columns>id</columns>    <!--這裏指定的是以哪裏列進行分表的-->
                        <algorithm>mod_long</algorithm>  <!--這裏指定的是分片方法-->
                </rule>
        </tableRule>
                        ...................
                                                <!--下面的name必須和上面的分片方法一致-->
        <function name="mod_long" class="io.mycat.route.function.PartitionByMod">
                <!-- how many data nodes -->
                <property name="count">2</property>    <!--這裏指定的是node節點數量-->
        </function>

3)重啓mycat並登陸到mycat

[root@mycat mycat]# mycat restart

4)登陸到mycat插入數據進行測試分表效果

[root@mycat mycat]# mysql -umycat -ppwd@123 -h 192.168.20.4 -P 8066
mysql> use test_db;
#建立emp表(後端兩個數據庫節點上都會有這張表)
mysql> create table emp (
    -> id int,
    -> name varchar(10)
    -> );
Query OK, 0 rows affected (0.41 sec)
#插入多條數據(對於這種分表,插入數據須要指定列名,不然會報錯)
mysql> insert into emp(id,name) values(1,'張三'),(2,'李四'),(3,'王五'),(4,'趙六');
#查詢新插入的數據
mysql> select * from emp;    
+------+--------+
| id   | name   |
+------+--------+
|    2 | 李四   |
|    4 | 趙六   |
|    1 | 張三   |
|    3 | 王五   |
+------+--------+
4 rows in set (0.04 sec)
mysql> select * from emp order by id;    #對結果進行排序
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
|    4 | 趙六   |
+------+--------+
4 rows in set (0.01 sec)

#後端mysql01主機查詢到該表的數據以下:
mysql> select * from emp;
+------+--------+
| id   | name   |
+------+--------+
|    2 | 李四   |
|    4 | 趙六   |
+------+--------+
2 rows in set (0.00 sec)
#mysql03主機查詢到的數據以下:
mysql> select * from emp;          
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    3 | 王五   |
+------+--------+
2 rows in set (0.00 sec)

能夠看到數據是平均分散在不一樣節點上保存的。

可是通過分片的普通表,是沒法直接和其餘表進行join的。

要想解決多表join的問題,還須要修改schema.xml配置文件,在分表的table字段下添加childTable字段,具體怎麼實現,還在研究中。可是能夠將分表的類型設置爲全局表,這是一種比較簡單的實現方式。

三、全局表

分表後,與其餘表如何join就成了比較棘手的問題,咱們能夠選擇將分表設置爲全局表,這是一個解決join的方法(但不是惟一的方法),但全局表有必定的侷限性,以下:

  • 全局表變更不頻繁;
  • 數據量整體變化不大;
  • 數據規模不大,不多有超數十萬的記錄。

全局表有如下特性:

  • 全局表的插入、更新操做會實時在全部節點上執行,保持各個分片的數據一致性;
  • 全局表的查詢操做,只從一個節點獲取;
  • 全局表能夠跟任何一個表進行 JOIN 操做。

若想建立全局表,只需在table字段增長type="global"便可。以下:

1)定義全局表

[root@mycat conf]# cat schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

    <schema name="test_db" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
        <table name="customer" dataNode="dn2"/>
        <!--在下面的table字段增長global屬性便可-->
        <table name="emp" dataNode="dn1,dn2" type="global">
        </table>
    </schema>
    <dataNode name="dn1" dataHost="host1" database="test" />
    <dataNode name="dn2" dataHost="host2" database="test" />
    <dataHost name="host1" maxCon="1000" minCon="10" balance="3"
              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.20.2:3306" user="root"
                   password="123.com">
            <!-- can have multi read hosts -->
            <readHost host="hostS2" url="192.168.20.3:3306" user="root" password="123.com" />
        </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.20.21:3306" user="root"
                   password="123.com">
        </writeHost>
    </dataHost>
</mycat:schema>

2)驗證全局表

mysql> drop table emp;    #須要刪除以前建立的emp表
Query OK, 0 rows affected (0.03 sec)
#從新建立emp表
mysql> create table emp(
    -> id int,
    -> name varchar(10)
    -> );
Query OK, 0 rows affected (0.38 sec)
#插入數據測試
mysql>  insert into emp(id,name) values(1,'張三'),(2,'李四'),(3,'王五'),(4,'趙六');
#在mycat上查詢插入的數據以下:
mysql> select * from emp;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
|    4 | 趙六   |
+------+--------+
4 rows in set (0.02 sec)
#在後端主機上分別查看emp表中的數據:
#mysql01主機上數據以下:
mysql> select * from emp;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
|    4 | 趙六   |
+------+--------+
4 rows in set (0.00 sec)
#mysql03主機上數據以下:
mysql> select * from emp;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 張三   |
|    2 | 李四   |
|    3 | 王五   |
|    4 | 趙六   |
+------+--------+
4 rows in set (0.00 sec)
相關文章
相關標籤/搜索