前言

在筆者的《在CentOS上使用Nginx和Tomcat搭建高可用高併發網站》這篇文章中,筆者介紹瞭如何在CentOS上搭建一個可支持高可用高併發的Java web後端服務器。善於思考的讀者可能會想到,在上一篇文章中,我們只是實現Java web服務器的分佈式來應對高併發,但是高併發對數據庫的的負擔也是很重的。在上一篇文章中,我們只是使用到一個MySQL服務器,但是但數據量非常大的時候,比如有一千萬的用戶,如果只有單個數據庫存儲,那一張用戶表就有一千萬條數據。龐大的數據量使得我們對數據進行查詢的時候非常慢,但出現高併發的時候,大量的查詢請求發送到數據庫服務器,而數據庫來不及響應,隨時可能出現數據庫崩潰的情況。

面對這個問題,我們使用Mycat來實現分佈式數據庫,假設我們有兩個數據庫服務器,那麼一千萬條的數據分開來存儲,這樣每個數據庫只有五百萬條數據,可以大大提高查詢速度。如果有更多的數據庫服務器,那麼每個數據庫所需要存儲的數據就更少了,查詢速度就會更快。基於這一個問題,我們就來學習如何在CentOS下安裝和使用Mycat實現分佈式數據庫。

分佈式數據庫的整體架構:

總體架構

我們使用3個裝有CentOS系統機器,這三機器都是在虛擬機上創建的,如果不知道如何安裝虛擬機上創建CentOS,可以參考筆者的上一篇文章《在CentOS上使用Nginx和Tomcat搭建高可用高併發網站》來安裝這三個虛擬機。下面的這張表就是三個機器的信息和負責的任務。

主機名 IP地址 數據庫名稱 任務角色
node1 192.168.204.121 Mycat
node2 192.168.204.122 db2 MySQL
node3 192.168.204.123 db3 MySQL

前提工作

在開始安裝之前,我們還要設置一下CentOS的hosts文件,添加我們的IP地址和主機名。三個機器都要設置,主要是node1,如果node1沒有設置,Mycat會報錯,會報node1: 域名解析暫時失敗的錯誤。如果讀者在安裝的時候沒有修改過主機名,或者沒有在/etc/sysconfig/network下修改過主機名,那可以不用做以下的操作。

設置的文件是/etc/hosts,可以使用以下的命令編寫:

vim /etc/hosts

node1機器上添加以下信息:

192.168.204.121    node1

同理,node2和node3添加以下信息:

192.168.204.122    node2
192.168.204.123    node3

最後提醒一下,是添加,不是覆蓋。

安裝MySQL數據庫

這一部分我們將介紹在node2和node3機器上安裝MySQL數據庫。

首先關閉防火牆,方便之後的操作。

service iptables stop

我們可以使用以下命令查看MySQL是否安裝了:

rpm -qa | grep mysql

應該會輸出一下日誌:

[root@localhost ~]# rpm -qa | grep mysql
mysql-libs-5.1.71-1.el6.x86_64

然後我們可以先移除這個MySQL,重新安裝一個:

yum -y remove mysql-libs-5.1.71-1.el6.x86_64

移除之前的MySQL之後,可以重新安裝MySQL:

yum -y install mysql-server mysql mysql-devel

最後再查看安裝情況:

rpm -qa | grep mysql

正常的應該會輸出以下信息:

mysql-5.1.73-8.el6_8.x86_64
mysql-libs-5.1.73-8.el6_8.x86_64
mysql-server-5.1.73-8.el6_8.x86_64
mysql-devel-5.1.73-8.el6_8.x86_64

安裝完成之後,我們可以對MySQL數據庫進行一些配置:

vim /etc/my.cnf

主要也是在[mysqld]下加上下面兩個代碼,主要是設置編碼方式和不區分字母大小寫。

default-character-set=utf8
lower_case_table_names=1

修改之後的配置文件如下:

[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

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

配置完成之後,我們直接啓動MySQL:

service mysqld start

然後我們可以把MySQL服務添加到開機自動啓,這樣就不用每次都啓動了。

chkconfig mysqld on

我們可以使用以下的命令查看是否成功添加到開機服務中了。

chkconfig --list | grep mysqld

設置MySQL數據庫的密碼:

mysqladmin -u root password 'root'

登錄數據庫,輸入該命令之後還有輸入數據庫的密碼,這個密碼就是上面設置的root:

mysql -u root -p

爲了讓Mycat可以連接MySQL數據庫,我們還要設置數據庫支持遠程連接,在登錄數據庫之後輸入以下兩條命令:

mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;

可以使用以下的命令查看所有的數據庫:

mysql> show databases;

使用以下的命令可以進入mysql數據庫中。

mysql> use mysql;

使用以下命令可以查看該數據庫有多少張表。

mysql> show tables;

使用以下的查詢SQL語句可以查看用戶名和對應支持訪問的IP地址,因爲我們在上面已經設置root用戶可以支持遠程登錄了,所以可以看到有一個root用的host%

mysql> select user,host from user;

我們看到有一些host是空用戶的,我們可以使用以下的SQL語句刪除這個數據。

mysql> delete from user where user="";

再來查詢一下,可以看到已經沒有了空用戶的信息了。

mysql> select user,host from user;

然後我們創建一個數據庫,node2創建數據庫db2,node3創建數據庫db3。

mysql> create database db2;
mysql> create database db3;

最後還是使用這個命令可以查看到剛纔創建的數據庫,node2是db2,node3是db3。

mysql> show databases;

然後我們使用這兩個數據中都創建一張表,首先是要進行到這個數據庫。node2的是db2,node3的是db3。

mysql> use db2;
mysql> use db3;

最後在這兩個數據庫中都創建一張employee表,結構需要一致,否則會出錯的。這個兩張是真實表,爲什麼叫真實表,因爲在Mycat中還有一個邏輯表,Mycat中的邏輯表就是指向這兩張真實表的。

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

其中字段sharding_id是爲了作分片存儲使用的,下面會介紹到。

到這裏MySQL數據庫的安裝和配置就完成了,接下來就是Mycat的安裝和配置了。

安裝和配置Mycat

在這一部分中,將會介紹Mycat的安裝和配置。Mycat主要是接收網站後端操作請求,再去操作各個數據庫服務器中的MySQL數據庫。接下來的操作在node1下完成。

在操作之前,首先關閉防火牆,方便之後的操作。

service iptables stop

Mycat是一個是免安裝的的,首先我們下載Mycat的壓縮包,我們的安裝路徑是/opt/sxt/soft/

cd /opt/sxt/soft/
wget http://dl.mycat.io/Mycat-server-1.4-beta-20150604171601-linux.tar.gz

然後解壓Mycat壓縮包,會得到一個mycat的文件夾。

tar -zxvf Mycat-server-1.4-beta-20150604171601-linux.tar.gz

Mycat解壓就可以使用了,但是我爲了方便我們操作Mycat,我們在配置文件上添加環境變量。

vim /etc/profile

添加以下的信息,前兩條是添加Mycat的環境變量,第三條是設置開機啓動。

export MYCAT_HOME=/opt/sxt/soft/mycat
PATH=$PATH:$MYCAT_HOME/bin

sh $MYCAT_HOME/bin/mycat start

然後爲了能夠讓Mycat正常工作,還要對Mycat進行一些配置,主要配置的文件有schema.xmlserver.xmlrule.xml這個三個文件,其中rule.xml主要是配置規則的,我們直接使用默認的配置文件就行了。所以只配置另外兩個配置文件,爲了方便以後的使用,我們備份原來的配置文件。

cp $MYCAT_HOME/conf/schema.xml $MYCAT_HOME/conf/schema.xml.tmp
cp $MYCAT_HOME/conf/server.xml $MYCAT_HOME/conf/server.xml.tmp
cp $MYCAT_HOME/conf/rule.xml $MYCAT_HOME/conf/rule.xml.tmp

首先配置schema.xml,這個配置文件主要是設置各個服務器的數據庫和對應的表。

vim $MYCAT_HOME/conf/schema.xml

我們清空之前的配置信息,加入下面的的配置信息。清空的快捷鍵是:把光標移到第一行,在命令狀態下輸入:.,$d即可,或者是在命令狀態下輸入dd快速刪除一行。這個配置文件是創建邏輯數據庫,邏輯數據庫中包含邏輯數據表,可以指定這張邏輯數據表在那個真實數據庫。通過url訪問的真實數據庫並找到對應的數據庫,如db2和db3。同時還指定插入數據的分片規則是sharding-by-intfile

<?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="db2" />
    <dataNode name="dn3" dataHost="node3" database="db3" />

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

        <!--mysql數據庫的連接串 -->
        <writeHost host="hostM2" url="192.168.204.122:3306" user="root" password="root"></writeHost>
    </dataHost>

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

        <!--mysql數據庫的連接串 -->
        <writeHost host="hostM3" url="192.168.204.123:3306" user="root" password="root"></writeHost>
    </dataHost>

</mycat:schema>

然後是配置server.xml,這個配置文件主要是設置連接Mycat的賬號和密碼,同時還指定剛纔配置的邏輯數據庫。

vim $MYCAT_HOME/conf/server.xml

同樣我們清空之前的配置信息,加入下面的的配置信息。其中schemas的值是上面配置的邏輯數據庫的名稱。

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

如果有多個數據庫服務器,還要配置插入數據的分片ID,在插入數據的時候,會根據這個ID進行分佈存儲。我們之所以使用這個分片規則,這個是因爲我們在配置schema.xml的時候指定的分片規則是sharding-by-intfile,這個規則我們可以在rule.xml中找到。如下圖:

這個規則就是根據表中的sharding_id字段分片存儲到不同的真實數據表中。這個ID在下面的文件中配置。

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

比如我們有三個數據庫,如果數據中的字段sharding_id的值是10000就存在第一個數據表中,如果是10010就存儲在第二個數據表中。多個數據庫的話,就以此類推。

10000=0
10010=1

除了sharding-by-intfile規則之外,還有很多種分片存儲的規則,比如還有一種比較常用的auto-sharding-long。我們可以看看這個規則:

auto-sharding-long這個規則同樣有一個配置文件指定分片方式,我們可以以下命令查看:

vim $MYCAT_HOME/conf/autopartition-long.txt

該配置文件的內容如下,根據rule.xml的配置文件指定是數據表的字段idid0到500萬的時候,數據存儲在第一個數據庫中,id500萬到1000萬的時候,數據存儲在第二個數據庫中,依次類推。

# range start-end ,data node index 
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2

配置完成之後就可以啓動Mycat了。

mycat start

啓動之後可以使用以下命令查看啓動輸出的日誌。

tail -f $MYCAT_HOME/logs/wrapper.log

正常情況下是輸出以下的日誌信息,如果輸出錯誤信息,可以根據錯誤信息定位錯誤的位置再進行修改。

[root@node1 mycat]# tail -f $MYCAT_HOME/logs/wrapper.log
INFO   | jvm 1    | 2018/06/30 00:15:12 |   ... 7 more
STATUS | wrapper  | 2018/06/30 00:15:14 | <-- Wrapper Stopped
STATUS | wrapper  | 2018/06/30 00:17:42 | --> Wrapper Started as Daemon
STATUS | wrapper  | 2018/06/30 00:17:42 | Launching a JVM...
INFO   | jvm 1    | 2018/06/30 00:17:42 | Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=64M; support was removed in 8.0
INFO   | jvm 1    | 2018/06/30 00:17:44 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
INFO   | jvm 1    | 2018/06/30 00:17:44 |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
INFO   | jvm 1    | 2018/06/30 00:17:44 | 
INFO   | jvm 1    | 2018/06/30 00:17:45 | log4j 2018-06-30 00:17:45 [./conf/log4j.xml] load completed.
INFO   | jvm 1    | 2018/06/30 00:17:46 | MyCAT Server startup successfully. see logs in logs/mycat.log

使用以下命令可以查看mycat輸出的日誌信息。

tail -f $MYCAT_HOME/logs/mycat.log

正常情況下是輸出以下信息,如果出現連接數據庫不成功的日誌,就要查看連接數據庫的URL、賬號、密碼是否正常,又或者是否關閉了防火牆。

[root@node1 mycat]# tail -f $MYCAT_HOME/logs/mycat.log 
06/30 00:17:46.029   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=13, lastTime=1530289066029, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=989, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.042   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=14, lastTime=1530289066042, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=990, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.055   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=15, lastTime=1530289066055, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=991, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.093   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=16, lastTime=1530289066093, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=992, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.142   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=17, lastTime=1530289066131, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=993, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.144   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=18, lastTime=1530289066144, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=994, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.149   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=19, lastTime=1530289066149, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=995, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.160   INFO [$_NIOREACTOR-0-RW] (GetConnectionHandler.java:66) -connected successfuly MySQLConnection [id=20, lastTime=1530289066160, schema=db3, old shema=db3, borrowed=true, fromSlaveDB=false, threadId=996, charset=utf8, txIsolation=0, autocommit=true, attachment=null, respHandler=null, host=192.168.204.123, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
06/30 00:17:46.215   INFO [WrapperSimpleAppMain] (PhysicalDBPool.java:301) -init result :finished 10 success 10 target count:10
06/30 00:17:46.215   INFO [WrapperSimpleAppMain] (PhysicalDBPool.java:243) -node3 index:0 init success

Mycat啓動完成之後,我們就開始使用連接工具來操作Mycat,筆者使用的是NavicatForMySQL,讀者可以根據自己習慣的連接工具來連接各個數據庫和Mycat。

我們可以使用NavicatForMySQL來連接Mycat,Mycat的默認端口是8066。

我們也可以使用NavicatForMySQL連接MySQL數據庫,以下就是我們連接node2和node3的MySQL數據庫。

連接Mycat之後,可以直接在Mycat上操作數據表。比如我們要插入以下的數據,其中sharding_id就是我們分片存儲的ID。

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

插入數據完成之後,可以在Mycat的邏輯表中看到添加的數據。

然後數據被分片存儲到node2和node3數據庫中,下圖是node2數據庫表中的數據:

下圖是node3數據庫表中的數據:

之後的數據操作,只要對Mycat的邏輯表操作就可以了,操作方式跟操作MySQL數據庫一樣。但是要注意一點的是,創建數據庫和創建數據表都只能在schema.xml上配置,而且Mycat的數據表是邏輯數據表,必須要真實數據庫中有對應的數據表。

好了,關於CentOS下安裝和使用Mycat實現分佈式數據庫就介紹到這裏。路漫漫其修遠兮,吾將上下而求索。

注意

這裏要說一下的是,我們爲了方便外界可以訪問到服務器的端口,我們把防火牆關閉了,但是這種是非常不安全的。所以我們可以單獨開放某一端口,比如我們要開放MySQL數據庫的3306端口號,操作如下:

編輯防火牆配置文件:

vim /etc/sysconfig/iptables

添加以下信息:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT

如下圖所示:

保存退出,最後重啓防火牆:

service iptables restart

開放其他端口也是同樣的操作。

參考資料

  1. https://blog.csdn.net/y534560449/article/details/54095574
  2. https://blog.csdn.net/peppapiggit/article/details/54173486
  3. https://www.cnblogs.com/wanghuaijun/p/6859593.html
  4. https://blog.csdn.net/yinwenjie/article/details/53317948
  5. http://dl.mycat.io/
小夜