在笔者的《在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
最后提醒一下,是添加,不是覆盖。
这一部分我们将介绍在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主要是接收网站后端操作请求,再去操作各个数据库服务器中的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
。
<mycat:schema xmlns:mycat="http://org.opencloudb/">
<schema name="JamesMycatSchema" checkSQLschema="false" sqlMaxLimit="100">
<table name="employee" primaryKey="ID" dataNode="dn2,dn3" rule="sharding-by-intfile" />
schema>
<dataNode name="dn2" dataHost="node2" database="db2" />
<dataNode name="dn3" dataHost="node3" database="db3" />
<dataHost name="node2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()heartbeat>
<writeHost host="hostM2" url="192.168.204.122:3306" user="root" password="root">writeHost>
dataHost>
<dataHost name="node3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()heartbeat>
<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
的值是上面配置的逻辑数据库的名称。
<mycat:server xmlns:mycat="http://org.opencloudb/">
<system>
<property name="defaultSqlParser">druidparserproperty>
system>
<user name="mycat">
<property name="password">123456property>
<property name="schemas">JamesMycatSchemaproperty>
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数据库表中的数据:
之后的数据操作,只要对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
开放其他端口也是同样的操作。