在高并发的互联网项目中,数据库的压力一直是一个瓶颈,而大量的操作均为读操作,通过MySQL的读写分离,一主多从的当时能够大大的降低数据库的压力。在之前的Springboot章节中有过类似的话题,当时我们可以通过配置多个数据源,通过不同的数据源指向实现读写分离,还实现了基于AOP的动态数据源切换,当然这也需要归功于Spring提供了数据源的路由功能,不过此方式依然会对我们的业务代码造成侵入,本章将通过MyCat实现无侵入的读写分离。
基础设施
目前本地已经运行有3个实例服务
Master 127.0.0.1:3310
Slave 127.0.0.1:3321
Slave 127.0.0.1:3322
本案例主要验证读写分离,故数据库均为springboot1,且用户名密码均为root。
特殊数据,由于我们是单向的复制,故将3321中的被查询数据的password调整为1111,3310和3322中均为111,后续将通过对此数据的查询验证。
工程改造
1、调整内容非常简单,仅需要我们将原MySQL的连接信息替换为MyCat的连接信息即可:
#single configure
#spring.datasource.url=jdbc:mysql://localhost:3306/springboot1?useSSL=false
#spring.datasource.username=root
#spring.datasource.password=root
#MyCat
spring.datasource.url=
jdbc:mysql://localhost:8066/TESTDB?useSSL=false
spring.datasource.username=root
spring.datasource.password=
123456
2、调整默认事务配置,关闭spring-data-jpa默认的事务管理机制:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
(enableDefaultTransactions=false,
entityManagerFactoryRef="entityManagerFactory1",
transactionManagerRef="transactionManager1",
basePackages= { "com.shf.springboot.*","com.shf.springboot2","com.shf.springboot3.service" }//
,repositoryFactoryBeanClass=BaseRepositoryFactoryBean.class
) //设置Repository所在位置
public class JpaSource1Configuration implements Ordered {
.........................
}
在使用mycat时需要关闭spring-data-jpa默认的事务管理机制.
原因如下:
- mycat对于开启了事务的查询,插入等操作,都会走主库
- spring-data-jpa默认的事务管理机制对查询操作执行的是只读事务,可惜只读事务也是事务
鉴于以上两个原因.我们就得使用enableDefaultTransactions = false来关闭spring-data-jpa默认的事务管理机制
以上就是所有的代码改造,可以看到基本是无任何侵入的。
验证
读验证
当我们执行查询时,首次查询
其对应的日志记录
2017-02-05 16:24:57.758 DEBUG [$_NIOREACTOR-3-RW] (io.mycat.server.NonBlockingSession.releaseConnection(NonBlockingSession.java:341)) - release connection MySQLConnection [id=16, lastTime=1486283097750, user=root, schema=springboot1, old shema=springboot1, borrowed=true, fromSlaveDB=true, threadId=8, charset=utf8, txIsolation=3, autocommit=true, attachment=dn1{select user0_.id as id1_1_, user0_.birthday as birthday2_1_, user0_.email as email3_1_, user0_.loginname as loginnam4_1_, user0_.name as name5_1_, user0_.password as password6_1_ from t_sys_user user0_}, respHandler=SingleNodeHandler [node=dn1{select user0_.id as id1_1_, user0_.birthday as birthday2_1_, user0_.email as email3_1_, user0_.loginname as loginnam4_1_, user0_.name as name5_1_, user0_.password as password6_1_ from t_sys_user user0_}, packetId=-127], host=127.0.0.1, port=
3322
, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
再次查询
此次的日志记录
2017-02-05 16:34:22.843 DEBUG [$_NIOREACTOR-2-RW] (io.mycat.server.NonBlockingSession.releaseConnection(NonBlockingSession.java:341)) - release connection MySQLConnection [id=14, lastTime=1486283662774, user=root, schema=springboot1, old shema=springboot1, borrowed=true, fromSlaveDB=false, threadId=5, charset=utf8, txIsolation=3, autocommit=true, attachment=dn1{select user0_.id as id1_1_, user0_.birthday as birthday2_1_, user0_.email as email3_1_, user0_.loginname as loginnam4_1_, user0_.name as name5_1_, user0_.password as password6_1_ from t_sys_user user0_}, respHandler=SingleNodeHandler [node=dn1{select user0_.id as id1_1_, user0_.birthday as birthday2_1_, user0_.email as email3_1_, user0_.loginname as loginnam4_1_, user0_.name as name5_1_, user0_.password as password6_1_ from t_sys_user user0_}, packetId=-127], host=localhost, port=
3321
, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
可以看到两次的查询记录并不相同,但与我们预设的从库数据是对应的,说明我们的查询确实是从Slave库读取。
写验证
当我们单元测试新增时
@Test
public void testUserSave(){
try{
List<User> list=new ArrayList<User>(10);
list.add(new User("11121","11221"));
list.add(new User("22222","222"));
list.add(new User("333","333"));
userService.save(list);
}catch(Exception e){
e.printStackTrace();
}
}
我们查看日志
2017-02-05 16:19:46.367 DEBUG [$_NIOREACTOR-0-RW] (io.mycat.server.NonBlockingSession.releaseConnection(NonBlockingSession.java:341)) - release connection MySQLConnection [id=8, lastTime=1486282786334, user=root, schema=springboot1, old shema=springboot1, borrowed=true, fromSlaveDB=false, threadId=13, charset=utf8, txIsolation=3, autocommit=false, attachment=dn1{insert into t_sys_user (loginname, password) values ('333', '333')}, respHandler=io.mycat.backend.mysql.nio.handler.CommitNodeHandler@3efbb1d8, host=localhost, port=
3310
, statusSync=null, writeQueue=0, modifiedSQLExecuted=true]
采用的为Master库。
总结:
1、通过MyCat在无侵入的情况下实现了读写分离;
2、我们需要特别注意关闭spring-data-jpa默认的事务管理机制即可,在业务层使用@Transactional注解来进行声明式事务管理。