Mycat工作原理
- 拦截用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:
- 如分片分析、路由分析、读写分离分析、缓存分析等
- 然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
- 步骤
- mycat启动时,就生成了管理连接后端真实数据库的所有datasource
- 客户端系统只能连接mycat发送sql语句
- mycat接收到sql语句,执行各种计算,计算分片sql语句对应数据在哪个分片,计算读写分离,从这个分片主从结构中到哪个节点读/写,拿到了具体的数据源,获取连接发送sql语句
- 数据库拿到mycat执行的sql开始执行
- mycat将数据库返回结果返回给客户端.
Mycat特点
-
读写分离:
- 读写分离是建立在主从结构之上,让主节点去承载写操作,从节点承载读操作,这样做的目的就是可以分担主节点的压力,提升主从结构的整体效率。
-
垂直拆分
- 简单来说就是mycat中的表(不同的表)可以来自于多个不同的服务器。
-
水平拆分
- mycat中的表(一张表)来自于不同的服务器。
Mycat+Mysql节点关系说明
- 三台服务器防火墙先关闭
- systemctl stop firewalld.service
- systemctl status firewalld.service
- 三台服务器都需要安装mysql
mysql-master(192.168.174.136)
mysql-slave(192.168.174.137)
mycat(192.168.174.138)
Mysql安装
-
mysql 安装请在此文mysql5.7安装中搜索(mysql对应部分)
-
安装完成后添加hui数据库,用于主从和mycat
Mysql配置主从
- whereis my.cnf 查找mysql 配置文件目录
- mater节点添加以下内容
server-id=1 # 定义服务器唯一ID
log-bin=mysql-bin # 启用二进制日志
binlog-do-db=hui # 设置需要主从复制的库
binlog_format=ROW # binlog记录内容的方式,记录被操作的每一行
- slave节点添加以下内容
server-id=2 #从服务器唯一ID
log-bin=mysql-bin # 启用二进制日志
replicate-do-db=hui # 设置需要同步的数据库
binlog_format = ROW # binlog记录内容的方式,记录被操作的每一行
- master节点登陆 建立一个从库复制的授权用户
create user 'slave1'@'192.168.174.137' identified by '123456'; # 创建用户
grant replication slave on *.* to 'slave1'@'192.168.174.137'; # 授权,这里为了方便,授予全部权限
flush privileges; # 刷新权限
- service mysqld restart 重启master节点mysql服务
- 登陆mysql,使用show master status; 查看主机 master 的状态
- 记录下mysql-bin.000002 和 154的值
- slave 节点 service mysqld restart 重启master节点mysql服务
- 登陆后输入一下命令
change master to master_host='192.168.174.136',master_user='slave1',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=154;
#开启同步功能
start slave;
######如果产生了数据无法同步的情况,在从节点上执行以下步骤(标记1)
1. stop slave;
2. 执行change 步骤
-> 'mysql-bin.000002' 和 154两个值要视重新同步时修改,使用show master status;获取
3. 执行start slave;
- show slave status \G;
- 图示部分Slave_IO_Running和Slave_SQL_Running为yes即为成功
- 主从测试 (命令行和navicat都可以)
- 如下为第一次同步时的数据
- 如下为第一次同步时的数据
- 在master的enterprise表中分别修改和插入一条数据
- 刷新从库表
- 在master中插入一张表
- 刷新从库表
- 在从库表中新增的test表添加一个id
- 主库没有改变
注意 - 主从同步可能会有一定的延迟
- 在主库进行表的增删,表内行数据的增删改,从库会自动同步
- 切勿手动操作
- 误操作后可以参考上面(标记1)的方法重新同步
Mycat 安装
mycat(192.168.174.138)
需先安装jdk
- yum install java-1.8.0-openjdk* -y
- mycat-github下载
- https://pan.baidu.com/s/1Xeyno8pRomT__AvmzcvKnQ
- 提取码 hmhc
- 上传至虚拟机并解压 tar -zxvf Mycat-server-1.6-linux.tar.gz
- 配置环境变量
- vim /etc/profile
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.345.b01-1.el7_9.x86_64
export MYCAT_HOME=/home/mycat
export PATH=$PATH:$JAVA_HOME/bin:$MYCAT_HOME/bin
-
source /etc/profile
-
cat /home/mycat/conf/server.xml
- schemas中的名称要与与schema.xml关联(TESTDB)
- schemas中的名称要与与schema.xml关联(TESTDB)
-
server.xml文件里登录mycat的用户名和密码可以任意定义,这个账号和密码是为客户机登录mycat时使用的账号信息
-
逻辑库名(如上面的TESTDB,也就是登录mycat后显示的库名,切换这个库之后,显示的就是代理的真实mysq|数据库的表)要在schema.xml里面也定义,否则会导致mycat服务启动失败!
-
如果定义多个标签,即设置多个连接mycat的用户名和密码,那么就需要在schema.xml文件中定义多个对应的库
-
vi /home/mycat/conf/schema.xml
- 修改为以下内容
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"></schema>
<dataNode name="dn1" dataHost="host1" database="hui" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.174.136:3306" user="root" password="123456">
<readHost host="hostS1" url="192.168.174.137:3306" user="root" password="123456"/>
</writeHost>
</dataHost>
</mycat:schema>
-
writeType 表示写模式
- 0 所有的操作发送到配置的第一个writehost
- 1 随机发送到配置的所有writehost
- 2 不执行写操作
-
switchType 切换的模式
- -1 表示不自动切换
- 1 默认值,表示自动切换
- 2 基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status
- 3 基于MySQL galary cluster的切换机制(适合集群)(1.4.1),心跳语句为show status like ‘wsrep%‘。
-
balance
- 0 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。
- 1 全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
- 2 所有读操作都随机的在writeHost、readhost上分发。
- 3 所有读请求随机的分发到writeHost下的readhost执行,writeHost不负担读压力
-
cd /home/mycat/bin
- ./mycat start
- ./mycat status
-
启动后发现navicat无法连接到mycat
- 连接报错ERROR 2003 (HY000): Can’t connect to MySQL server on ‘192.168.174.138’
- 通过mycat console查看得知以下问题
-
进入conf下的wrapper.conf注释掉以下行,然后重启 mycat restart
- #wrapper.java.additional.3=-XX:MaxPermSize=64M
- #wrapper.java.additional.3=-XX:MaxPermSize=64M
-
如果打开表出现ERROR 3009 (HY000): java.lang.IllegalArgumentException: Invalid DataSource:0
- 原因是没有导入数据库hui
<dataNode name="dn1" dataHost="host1" database="hui" />
- 在mysql-master和mysql-slave中导入就可以了
- 此处是因为博主先部署了mycat,后设置的mysql主从,正常不会遇到这个问题
- 原因是没有导入数据库hui
-
通过navicat操作mycat测试读写分离
- 在TESTDB下新建执行以下操作,添加一条数据
- 在TESTDB下新建执行以下操作,添加一条数据
-
可以看到mysql-master节点执行了操作
- mysql-slave节点自动同步了数据
- TESTDB中也有了
- 测试delete
- DELETE FROM enterprise where id = 5
- DELETE FROM enterprise where id = 5
Java整合mycat (本文使用springboot项目)
- 新建一个boot-mycat项目
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
- 修改配置.yml文件
server:
port: 8080
spring:
main:
allow-bean-definition-overriding: true
application:
name: mycat
# Mycat配置信息
datasource:
url: jdbc:mysql://192.168.174.138:8066/TESTDB?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
# mybatis是依赖5.*的版本,所以相对于8.*的少个cj;
driver-class-name: com.mysql.jdbc.Driver
# MyBatis
mybatis:
type-aliases-package: com.example.bootmycat.dao
mapper-locations: classpath:/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
- 新建一个实体类
/**
* @author jigua
* @version 1.0
* @className Enterprise
* @description
* @create 2022/9/27 16:14
*/
public class Enterprise {
private Long id;
private String phone;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 新建一个dao
import com.example.bootmycat.pojo.Enterprise;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author jigua
* @version 1.0
* @className TestDao
* @description
* @create 2022/9/27 16:13
*/
@Mapper
public interface TestDao {
@Select("select * from enterprise")
List<Enterprise> selectAll();
@Insert("insert into enterprise(phone,name) values(#{phone},#{name})")
int add(Enterprise enterprise);
}
- 新建一个controller
- 示例提供一个查询和一个插入方法
import com.example.bootmycat.dao.TestDao;
import com.example.bootmycat.pojo.Enterprise;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
/**
* @author jigua
* @version 1.0
* @className TestController
* @description
* @create 2022/9/27 16:15
*/
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestDao testDao;
@GetMapping("/selectAll")
@ResponseBody
public List<Enterprise> selectAll() {
List<Enterprise> enterprises = testDao.selectAll();
return enterprises;
}
@GetMapping("/insert")
@ResponseBody
public String insert() {
Enterprise enterprise = new Enterprise();
String name = UUID.randomUUID().toString().replaceAll("-", "");
enterprise.setName(name);
enterprise.setPhone(name.substring(0, 3));
int i = testDao.add(enterprise);
if (i > 0) {
return "success";
}
return "fail";
}
}
测试插入
- 可以发现master-slave-TESTDB中都新增了一条
测试查询
- 成功获取数据
注意:如果此时master节点挂掉,slave也不可用
keepalived实现Mycat高可用
--------------------最近忙,过阵子更------------------------------