关于利用mycat结合springboot实现数据库的读写分离(动态切换数据源)个人笔记

5 篇文章 0 订阅
1 篇文章 0 订阅

关于mycat这个中间件,我就不多写了,网上褒贬不一。但是我们作为学习来说,是学的一个思维能力,动手能力,理解能力。这里做一下笔记,方便以后查看吧。

 

这里说一下我这边的环境:

数据库:mysql5.7以上

mycat:1.65

 

首先我们完成mysql的主从同步,这里就不说怎么完成的了,在之前文章里讲到过。

安装mycat也不多说了,大家可以去网上找教程什么的,都很方便。

 

安装完成之后,我们打开mycat路径里的conf配置文件目录,在里面我们要注意两个配置文件:

1.schema.xml   Mycat对应的物理数据库和数据库表的配置

2.server.xml   Mycat的配置文件,设置账号、参数等

我们这里要对这俩配置文件进行配置:

schema.xml:

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <!-- TESTDB1 是mycat的逻辑库名称,链接需要用的 -->
    <schema name="mycat_zyx" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"></schema>
        <!-- database 是MySQL数据库的库名 -->
    <dataNode name="dn1" dataHost="localhost1" database="zyx" />
    <!--
    上面的dataNode对应这里的dataNode的name,也就是dn1,这里相同即可,可以随便取名
    dataNode节点中各属性说明:
    name:指定逻辑数据节点名称;
    dataHost:指定逻辑数据节点物理主机节点名称;也就是下面的datahost的name,也就是localhost1对面下面的localhost1,这里相同即可
    database:指定物理主机节点上。如果一个节点上有多个库,可使用表达式db$0-99,     表示指定0-99这100个数据库;

    dataHost 节点中各属性说明:
        name:物理主机节点名称;
        maxCon:指定物理主机服务最大支持1000个连接;
        minCon:指定物理主机服务最小保持10个连接;
        writeType:指定写入类型;
            0,只在writeHost节点写入;
            1,在所有节点都写入。慎重开启,多节点写入顺序为默认写入根据配置顺序,第一个挂掉切换另一个;
        dbType:指定数据库类型;
        dbDriver:指定数据库驱动;
        balance:指定物理主机服务的负载模式。
            0,不开启读写分离机制;
            1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡;
            2,所有的readHost与writeHost都参与select语句的负载均衡,也就是说,当系统的写操作压力不大的情况下,所有主机都可以承担负载均衡;
-->
    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- 可以配置多个主从 -->
        <writeHost host="hostM1" url="192.168.1.133:3306" user="root" password="root">
            <!-- 可以配置多个从库 -->
            <readHost host="hostS2" url="192.168.1.132:3306" user="root" password="root" />
        </writeHost>
    </dataHost>
</mycat:schema>
 

 不要管它的配置,直接把我这段内容覆盖进去即可。

这里大致说一下

mysql主库ip:192.168.1.133

mysql从库ip:192.168.1.132

mycat安装的服务器ip:192.168.1.133也就是安装在主库服务器下

server.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
    - you may not use this file except in compliance with the License. - You 
    may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
    - - Unless required by applicable law or agreed to in writing, software - 
    distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
    License for the specific language governing permissions and - limitations 
    under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
   

   <!-- 读写都可用的用户 -->
    <user name="root" defaultAccount="true">
        <property name="password">root</property>
        <property name="schemas">mycat_zyx</property>
    </user>

    <!-- 只读用户 -->
    <user name="user">
        <property name="password">user</property>
        <property name="schemas">mycat_zyx</property>
        <property name="readOnly">true</property>
    </user>

</mycat:server>

关于更多配置,这里就不写了,可以自行查询,我这么配,主要是为了完成集成mycat中间件来实现读写分离。

就这么简单,配置就完成了。我们进入mycat目录启动mycat即可

mycat目录启动mycat:./mycat start

关于是否启动成功,进入mycat的logs目录下查看wrapper.log。里面如果有successful就说明启动成功了。

实在不放心,我们可以利用navicat工具去外部连接mycat虚拟数据库去看看是否成功

这里要注意一下,mycat的端口号为8066,如果连不上,10060报错,注意一下端口号是否开放,或者说是否关闭防火墙。

到此处,mycat全部配置结束了。我们进入下一步看如何结合springboot来实现动态数据源切换。

 

首先,我们在maven引入以下包,如果有了的,可以忽略,重点就是aop这个包:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.23</version>
        </dependency>
    </dependencies>

 

之后我们在springboot的yml配置文件里加入以下配置:

这里的username和password对应的是你在server.xml里配置的用户名和密码

然后在config包里创建配置类:

DataSourceConfig.java

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DataSourceConfig {

    // 创建可读数据源
    @Bean(name = "selectDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.select") // application.properteis中对应属性的前缀
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    // 创建可写数据源
    @Bean(name = "updateDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.update") // application.properteis中对应属性的前缀
    public DataSource dataSource2() {
        return DataSourceBuilder.create().build();
    }

}

 DataSourceContextHolder.java

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy(false)
public class DataSourceContextHolder {
    // 采用ThreadLocal 保存本地多数据源
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    // 设置数据源类型
    public static void setDbType(String dbType) {
        contextHolder.set(dbType);
    }

    public static String getDbType() {
        return contextHolder.get();
    }

    public static void clearDbType() {
        contextHolder.remove();
    }

}

DynamicDataSource.java

 

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

//在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。

@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Autowired
    @Qualifier("selectDataSource")
    private DataSource selectDataSource;

    @Autowired
    @Qualifier("updateDataSource")
    private DataSource updateDataSource;

    /**
     * 这个是主要的方法,返回的是生效的数据源名称
     */
    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("DataSourceContextHolder:::" + DataSourceContextHolder.getDbType());
        return DataSourceContextHolder.getDbType();
    }

    /**
     * 配置数据源信息
     */
    @Override
    public void afterPropertiesSet() {
        Map<Object, Object> map = new HashMap<>();
        map.put("selectDataSource", selectDataSource);
        map.put("updateDataSource", updateDataSource);
        setTargetDataSources(map);
        setDefaultTargetDataSource(updateDataSource);
        super.afterPropertiesSet();
    }
}

配置完成之后,我们利用springboot的aop技术,来完成动态数据源切换。

SwitchDataSourceAOP.java

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.zyx.db.config.DataSourceContextHolder;

// 使用AOP动态切换不同的数据源
@Aspect
@Component
@Lazy(false)
@Order(0) // Order设定AOP执行顺序 使之在数据库事务上先执行
public class SwitchDataSourceAOP {
    // 这里切到你的方法目录
    @Before("execution(* com.zyx.service.*.*(..))")
    public void process(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        if (methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("find")
                || methodName.startsWith("list") || methodName.startsWith("select") || methodName.startsWith("check")) {
            DataSourceContextHolder.setDbType("selectDataSource");
        } else {
            // 切换dataSource
            DataSourceContextHolder.setDbType("updateDataSource");
        }
    }
}

 我们利用aop拦截service方法前置名来拦截service方法进行动态数据源切换。

所以这个事提醒了我们编程要规范。方法名最好不要随意乱取,要根据一定的规范来命名。

 

到这里,就全部结束了,至于是否成功,可以写一个controller,service,dao来做个测试,这里就不放测试结果了。

注意service的方法名前缀,包地址要和SwitchDataSourceAOP.java里的@Before("execution(* com.zyx.service.*.*(..))")地址对应

至于验证过程,大可以把yml里的配置改一下,读写数据源都用那个mycat的读数据源,然后用service里的写数据操作。看看是否能写即可。

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值