利用MyCAT实现MySQL的读写分离

Posted y_zilong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用MyCAT实现MySQL的读写分离相关的知识,希望对你有一定的参考价值。

环境准备:

服务器共三台

所在主机的系统环境    CentOS Linux release 8.3.2011

mycat-server     10.0.0.10    #内存建议2G以上

mysql-master    10.0.0.20     mysql 8.0

mysql-slave       10.0.0.30    mysql 8.0

关闭selinux和firewalld,时间同步

systemctl stop firewalld

setenforce 0

ntpdate ntp.aliyun.com


1、创建MySQL主从数据库(详细步骤可以参考主从复制的那篇文章)

[root@mysql-master ~]# yum -y install mysql-server

[root@mysql-slave ~]# yum -y install mysql-server

#修改master和slave上的配置文件
[root@mysql-master ~]# vi /etc/my.cnf
[mysqld]
server-id=20
log-bin
[root@mysql-master ~]# systemctl enable --now mysqld

[root@mysql-slave ~]# vi /etc/my.cnf
[mysqld]
server-id=30
[root@mysql-slave ~]# systemctl enable --now mysqld

#master授权一个账号给从、可以从主复制数据
mysql> create user yzil@'%' identified by 'redhat';
mysql> grant replication slave on *.* to yzil@'%';

mysql> show master status;
 mysql-master-bin.000001 |      659 |              

#slave使用有复制权限的用户账号连接至主服务器,并启动复制线程
mysql> CHANGE MASTER TO
    ->   MASTER_HOST='10.0.0.20',
    ->   MASTER_USER='yzil',
    ->   MASTER_PASSWORD='redhat',
    ->   MASTER_PORT=3306,
    ->   MASTER_LOG_FILE='mysql-master-bin.000001',
    ->   MASTER_LOG_POS=659;

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> show slave status\\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.0.0.20
                  Master_User: yzil
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-master-bin.000001
          Read_Master_Log_Pos: 659
               Relay_Log_File: mysql-slave-relay-bin.000002
                Relay_Log_Pos: 331
        Relay_Master_Log_File: mysql-master-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
        Seconds_Behind_Master: 0

mysql> 

2、在MySQL代理服务器10.0.0.10安装mycat并启动

[root@mycat-server ~]# yum -y install java
#确认安装成功
[root@mycat-server ~]# java -version
openjdk version "1.8.0_292"
OpenJDK Runtime Environment (build 1.8.0_292-b10)
OpenJDK 64-Bit Server VM (build 25.292-b10, mixed mode)

#下载并安装
[root@mycat-server ~]# wget http://dl.mycat.org.cn/1.6.7.6/20210303094759/Mycat-server-1.6.7.6-release-20210303094759-linux.tar.gz

[root@mycat-server ~]# mkdir /apps

[root@mycat-server ~]# tar xvf Mycat-server-1.6.7.6-release-20210303094759-linux.tar.gz -C /apps/

#配置环境变量
[root@mycat-server apps]# echo 'PATH=/apps/mycat/bin:$PATH' > /etc/profile.d/mycat.sh
[root@mycat-server apps]# source /etc/profile.d/mycat.sh 

#查看端口
[root@mycat-server apps]# ss -ntl
State              Recv-Q             Send-Q                         Local Address:Port                         Peer Address:Port             
LISTEN             0                  128                                  0.0.0.0:22                                0.0.0.0:*                
LISTEN             0                  100                                127.0.0.1:25                                0.0.0.0:*                
LISTEN             0                  128                                     [::]:22                                   [::]:*                
LISTEN             0                  100                                    [::1]:25                                   [::]:*                

#启动mycat
[root@mycat-server ~]# cd /apps/mycat/bin/
[root@mycat-server bin]# mycat 
Usage: /apps/mycat/bin/mycat { console | start | stop | restart | status | dump }

#注意:此步启动较慢,需要等一会儿,另外如果内存太小,会导致无法启动
[root@mycat-server bin]# mycat start
Starting Mycat-server...

#可以看到打开多个端口,其中8066端口用于连接mycat
[root@mycat-server bin]# ss -ntlp
State        Recv-Q       Send-Q             Local Address:Port              Peer Address:Port                                                
LISTEN       0            128                      0.0.0.0:22                     0.0.0.0:*           users:(("sshd",pid=971,fd=4))           
LISTEN       0            100                    127.0.0.1:25                     0.0.0.0:*           users:(("master",pid=1328,fd=16))       
LISTEN       0            1                      127.0.0.1:32000                  0.0.0.0:*           users:(("java",pid=26914,fd=4))         
LISTEN       0            50                             *:36691                        *:*           users:(("java",pid=26914,fd=74))        
LISTEN       0            128                         [::]:22                        [::]:*           users:(("sshd",pid=971,fd=6))           
LISTEN       0            100                        [::1]:25                        [::]:*           users:(("master",pid=1328,fd=17))       
LISTEN       0            50                             *:1984                         *:*           users:(("java",pid=26914,fd=73))        
LISTEN       0            128                            *:8066                         *:*           users:(("java",pid=26914,fd=103))       
LISTEN       0            50                             *:38819                        *:*           users:(("java",pid=26914,fd=72))        
LISTEN       0            128                            *:9066                         *:*           users:(("java",pid=26914,fd=99))        
[root@mycat-server bin]# 

#查看日志,确定成功,可能需要一会才能看到成功的提示
[root@mycat-server ~]# tail /apps/mycat/logs/wrapper.log 
STATUS | wrapper  | 2021/05/24 13:12:26 | --> Wrapper Started as Daemon
STATUS | wrapper  | 2021/05/24 13:12:26 | Launching a JVM...
INFO   | jvm 1    | 2021/05/24 13:12:27 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
INFO   | jvm 1    | 2021/05/24 13:12:27 |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
INFO   | jvm 1    | 2021/05/24 13:12:27 | 
INFO   | jvm 1    | 2021/05/24 13:12:35 | MyCAT Server startup successfully. see logs in logs/mycat.log
[root@mycat-server ~]#

用默认密码123456来连接mycat

Centos8:无法连接mycat需要加参数连接

[root@mysql-slave ~]# mysql -uroot -p'123456' -h 10.0.0.10 -P8066
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (HY000): Access denied for user 'root', because password is error 

[root@mysql-master ~]# vi /etc/my.cnf
[client]
default-auth=mysql_native_password
[root@mysql-master ~]# 

[root@mysql-master ~]# mysql -uroot -p'123456' -h 10.0.0.10 -P8066
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \\g.
Your MySQL connection id is 11
Server version: 5.6.29-mycat-1.6.7.6-release-20210303094759 MyCat Server (OpenCloudDB)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.

mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |
+----------+
1 row in set (0.00 sec)

mysql> 

3、定义mycat

mycat分前端和后端

前端,定义给前端APP连接

后端,定义转发至真实mysql server的规则

注意:前端APP连接数据库,只能连它自己的数据库,所以定义都是按库为单位进行定义的,不是整个mysql server

           后端真实库的权限问题,不要设置的太高

在mycat服务器上修改server.xml文件配置Mycat的连接信息,定义前端

[root@mycat-server ~]# vi /apps/mycat/conf/server.xml
                <!--
                        <property name="serverPort">8066</property>
                        <property name="managerPort">9066</property>
                        <property name="idleTimeout">300000</property>
                        <property name="authTimeout">15000</property>
                        <property name="bindIp">0.0.0.0</property>
                        <property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查
                        <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->

        <user name="root" defaultAccount="true">
                <property name="password">123456</property>
                <property name="schemas">TESTDB</property>
#改为下面
#修改8066改为3306,并取消注释信息
                        <property name="serverPort">3306</property>
                        <property name="managerPort">9066</property>
                        <property name="idleTimeout">300000</property>
                        <property name="authTimeout">15000</property>
                        <property name="bindIp">0.0.0.0</property>
                        <property name="dataNodeIdleCheckPeriod">300000</property> 
                        <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> 

或者:修改下面行的8066改为3306复制到独立非注释行
  <property name="serverPort">3306</property>
  <property name="handleDistributedTransactions">0</property>  #将上面行放在此行前面

        <user name="root" defaultAccount="true">               #连接mycat的用户名
                <property name="password">redhat</property>    #连接mycat的密码
                <property name="schemas">TESTDB</property>   #数据库名要和schema.xml相对应

#查看监听端口变为3306
[root@mycat-server bin]# ss -ntlp
State        Recv-Q       Send-Q             Local Address:Port              Peer Address:Port                                                
LISTEN       0            128                      0.0.0.0:22                     0.0.0.0:*           users:(("sshd",pid=971,fd=4))           
LISTEN       0            100                    127.0.0.1:25                     0.0.0.0:*           users:(("master",pid=1328,fd=16))       
LISTEN       0            1                      127.0.0.1:32000                  0.0.0.0:*           users:(("java",pid=27529,fd=4))         
LISTEN       0            128                         [::]:22                        [::]:*           users:(("sshd",pid=971,fd=6))           
LISTEN       0            50                             *:34327                        *:*           users:(("java",pid=27529,fd=72))        
LISTEN       0            100                        [::1]:25                        [::]:*           users:(("master",pid=1328,fd=17))       
LISTEN       0            50                             *:1984                         *:*           users:(("java",pid=27529,fd=73))        
LISTEN       0            50                             *:44771                        *:*           users:(("java",pid=27529,fd=74))        
LISTEN       0            128                            *:3306                         *:*           users:(("java",pid=27529,fd=103))       
LISTEN       0            128                            *:9066                         *:*           users:(("java",pid=27529,fd=99))        
[root@mycat-server bin]# 

这里使用的是root,密码是redhat,逻辑数据库为TESTDB,可以定义多个TESTDB和TESTDB2,这些信息都可以自己随意定义,读写权限都有,没有针对表做任何特殊的权限。


4、修改schema.xml实现读写分离策略,定义后端

[root@mycat-server ~]# vi /apps/mycat/conf/schema.xml

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

#定义schema,也就是后端虚拟库,并指定对应的虚拟节点
#当前端访问TESTDB这个虚拟库时,会转发到dn1这个节点
#建议虚拟库名和真实库名一致
        <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema>

#定义虚拟节点主机信息,及对应的真实库名称
#当前端访问请求到dn1,会转发到localhost1这个主机的db1库
#注意:db1指的是后端真实的库名
        <dataNode name="dn1" dataHost="localhost1" database="hellodb" />

#定义localhost1这个主机对应的真实后端,并指定路由规则
        <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">

#心跳检测,检测后端真实server的可用性,使用select user()语句去检测
                <heartbeat>select user()</heartbeat>

#指定主要的写主机与读主机
#注意,账号密码,最好重新创建一个,尽量不要使用root
#注意,写,不能同时写,会有锁等待问题,一次写操作,只能在一个写节点进行写,读可以多个
                <writeHost host="host1" url="10.0.0.20:3306" user="root"
                                   password="123456">
                <readHost host="host1" url="10.0.0.30:3306" user="root"
                                   password="123456" />
                </writeHost>

#定义的虚拟节点对应的真实节点的路由规则
        </dataHost> 
</mycat:schema>

#重新启动mycat
[root@mycat-server ~]# cd /apps/mycat/bin/
[root@mycat-server bin]# mycat restart
[root@mycat-server bin]# ss -ntlp
State        Recv-Q       Send-Q             Local Address:Port              Peer Address:Port                                                
LISTEN       0            128                      0.0.0.0:22                     0.0.0.0:*           users:(("sshd",pid=963,fd=4))           
LISTEN       0            100                    127.0.0.1:25                     0.0.0.0:*           users:(("master",pid=1460,fd=16))       
LISTEN       0            1                      127.0.0.1:32000                  0.0.0.0:*           users:(("java",pid=7156,fd=4))          
LISTEN       0            128                            *:3306                         *:*           users:(("java",pid=7156,fd=103))        
LISTEN       0            128                            *:9066                         *:*           users:(("java",pid=7156,fd=99))         
LISTEN       0            50                             *:38101                        *:*           users:(("java",pid=7156,fd=72))         
LISTEN       0            128                         [::]:22                        [::]:*           users:(("sshd",pid=963,fd=6))           
LISTEN       0            100                        [::1]:25                        [::]:*           users:(("master",pid=1460,fd=17))       
LISTEN       0            50                             *:40957                        *:*           users:(("java",pid=7156,fd=74))         
LISTEN       0            50                             *:1984                         *:*           users:(("java",pid=7156,fd=73))         
[root@mycat-server bin]# 

balance属性

balance="0",不开启读写分离机制,所有读操作都发送到当前可用的writeHost上

balance="1",  定义的全部主机都参与select语句的负载均衡,也就是说,当双主双从模式下,M2,S1,S2都参与select语句的负载均衡

balance="2",  所有读请求随机的分发到writeHost,readHost上分布

balance="3",  所有读请求随机的分发到writeHost对应的readHost执行,writeHost不负担读压力,注意,balance=3,只有在1.4及以后的版本才有

writeType属性

writeType="0",所有写操作发送到配置的第一个writeHost,第一个挂了,切换到定义的第二个writeHost,这时,即使重启服务,也不会切换回去,还是以第二个writeHost为准

writeType="1", 所有写操作会随机发送到writeHost。注意,1.5版本以后废弃了,不推荐使用

 

上面配置中,balance改为1,表示读写分离,writeType="0",表示写操作发送到配置的第一个writeHost,第一个挂了,切换到定义的第二个writeHost

以上配置达到的效果就是10.0.0.20为主库,10.0.0.30为从库

注意:要保证能使用root/123456权限成功登录10.0.0.20和10.0.0.30机器上面的mysql数据库。同时也一定要授权mycat机器能使用root/123456权限成功登录这两台机器的mysql数据库!

 

5、在后端主服务器创建用户并对mycat授权

mysql> create database mycat;
mysql> create user root@'10.0.0.%' identified by '123456';
mysql> grant all on *.* to 'root'@'10.0.0.%';
mysql> fulsh privileges;

6、在mycat服务器上连接并测试

[root@mycat-server ~]# yum install -y mysql
[root@mycat-server ~]# vi /etc/my.cnf
[client]
default-auth=mysql_native_password
[root@mycat-server ~]# mysql -uroot -predhat -h 127.0.0.1 TESTDB
mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |          #只能看到一个虚拟数据库
+----------+
1 row in set (0.00 sec)

mysql> use TESTDB;
Database changed
mysql> create table t1(id int);
Query OK, 0 rows affected (0.31 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          30 |
+-------------+
1 row in set (0.02 sec)

mysql> select @@hostname;
+----------------------+
| @@hostname           |
+----------------------+
| mysql-slave.yzil.xyz |
+----------------------+
1 row in set (0.01 sec)

mysql> 

7、通过通用日志确认实现读写分离

mysql> show variables like 'general_log';    #查看日志是否开启
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+
1 row in set (0.01 sec)
  
mysql> set global general_log=on;              #开启日志功能
Query OK, 0 rows affected (0.00 sec) 

mysql> show variables like 'general_log_file';     #查看日志文件保存位置
+------------------+--------------------------------+
| Variable_name    | Value                          |
+------------------+--------------------------------+
| general_log_file | /var/lib/mysql/mysql-slave.log |
+------------------+--------------------------------+
1 row in set (0.00 sec)

mysql> 

在主和从服务器分别启用通用日志,查看读写分离

[root@mysql-master ~]# vi /etc/my.cnf

[mysqld]
general_log=ON

[root@mysql-master ~]# systemctl restart mysqld

mysql> show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | ON    |
+---------------+-------+
1 row in set (0.13 sec)

mysql> 
mysql> use TESTDB;
Database changed
mysql> show tables;
+-------------------+
| Tables_in_hellodb |
+-------------------+
| classes           |
| coc               |
| courses           |
| scores            |
| students          |
| t1                |
| teachers          |
| toc               |
+-------------------+
8 rows in set (0.01 sec)

mysql> 


[root@mysql-master ~]# tail -f /var/lib/mysql/mysql-master.log
2021-05-25T03:05:00.603842Z	   11 Query	select user()   #mycat对后端服务器的健康性检查方法select user()
2021-05-25T03:05:10.603841Z	   13 Query	select user()   
2021-05-25T03:05:20.603842Z	    8 Query	select user()


[root@mysql-slave ~]# tail -f /var/lib/mysql/mysql-slave.log
2021-05-25T03:05:10.603257Z	   13 Query	select user()
2021-05-25T03:05:11.616535Z	   12 Query	show tables
2021-05-25T03:05:20.603478Z	   14 Query	select user()

#从日志中可以观察到读是在slave上面

8、停止从节点,mycat自动调度读请求至主节点

     停止主节点,mycat不会自动调度写请求至从节点

[root@mysql-slave ~]# systemctl stop mysqld

[root@mycat-server ~]# mysql -uroot -predhat -h 127.0.0.1 TESTDB
mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          20 |
+-------------+
1 row in set (0.01 sec)

mysql> 

#停止主节点,mycat不会自动调度写请求至从节点
mysql> insert teachers values (5,'yyy','M');
ERROR 1184 (HY000): java.net.ConnectException: Connection refused

 

以上是关于利用MyCAT实现MySQL的读写分离的主要内容,如果未能解决你的问题,请参考以下文章

mycat实现读写分离

mysql+mycat搭建稳定高可用集群,负载均衡,主备复制,读写分离

mysql+mycat搭建稳定高可用集群,负载均衡,主备复制,读写分离

MySQL+MyCat分库分表 读写分离配置MySQL+MyCat分库分表 读写分离配置

Linux 使用Mycat实现读写分离(基于Mysql的读写分离)

基于Mycat实现读写分离