mycat 实现读写分离

1. mycat简介

mycat是一个开源数据库中间件;它可以管理你的所有数据库,并对他们进行读写分离,分库分表等。

使用起来的话Mycat就是一个近似于MySQL的数据库服务器,你可以用连接MySQL的方式去连接Mycat(除了端口不同,默认的Mycat端口是8066而非MySQL的3306),大多数情况下,可以用你熟悉的对象映射框架比如MyBatis操作Mycat。

它最主要的两个功能就是:

  • 数据库的读写分离
  • 分库分表

2. 下载mycat

mycat的官网:https://github.com/MyCATApache/Mycat-Server

可以从这里去下载,我下载的是1.6版本的

下载后,直接解压

tar  –zxvf  Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz

然后进入bin目录,执行以下命令就可以启动了

./mycat start

MyCat关闭命令:

./mycat stop

登录mycat:用类似mysql的登录方式就可以登录了,初始账号下面会有说明

mysql -uroot -p -P8066 -h127.0.0.1

mycat默认数据访问端口是8066

3. 配置文件

其实进行读写分离 我们只需要配置2个文件就可以使用了,进入conf文件夹:

  • server.xml:系统配置,比如用户,白名单等
  • schema.xml: 配置关联的数据库,在这里面做读写分离,分库分表等操作

3.1 server.xml讲解

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
	<system>
	<property name="useSqlStat">0</property>  <!-- 1为开启实时统计、0为关闭 -->
	<property name="useGlobleTableCheck">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->
	
	<!-- 指定使用Mycat全局序列的类型:即数据库主键的生成方式
		0为本地文件方式,1为数据库方式,2为时间戳序列方式。
		对于读写分离而言,是不需要考虑主键生成方式的,也就是不需要配置全局序列号的。 -->
		<property name="sequnceHandlerType">2</property>
		
		<property name="processorBufferPoolType">0</property>
		<property name="handleDistributedTransactions">0</property>
		<property name="useOffHeapForMerge">1</property>
		<property name="memoryPageSize">1m</property>
		<property name="spillsFileBufferSize">1k</property>
		<property name="useStreamOutput">0</property>
		<property name="systemReserveMemorySize">384m</property>
		<property name="useZKSwitch">true</property>
	</system>
	
	<!-- 全局SQL防火墙设置 -->
	<!-- 
	<firewall> 
	   <whitehost>
	      <host host="127.0.0.1" user="mycat"/>
	      <host host="127.0.0.2" user="mycat"/>
	   </whitehost>
       <blacklist check="false">
       </blacklist>
	</firewall>
	-->
	
	<!-- 设置连接mycat时的用户名和密码, 逻辑库
		逻辑库是指一个虚拟的数据库,通过这个虚拟的数据库去管理多个真实的数据库
		我这里创建了一个用户名为mycat,密码为123456的账号,该用户可以连接到mycatdb这个逻辑数据库
	-->
	<user name="mycat">
		<property name="password">123456</property>
		<property name="schemas">mycatdb</property>
	</user>
	
	<!-- root和user账号都是默认生成的,所以一开始什么都不设置的情况下,可以通过这两个账号登录mycat
		注意修改一下属性为schemas的值,默认是TESTDB,但是我们没有在schema.xml文件中配置这个逻辑数据库,所以启动时就会报错,
		所以我们这里将这两个默认账号的schemas都设置为mycatdb
	-->
	<user name="root">
		<property name="password">123456</property>
		<property name="schemas">mycatdb</property>
		
		<!-- 表级 DML 权限设置 -->
		<!-- 		
		<privileges check="false">
			<schema name="TESTDB" dml="0110" >
				<table name="tb01" dml="0000"></table>
				<table name="tb02" dml="1111"></table>
			</schema>
		</privileges>		
		 -->
	</user>
	
	<user name="user">
		<property name="password">user</property>
		<property name="schemas">mycatdb</property>
		<property name="readOnly">true</property>
	</user>

</mycat:server>

其实在这个文件中,我们只是新增了一个mycat账号,并且把所有用户的逻辑数据库全部改为了mycatdb,接下来我们就要去创建逻辑数据库mycatdb了。

 

3.2 schema.xml解读

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

	<!-- 这里设置逻辑数据库
		name:指定逻辑数据库的名字
		dataNode: 指向下面dataNode的name属性,进行关联-->
	<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
		<!--只做读写分离,不做分库分表,Mycat只是帮我们转发一下请求,读转发到从库,写转发到主库,则schema标签里面不用配置table-->
	</schema>

	<!-- dataNode表示数据分片,可以理解成一个真实数据库在多个msql中的集合
		 database:表示真实数据库,设置为你想进行读写分离的那个数据库,这里我是要对tfp这个数据库进行读写分离
      	 dataHost: 指向下面dataHost的name属性值	 -->
	<dataNode name="dn1" dataHost="localhost1" database="tfp" />
	<!-- 可以添加多个dataNode标签,来对不同数据库进行读写分离 -->
	
	<!-- dataHost表示真实的mysql实例,需要配置真实的mysql实例列表
		 balance:负载均衡类型,目前的取值有4种:
				balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上;
				balance="1",全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
				balance="2",所有读操作都随机的在writeHost、readhost上分发。
				balance="3",所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力。
				推荐balance设置为1
		 switchType:用于指定主服务器发生故障后的切换类型。
				-1 表示不自动切换
				1 默认值,自动切换(推荐)
				2 基于MySQL主从同步的状态决定是否切换
				3 基于MySQL galary cluster的切换机制(适合集群)(1.4.1)
				通常情况下,我们MySQL采用双主双从的模式下,switchType为1即可。因为双主从模式下,主从同步关系很复杂,不能根据MySQL的状态来切换。只需要在一个主出问题后,切换到另外的主。
	-->
	<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
			  
		
		<!-- heartbeat:用于和后端数据库进行心跳检查的语句,检测MySQL数据库是否正常运行。
		当switchType为1时,mysql心跳检查语句是select user()。
		当switchType为2时,mysql心跳检查语句是show slave status。
		当switchType为3时,mysql心跳检查语句是show status like 'wsrep%'。 -->
		<heartbeat>select user()</heartbeat>


		<!--writeHost: 指定写实例(主)
						host属性可以随便写
						user和password表示真实数据库的用户名密码-->
		<writeHost host="hostM3339" url="127.0.0.1:3339" user="root"
				   password="123456">
			<!-- 指定读实例(从) -->
			<readHost host="hostS3340" url="127.0.0.1:3340" user="root" password="123456" />
			<readHost host="hostS3341" url="127.0.0.1:3341" user="root" password="123456" />
		</writeHost>
		
	</dataHost>
</mycat:schema>

 我的数据库格式如下:

 dataNode标签中的database属性指的就是真实tfp数据库。

然后在dataHost中配置,tfp数据库所在的所有真实mysql数据库实例列表,即一主两从的3个数据库实例

4. 启动测试

启动mycat,然后进入客户端进行操作增删改查

mysql -umycat -p -P8066 -h127.0.0.1
show databases;
use mycatdb;
show tables;

当执行show databases;命令时可以看出来,它里面存放的其实就是mycatdb这个逻辑数据库。

这里还有一个问题:

如果进行正常的操作增删改查,其实看不出来到底是不是读写分离了,因为3个数据库的数据始终是一样的。

做法:可以对两个slaver从数据库中的user表中的数据进行略微的修改(不要进行增删操作,否则可能会出问题),比如修改一条记录的值。最后进行查找操作,查看数据是从哪个数据库中查出来的。

使用navicat连接bug:

使用navicat 12连接mycat时提示表不存在,

解决方案:使用低版本的navicat

5. java代码实现读写分离

使用springboot +mybatis的方式连接mycat进行读写分离

源码地址:https://gitee.com/tfp-study/mycat-read-write-split

其实和普通的项目没有什么区别,该怎么做还是怎么做,只是修改一下数据库连接时的url,username,password

遇到一个bug,启动报错:

java.sql.SQLNonTransientConnectionException: CLIENT_PLUGIN_AUTH is required

解决办法:
将数据库连接版本降低成5.1.37

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.37</version>
	<scope>runtime</scope>
</dependency>
驱动使用:spring.datasource.driver-class-name=com.mysql.jdbc.Driver即可