前言¶
在筆者的《在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.xml,server.xml,rule.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的配置文件指定是數據表的字段id,id在0到500萬的時候,數據存儲在第一個數據庫中,id在500萬到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
開放其他端口也是同樣的操作。
參考資料¶
- https://blog.csdn.net/y534560449/article/details/54095574
- https://blog.csdn.net/peppapiggit/article/details/54173486
- https://www.cnblogs.com/wanghuaijun/p/6859593.html
- https://blog.csdn.net/yinwenjie/article/details/53317948
- http://dl.mycat.io/