前言

筆者在《MySQL數據庫實現主從複製》這一篇文章中有提到讀寫分離這個技術,這個技術時基於主從複製之後的一種技術。在數據庫主從複製中,一個主數據庫有一個或者多個從數據庫,我們可以對主數據庫進行寫入操作(insert,delete,update),對一個或者多個從數據庫進行讀取操作(select)。這個的操作方式,就是大量的查詢請求也會分佈到各個從數據庫上,達到負載均衡,比如3個從數據庫,有9條查詢請求,那麼每一個從數據庫只要處理3條查詢請求就可以了,大大減少了數據庫的查詢壓力。

在Mycat中,讀寫分離可以說有兩種,一種是一主一從,另一種是一主多從。我們分別來介紹這兩種方式:

一主一從: 是一個主數據庫一個從數據庫,如果我們對主數據庫進行寫入操作,那麼從數據庫也會執行相應的操作。筆者把一主多從也歸爲這種方式,因爲一主多從其實也就是增加多幾個從數據庫而已,具體的結構每個變動太大。在一主一從中,最大的好處是實現了讀寫分離,在Mycat的調度下,把所有寫的操作都是在主數據庫中,把所有的讀的操作都放在從數據庫中。可能有讀者可能會有疑問,這中讀取分離也可以使用後端代碼實現,爲什麼要使用Mycat呢。主要是使用Mycat配置會更加簡單,更容易維護,當數據庫的位置發生了變化,如果是使用代碼實現的,那麼網站的後端代碼要全部修改了,對於分佈式的後端服務器來說,那工作量是非常大的。再且使用後端代碼實現的讀寫分離也很難實現數據庫的分佈式。而使用Mycat都能解決這些問題。

如圖所示:

雙主雙從: 這個主要是保證數據的安全,保證數據庫的高可用。在這篇文章《MySQL數據庫實現主從複製》中,筆者也介紹到主從複製最大的作用就是保證數據的安全。在一主一從中,我們的從數據庫在數據安全性上並沒有發揮到最好,只是爲了提供讀寫分離和查詢負載均衡。當主數據庫服務器掛掉了,那麼就無法進行寫入數據,整個數據庫就無法正常工作了。所以雙主雙從或者多主多從的出現就是爲了解決這個問題的,在雙主雙從模式下(M1 ->S1 , M2->S2,M1->M2,M2->M1並且 M1 與 M2 互爲主備),這種雙主雙從的模型,只要我們對M1或者M2任意一個主數據庫插入數據,其他3個數據庫也會作相應的改動。因爲M1和M2是互爲主從數據庫,所以兩個數據庫是互相同步的,另外兩個數據庫是他們的從數據庫,寫入操作也會更新從數據庫。當M1掛掉了,會自動啓動M2作爲該數據庫的主數據庫,保證了網站的正確運行。同時在正常情況下,M2,S1,S2 都參與查詢的負載均衡。

如圖所示:

那麼下面就介紹Mycat中間件實現一主一從和雙主雙從的讀寫分離,在閱讀下面教程之前,讀者應該先閱讀筆者之前的兩篇文章,分別是《CentOS下安裝和使用Mycat實現分佈式數據庫》《MySQL數據庫實現主從複製》,在接下來的教程中會運用到這兩篇文章的知識,所以讀者要閱讀並理解這兩篇文件的知識。好,我們現在開始吧。

一主一從讀寫分離

我們一共使用5個虛擬機,每個機器的作用如下:

主機名 IP地址 任務角色 數據庫
node1 192.168.204.121 Mycat
node2 192.168.204.122 master1 MySQL
node3 192.168.204.123 slave1 MySQL
node4 192.168.204.124 master2 MySQL
node5 192.168.204.125 slave2 MySQL

MySQL數據庫配置

首先我們要在node2、node3、node4和node5上安裝MySQL數據庫,安裝方式 可以參考《MySQL數據庫實現主從複製》的安裝MySQL部分,這裏就不展開講了。安裝完成之後要對數據庫做一些配置,如下:
node2的數據庫是node3的數據庫的主數據庫,所以node2數據庫的配置文件/etc/my.cnf如下:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

default-character-set=utf8   # 設置編碼方式
lower_case_table_names=1     # 不區分字母大小寫

log-bin=mysql-bin  # 開啓二進制日誌
server-id=1        # 設置server-id

# 不同步哪些數據庫  
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema

# 只同步哪些數據庫,除此之外,其他不同步  
# binlog-do-db = game  


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node3的數據庫是node2上數據庫的從數據庫,所以不用修改太多。node3數據庫的配置文件/etc/my.cnf如下:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
default-character-set=utf8
lower_case_table_names=1

server-id=2 #設置server-id,必須唯一

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node4的數據庫是node5的數據庫的主數據庫,所以node4數據庫的配置文件/etc/my.cnf如下:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

default-character-set=utf8   # 設置編碼方式
lower_case_table_names=1     # 不區分字母大小寫

log-bin=mysql-bin  # 開啓二進制日誌
server-id=3        # 設置server-id

# 不同步哪些數據庫  
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema

# 只同步哪些數據庫,除此之外,其他不同步  
# binlog-do-db = game  


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node5的數據庫是node4上數據庫的從數據庫,所以不用修改太多。node5數據庫的配置文件/etc/my.cnf如下:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
default-character-set=utf8
lower_case_table_names=1

server-id=4 #設置server-id,必須唯一

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

方便之後的操作,我們關閉了全部虛擬機的防火牆:

service iptables stop

全部都修改了配置文件,所以全部都要重啓MySQL數據庫:

service mysqld restart

我們進入到node2數據庫中,輸入以下命令查看相關信息:

mysql> show master status;

得到的信息如下:

mysql> show master status;
+------------------+----------+--------------+-------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB              |
+------------------+----------+--------------+-------------------------------+
| mysql-bin.000007 |      106 |              | mysql,test,information_schema |
+------------------+----------+--------------+-------------------------------+
1 row in set (0.00 sec)

根據node2這個主數據庫的信息,開始配置node3的從數據庫,進入到node3數據庫 中,輸入以下命令,注意master_log_filemaster_log_pos來自於上一步獲取到的node2主數據庫的信息:

mysql> change master to master_host='192.168.204.122', master_user='root', master_password='root', master_log_file='mysql-bin.000007', master_log_pos=106;

在執行上一條命令之前,要保證slave是關閉的,如果沒用關閉,使用以下命令關閉:

mysql> stop slave;

配置完成之後,要啓動slave,執行下面的命令啓動slave:

mysql> start slave;

其中完成之後使用以下命令查看啓動的情況:

mysql> show slave status\G;

正常情況下輸出以下內容,要保證Slave_IO_RunningSlave_SQL_RunningYes

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.204.122
                  Master_User: root
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000007
          Read_Master_Log_Pos: 106
               Relay_Log_File: mysqld-relay-bin.000002
                Relay_Log_Pos: 251
        Relay_Master_Log_File: mysql-bin.000007
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 106
              Relay_Log_Space: 407
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)

ERROR: 
No query specified

接下來開始配置node4數據庫中,進入到node4數據庫並輸入以下命令查看相關信息:

mysql> show master status;

得到的信息如下:

mysql> show master status;
+------------------+----------+--------------+-------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB              |
+------------------+----------+--------------+-------------------------------+
| mysql-bin.000001 |      106 |              | mysql,test,information_schema |
+------------------+----------+--------------+-------------------------------+
1 row in set (0.00 sec)

根據node4這個主數據庫的信息,開始配置node5的從數據庫,進入到node5數據庫 中,輸入以下命令,注意master_log_filemaster_log_pos來自於上一步獲取到的node4主數據庫的信息:

mysql> change master to master_host='192.168.204.124', master_user='root', master_password='root', master_log_file='mysql-bin.000001', master_log_pos=106;

在執行上一條命令之前,要保證slave是關閉的,如果沒用關閉,使用以下命令關閉:

mysql> stop slave;

配置完成之後,要啓動slave,執行下面的命令啓動slave:

mysql> start slave;

其中完成之後使用以下命令查看啓動的情況:

mysql> show slave status\G;

正常情況下輸出以下內容,要保證Slave_IO_RunningSlave_SQL_RunningYes

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.204.124
                  Master_User: root
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 106
               Relay_Log_File: mysqld-relay-bin.000002
                Relay_Log_Pos: 251
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 106
              Relay_Log_Space: 407
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)

ERROR: 
No query specified

Mycat的配置

關於Mycat的安裝可以參考《CentOS下安裝和使用Mycat實現分佈式數據庫》安裝和配置Mycat,這裏就不再介紹Mycat的安裝了,這一部分主要是介紹Mycat配置讀寫分離。

安裝完成之後,開始配置schema.xml文件,我們的讀寫分離主要是在這裏配置的。

vim $MYCAT_HOME/conf/schema.xml

該配置文件如下,我們創建了兩個數據庫,分別是yeyupiaoling1yeyupiaoling2,主要是分佈式數據庫,數據採用分片存儲。這個兩個數據庫都有同一張employee表,也可以配置多個表。我們的讀寫分離主要是在dataHost裏配置。主要的配置有三個balance="1" writeType="0" switchType="1",它們的作用分別是:

balance 屬性負載均衡類型,目前的取值有 4 種:

  • balance="0",不開啓讀寫分離機制,所有讀操作都發送到當前可用的writeHost 上。
  • balance="1",全部的 readHost 與 stand by writeHost 參與 select 語句的負載均衡,簡單的說,當雙主雙從模式(M1 ->S1 , M2->S2,並且 M1 與 M2 互爲主備),正常情況下, M2,S1,S2 都參與 select 語句的負載均衡。
  • balance="2",所有讀操作都隨機的在 writeHost、 readhost 上分發。
  • balance="3", 所有讀請求隨機的分發到 wiriterHost 對應的 readhost 執行,writerHost 不負擔讀壓力,注意 balance=3 只在 1.4 及其以後版本有, 1.3 沒有。

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

  • writeType="0",所有寫操作發送到配置的第一個 writeHost,第一個掛了切到還生存的第二個writeHost,重新啓動後已切換後的爲準,切換記錄在配置文件中:dnindex.properties .
  • writeType="1",所有寫操作都隨機的發送到配置的 writeHost,1.5 以後廢棄不推薦。
  • writeType="2",官方文檔沒有介紹。

switchType 屬性:

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

下面就是schema.xml文件全部配置信息:

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

    <!-- 設置表的存儲方式.schema name="JamesMycatSchema" 與 server.xml中的 JamesMycatSchema 設置一致  -->
    <schema name="JamesMycatSchema" checkSQLschema="false" sqlMaxLimit="100">
        <table name="employee" primaryKey="ID" dataNode="dn2,dn3" rule="sharding-by-intfile" />
    </schema>

    <!--數據節點dn1,對應的主機c1,對應是數據庫db1 -->
    <dataNode name="dn2" dataHost="node2" database="yeyupiaoling1" />
    <dataNode name="dn3" dataHost="node4" database="yeyupiaoling2" />

    <!-- 主機C2-->
    <dataHost name="node2" maxCon="1000" minCon="10" balance="1" writeType="0" switchType="1"
        dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>

        <!--MySQL的寫的數據,master1 -->
        <writeHost host="hostM2" url="192.168.204.122:3306" user="root" password="root">
            <!--MySQL讀的數據庫,slave1 -->
            <readHost host="hostM3" url="192.168.204.123:3306" user="root" password="root"/>
        </writeHost>
    </dataHost>

    <!-- 主機C3-->
    <dataHost name="node4" maxCon="1000" minCon="10" balance="1" writeType="0" switchType="1"
        dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>

        <!--MySQL的寫的數據,master2 -->
        <writeHost host="hostM4" url="192.168.204.124:3306" user="root" password="root">
            <!--MySQL讀的數據庫,slave2 -->
            <readHost host="hostM5" url="192.168.204.125:3306" user="root" password="root"/>
        </writeHost>
    </dataHost>

</mycat:schema>

然後配置server.xml,這裏主要是配置Mycat的登錄賬戶和密碼的,其中schemas的值要對應schema.xml中的schemaname的值。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
        <system>
                <property name="defaultSqlParser">druidparser</property>
        </system>

        <user name="mycat">
                <property name="password">123456</property>
                <property name="schemas">JamesMycatSchema</property>
        </user>
</mycat:server>

配置完成之後,可以重啓Mycat:

mycat restart

啓動之後,可以分別查看輸出的日誌信息,觀察是否正常啓動了:

tail -f $MYCAT_HOME/logs/wrapper.log
tail -f $MYCAT_HOME/logs/mycat.log

測試

正常其中之後,可以測試整個數據庫是否符合了分佈式切片存儲和讀寫分離。筆者使用的是NavicatForMySQL連接數據庫,讀者可以使用自己熟悉的連接工具,或者是直接在命令行上直接操作。

首先連接node2數據庫並在上面創建一個yeyupiaoling1數據庫:

然後在node2的yeyupiaoling1數據庫上創建一張employee表:

create table employee(id int not null primary key,name varchar(100),sharding_id int not null);

可以在新建查詢上執行這個SQL語句,其中字段sharding_id非常重要,我們選擇的規則就是Mycat是根據這個字段來分片存儲數據庫。

同樣連接node4數據庫並在上面創建一個yeyupiaoling2數據庫:

然後在node4的yeyupiaoling2數據庫上創建一張employee表,同樣的執行方式,就不重複介紹了。

create table employee(id int not null primary key,name varchar(100),sharding_id int not null);

然後連接node1的Mycat,記得Mycat默認的端口是8066,賬號和密碼是server.xml中設置的。連接之後可以看到一個JamesMycatSchema數據庫,這個就是我們在schema.xml配置的,可以看到有一張employee表,我們新建查詢在這裏插入一些數據:

insert into employee(id,name,sharding_id) values(1, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(2, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(3, 'I am db3',10010);
insert into employee(id,name,sharding_id) values(4, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(5, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(6, 'I am db3',10010);
insert into employee(id,name,sharding_id) values(7, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(8, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(9, 'I am db3',10010);
insert into employee(id,name,sharding_id) values(10, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(11, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(12, 'I am db3',10010);
insert into employee(id,name,sharding_id) values(13, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(14, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(15, 'I am db3',10010);
insert into employee(id,name,sharding_id) values(16, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(17, 'I am db2',10010);
insert into employee(id,name,sharding_id) values(18, 'I am db3',10010);

在Mycat中可以可以看到employee表已經插入的全部數據。

然後在node2和node3的數據庫中都可以看到插入的數據庫,根據分片規則存儲方式,這裏是就是這個數據庫存儲的數據。

同樣在node4和node5上也存儲了另一部分的數據。

從上面來看,分佈式存儲和主從複製已經沒有問題了,那麼我們如何支持是不是讀寫分離呢,那麼我們接下來就測試一下是不是讀寫分離。

首先我們把node5數據庫的數據庫全部清空,也就是node4的從數據庫。

node5的數據庫的數據清空了,但node5的主數據庫node4數據庫並沒清空,數據還是存在的。如果在Mycat上查詢不到node4數據庫的數據,那麼就證明在查詢的時候只是查詢的從數據庫的數據,也就是在schema.xmldataHost配置dataHost指定的數據庫。

經過在Mycat上查詢,發現結果如預想的一樣,所以證明了是讀寫分離的。

雙主雙從讀寫分離

在這一部分,我們來實現雙主雙從讀寫分離,主要在上一步的基礎之上做一些修改。在上一部分的一主一從讀寫分離中,M1->S1,M2->S2,但是M1和M2是沒有關係的。而在雙主雙從上M1和M2是互爲主從關係,它們各自是對方的主從數據庫,這樣的話,無論是M1或者M2發生數據變化,其他的數據庫也會發生相應的變化。在開始之前,先清空數庫之前的數據,保證各個數據庫一致。

MySQL數據庫配置

在上一部分的MySQL的配置基礎上,這部分的修改並不多,但爲了讀者更清楚瞭解各個配置文件的內容,筆者將會展示所有的配置信息:

node2(master1)的MySQL配置文件/etc/my.cnf,主要是增加了log-slave-updates,增加這個的作用主要是爲了當node2(master1)的數據庫作爲node4(master2)的從數據庫時,node4(master2)的數據發生更改時,作爲從數據庫的node2(master1)也會發生相應改變,但是如果沒有添加log-slave-updates的話,作爲從數據庫的node2(master1)在跟着node4進行寫入操作時,這個寫入操作node2(master1)並不會添加到bin二進制文件中,所以node2(master1)的從數據庫node3(slave1)就不會跟着改變,這樣的話就node3就不會跟node4(master2)的數據同步。

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

default-character-set=utf8   # 設置編碼方式
lower_case_table_names=1     # 不區分字母大小寫

log-bin=mysql-bin  # 開啓二進制日誌
server-id=1        # 設置server-id
log-slave-updates  # 在作爲從數據庫的時候,有寫入操作也要更新二進制日誌文件

# 不同步哪些數據庫  
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema

# 只同步哪些數據庫,除此之外,其他不同步  
# binlog-do-db = game  


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node3數據庫的配置文件/etc/my.cnf,這個配置不用做修改:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
default-character-set=utf8
lower_case_table_names=1

server-id=2 #設置server-id,必須唯一

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node4的配置跟node2一樣,主要不同的是server-id

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

default-character-set=utf8   # 設置編碼方式
lower_case_table_names=1     # 不區分字母大小寫

log-bin=mysql-bin  # 開啓二進制日誌
server-id=3        # 設置server-id
log-slave-updates  # 在作爲從數據庫的時候,有寫入操作也要更新二進制日誌文件

# 不同步哪些數據庫  
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema

# 只同步哪些數據庫,除此之外,其他不同步  
# binlog-do-db = game  


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node5數據庫的配置文件/etc/my.cnf,也是一樣只要修改server-id

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
default-character-set=utf8
lower_case_table_names=1

server-id=4 #設置server-id,必須唯一

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

node2的MySQL的信息如下:

mysql> show master status;
+------------------+----------+--------------+-------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB              |
+------------------+----------+--------------+-------------------------------+
| mysql-bin.000008 |      106 |              | mysql,test,information_schema |
+------------------+----------+--------------+-------------------------------+
1 row in set (0.00 sec)

配置node2的從數據庫,需要配置的是node3和node4的數據庫,配置如下:

首先要關閉slave:

mysql> stop slave;

配置信息,注意master_log_filemaster_log_pos的值:

mysql> change master to master_host='192.168.204.122', master_user='root', master_password='root', master_log_file='mysql-bin.000008', master_log_pos=106;

然後啓動slave:

mysql> start slave;

node4的MySQL的信息如下:

mysql> show master status;
mysql> show master status;
+------------------+----------+--------------+-------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB              |
+------------------+----------+--------------+-------------------------------+
| mysql-bin.000002 |      106 |              | mysql,test,information_schema |
+------------------+----------+--------------+-------------------------------+
1 row in set (0.00 sec)

配置node4的從數據庫,需要配置的是node2和node5的數據庫,配置如下:

首先要關閉slave:

mysql> stop slave;

配置信息,注意master_log_filemaster_log_pos的值:

mysql> change master to master_host='192.168.204.124', master_user='root', master_password='root', master_log_file='mysql-bin.000002', master_log_pos=106;

然後啓動slave:

mysql> start slave;

配置完成就可以查看各個數據庫的配置信息了,觀察是否配置成功了,以下是node2的配置信息:

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.204.124
                  Master_User: root
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 106
               Relay_Log_File: mysqld-relay-bin.000002
                Relay_Log_Pos: 251
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 106
              Relay_Log_Space: 407
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)

ERROR: 
No query specified

然後我們可以使用NavicatForMySQL連接node2數據庫,我們可以測試只對master1數據庫執行寫操作,其他的數據庫是不是也會做相應的改變。

比如我們在node2的數據庫創建一個yeyupiaoling數據庫:

然後在這個數據庫上創建一張employee表:

create table employee(id int not null primary key,name varchar(100),sharding_id int not null);

然後我們發現其它的數據庫也會做相應的改變:

證明了我們的配置是正確的。

Mycat的配置

我們首先配置schema.xml

vim $MYCAT_HOME/conf/schema.xml

配置信息如下:

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

    <!-- 設置表的存儲方式.schema name="JamesMycatSchema" 與 server.xml中的 JamesMycatSchema 設置一致  -->
    <schema name="JamesMycatSchema" checkSQLschema="false" sqlMaxLimit="100">
        <table name="employee" primaryKey="ID" dataNode="dn2" rule="sharding-by-intfile" />
    </schema>

    <!--數據節點dn1,對應的主機c1,對應是數據庫db1 -->
    <dataNode name="dn2" dataHost="node2" database="yeyupiaoling" />

    <!-- 主機C2-->
    <dataHost name="node2" maxCon="1000" minCon="10" balance="1" writeType="0" switchType="1"
        dbType="mysql" dbDriver="native">
        <heartbeat>select user()</heartbeat>

        <!--MySQL的寫的數據,master1 -->
        <writeHost host="hostM2" url="192.168.204.122:3306" user="root" password="root">
            <!--MySQL讀的數據庫,slave1 -->
            <readHost host="hostM3" url="192.168.204.123:3306" user="root" password="root"/>
        </writeHost>

        <!--MySQL的寫的數據,master2 -->
        <writeHost host="hostM4" url="192.168.204.124:3306" user="root" password="root">
            <!--MySQL讀的數據庫,slave2 -->
            <readHost host="hostM5" url="192.168.204.125:3306" user="root" password="root"/>
        </writeHost>

    </dataHost>

</mycat:schema>

然後配置server.xml

vim $MYCAT_HOME/conf/server.xml

配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
        <system>
                <property name="defaultSqlParser">druidparser</property>
        </system>

        <user name="mycat">
                <property name="password">123456</property>
                <property name="schemas">JamesMycatSchema</property>
        </user>
</mycat:server>

配置partition-hash-int.txt

vim $MYCAT_HOME/conf/partition-hash-int.txt

因爲我們選擇的分片規則是sharding-by-intfile,所以Mycat會根據sharding_id進行分片存儲到不同的數據庫節點的上,因爲我們上面配置的數據庫只是配置了一個數據庫節點,所以這個文件只是配置了一個:

10000=0

配置完成之後,要重新啓動Mycat:

mycat restart

啓動之後,可以分別查看輸出的日誌信息,觀察是否正常啓動了:

tail -f $MYCAT_HOME/logs/wrapper.log
tail -f $MYCAT_HOME/logs/mycat.log

正常啓動啓動之後,測試是否一切正常了。

測試

首先我們連接Mycat,然後在Mycat上對employee表添加數據:

insert into employee(id,name,sharding_id) values(1, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(2, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(3, 'I am db3',10000);
insert into employee(id,name,sharding_id) values(4, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(5, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(6, 'I am db3',10000);
insert into employee(id,name,sharding_id) values(7, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(8, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(9, 'I am db3',10000);
insert into employee(id,name,sharding_id) values(10, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(11, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(12, 'I am db3',10000);
insert into employee(id,name,sharding_id) values(13, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(14, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(15, 'I am db3',10000);
insert into employee(id,name,sharding_id) values(16, 'I am db1',10000);
insert into employee(id,name,sharding_id) values(17, 'I am db2',10000);
insert into employee(id,name,sharding_id) values(18, 'I am db3',10000);

操作如下:

然後我們連接4個數據庫,查看它們的數據,發現是跟預期的一樣,每個數據庫都插入了數據:

讀寫分離在上一部分已經測試過了,這裏主要是測試一下查詢負載均衡,這裏筆者刪除了node5(slave2)的id爲10之後的數據:

node3(slave1)數據庫的刪除id爲10之前的:

node4(master2)的數據庫的數據不做修改:

然後我們在Mycat上不斷進行查詢操作,觀察查詢到的結果,就可以判斷是不是slave1、master2、slave2都查詢。從查詢的結果來看,的確slave1、master2、slave2都查詢。

然後測試高可用作用,我們手動關閉node2(master1)數據庫,看看是不是自動啓用node4(master2)作爲主數據庫了。當我們關閉了node2(master1)的數據庫,發現Mycat還能正常工作,但是無論我們怎麼刷新查詢操作,都只能查詢到一下的數據,這個是node5(slave2)數據庫中的數據。這種情況證明了兩件事情,一是當node2(master)數據庫掛掉了,會自動啓用node4(master2)作爲主數據庫,二是如果主數據庫掛掉了,其的從數據庫也不會參與查詢操作,這樣保證了數據的一致性。

關閉數據庫命令:

service mysqld stop

然後我們在node4數據庫上插入一條數據,主要是觀察當再次啓動node2的數據庫時,會不會保證數據庫的數據一致。

insert into employee(id,name,sharding_id) values(20, 'hello',10000);

然後我們再啓動MySQL數據庫,觀察到在node4添加的數據也被添加到這裏了。

啓動數據庫命令:

service mysqld start

最後有一點要說的是,在真實的項目中,不應該對從數據庫(slave)做寫入操作,這樣會破壞數據的一致性的。上面筆者的操作主要是爲了測試使用的。

參考資料

  1. https://blog.csdn.net/ydyang1126/article/details/70224185
  2. https://www.cnblogs.com/biglittleant/p/7059569.html
  3. https://blog.csdn.net/qq_33200967/article/details/80939040
  4. https://blog.csdn.net/qq_33200967/article/details/80863305
小夜