一 概述
Mysql作为被广泛使用的开源数据库,在实际开发过程中,由单台Mysql作为独立的数据库是完全不能满足实际需求的,无论是从安全性,高可用性以及高并发等各个方面。
因此,一般都是通过主从复制(Master-Slave)的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。
二 Mybatis实现Mysql读写分离
配置信息实例
mysqldb.propertis
#主数据库数据源
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://ip:3306/dbName?useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=1
jdbc.minIdle=1
jdbc.maxActive=20
jdbc.maxWait=60000
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=180
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.minEvictableIdleTimeMillis=300000
jdbc.validationQuery=SELECT 1
jdbc.testWhileIdle=true
jdbc.testOnBorrow=false
jdbc.testOnReturn=false
#从数据库数据源
slave.jdbc.driverClassName=com.mysql.jdbc.Driver
slave.jdbc.url=jdbc:mysql://ip:3306/dbName?useUnicode=true&characterEncoding=utf-8&useSSL=false
slave.jdbc.username=root
slave.jdbc.password=123456
slave.jdbc.initialSize=1
slave.jdbc.minIdle=1
slave.jdbc.maxActive=20
slave.jdbc.maxWait=60000
slave.jdbc.removeAbandoned=true
slave.jdbc.removeAbandonedTimeout=180
slave.jdbc.timeBetweenEvictionRunsMillis=60000
slave.jdbc.minEvictableIdleTimeMillis=300000
slave.jdbc.validationQuery=SELECT 1
slave.jdbc.testWhileIdle=true
slave.jdbc.testOnBorrow=false
slave.jdbc.testOnReturn=false
mybatis-Spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- master数据源 -->
<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialSize" value="${jdbc.initialSize}" />
<property name="minIdle" value="${jdbc.minIdle}" />
<property name="maxActive" value="${jdbc.maxActive}" />
<property name="maxWait" value="${jdbc.maxWait}" />
<!-- 超过时间限制是否回收 -->
<property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
<!-- 超过时间限制多长; -->
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
<!-- 用来检测连接是否有效的sql,要求是一个查询语句-->
<property name="validationQuery" value="${jdbc.validationQuery}" />
<!-- 申请连接的时候检测 -->
<property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
<!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
<property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
<!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
<property name="testOnReturn" value="${jdbc.testOnReturn}" />
</bean>
<!-- slave数据源 -->
<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${slave.jdbc.driverClassName}" />
<property name="url" value="${slave.jdbc.url}" />
<property name="username" value="${slave.jdbc.username}" />
<property name="password" value="${slave.jdbc.password}" />
<property name="initialSize" value="${slave.jdbc.initialSize}" />
<property name="minIdle" value="${slave.jdbc.minIdle}" />
<property name="maxActive" value="${slave.jdbc.maxActive}" />
<property name="maxWait" value="${slave.jdbc.maxWait}" />
<property name="removeAbandoned" value="${slave.jdbc.removeAbandoned}" />
<property name="removeAbandonedTimeout" value="${slave.jdbc.removeAbandonedTimeout}" />
<property name="timeBetweenEvictionRunsMillis" value="${slave.jdbc.timeBetweenEvictionRunsMillis}" />
<property name="minEvictableIdleTimeMillis" value="${slave.jdbc.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${slave.jdbc.validationQuery}" />
<property name="testWhileIdle" value="${slave.jdbc.testWhileIdle}" />
<property name="testOnBorrow" value="${slave.jdbc.testOnBorrow}" />
<property name="testOnReturn" value="${slave.jdbc.testOnReturn}" />
</bean>
<!-- 动态数据源,根据service接口上的注解来决定取哪个数据源 -->
<bean id="dataSource" class="com.yzb.util.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write or slave -->
<entry key="slave" value-ref="slaveDataSource"/>
<!-- read or master -->
<entry key="master" value-ref="masterDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"/>
</bean>
<!-- Mybatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
<!-- 映射文件路径 -->
<property name="mapperLocations" value="classpath*:dbmappers/*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yzb.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 声明式开启 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="1"/>
<!-- 为业务逻辑层的方法解析@DataSource注解 为当前线程的HandleDataSource注入数据源 -->
<bean id="dataSourceAspect" class="com.yzb.util.DataSourceAspect" />
<aop:config proxy-target-class="true">
<aop:aspect id="dataSourceAspect" ref="dataSourceAspect" order="2">
<aop:pointcut id="tx" expression="execution(* com.yzb.service.impl..*.*(..)) "/>
<aop:before pointcut-ref="tx" method="before" />
</aop:aspect>
</aop:config>
</beans>
实际上Spring集成Mybatis实现Mysql读写分离是从代码层面实现读写分离,本质是通过注解和aop实现。初次之外,需要需要配置个不同据源分别为:masterDataSource,slaveDataSource,分别对应主从数据库,在代码实现方面需要指定使用哪个数据源。
基于上述实现,会增加一定的代码量(增加注解),并且耦合在代码中不利于扩展与后续维护,一旦进行修改就需要重新编译打包,最严重的是数据库出现宕机时,应用就会抛出异常,就完全无法进行服务。
二 数据库中间件Mycat实现数据库读写分离
Mycat是数据库中间件,介于数据库应用之间,进行数据处理与交互的中间件服务,可以简单的理解成数据库代理,在实际应用过程中,我们只需要与数据库中间件进行交互,而无需关注复杂的数据部署等细节。
细节包括数据源切换,事务处理数据集合等操作本来都需要从应用层面进行直接处理,现在我们通过借助数据库中间件进行处理,使得我们更加专注于业务的应用。
Mycat是一个功能丰富的数据库中间件,对数据库的读写分离和分库分表都有支持,而且对原有的系统减少代码的侵入,同时支持故障切换。