阅读 201

MySQL主从集群搭建

一、主从复制概述

在实际生产中,数据的重要性不言而喻

如果我们的数据库只有一台服务器,那么很容易产生单点故障的问题,比如这台服务器访问压力过大而没有响应或者奔溃,那么服务就不可用了,再比如这台服务器的硬盘坏了,那么整个数据库的数据就全部丢失了,这是重大的安全事故.

为了避免服务的不可用以及保障数据的安全可靠性,我们至少需要部署两台或两台以上服务器来存储数据库数据,也就是我们需要将数据复制多份部署在多台不同的服务器上,即使有一台服务器出现故障了,其他服务器依然可以继续提供服务.

MySQL提供了主从复制功能以提高服务的可用性与数据的安全可靠性。主从复制是指服务器分为主服务器和从服务器,主服务器负责读和写,从服务器只负责读,主从复制也叫 master/slavemaster是主,slave是从,但是并没有强制,也就是说从也可以写,主也可以读,只不过一般我们不这么做。

主从复制可以实现对数据库备份和读写分离

2. 主从复制原理(流程|步骤)

在这里插入图片描述

  1. 当 master 主服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中
  2. salve 从服务器会在一定时间间隔内对 master 主服务器上的二进制日志进行探测,探测其是否发生过改变(通过二进制文件的大小是否不同来进行判断,日志文件改变了的大小也可以叫作偏移),如果探测到 master 主服务器的二进制事件日志发生了改变,则开始一个 I/O Thread 请求 master 二进制事件日志
  3. 同时 master 主服务器为每个 I/O Thread 启动一个dump Thread,用于向其发送二进制事件日
  4. slave 从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件中
  5. salve 从服务器将启动 SQL Thread 从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;
  6. 最后 I/O Thread 和 SQL Thread 将进入睡眠状态,等待下一次被唤醒

注意:主从复制的过程会有很小的延迟,基本没有影响

二、多实例搭建

1. 概述

MySQL多实例是指安装MySQL之后,在一台Linux服务器上同时启动多个MySQL数据库(实例),不需要安装多个MySQL(适合技术研究和学习的场景) 如果是有多台Linux服务器,那么我们需要每台服务器都分别安装MySQL(适用于实际线上生产环境)

我们此处计划在一台Linux上启动多个MySQL,这样适合我们的技术研究和学习,如果要在多台Linux分别启动MySQL,这个与我们在一台机器上的配置与操作都是完全一样的。

如何实现在一台Linux服务器上同时启动多个MySQL数据库(实例)? 通过为各个数据库实例配置独立的配置文件来实现,即每个数据库实例有自己单独的配置文件

2. 多实例配置

下载地址:cdn.mysql.com//Downloads/…

(1) 先检查是否安装了mariadb

检查linux是否安装了mariadb数据库,mariadb数据库是mysql的分支。是免费开源的。mariadb和msyql会有冲突。首先要检查安装了mariadb, 卸载掉。

  • 检查检查命令:
    yum  list  installed  |  grep mariadb
    复制代码

若linux中安装了mariadb数据库,先卸载掉,mariadb数据库可能与安装mysql发生冲突, 在这里插入图片描述

  • 执行卸载命令:
yum -y remove mariadb-libs.x86_64
复制代码

其中mariadb-libs.x86_64是第2步搜索出来的mariadb软件包,不同机器可能不一样, -y参数确认删除。

等待卸载完成:提示Complete ,卸载完成 在这里插入图片描述

(2)解压并重命名MySQL

首先使用Xftp上传文件mysql-5.7.18-linux-glibc2.5-x86_64.tar

然后解压MySQL:

  • 执行命令:
tar  -zxvf  mysql-5.7.18-linux-glibc2.5-x86_64.tar.gz  -C  /usr/local/
复制代码

将解压后的mysql-5.7.18-linux-glibc2.5-x86_64改名为mysql-5.7.18 或者 mysql,为了明确mysql的版本,建议改名为mysql-5.7.18

  • 执行命令:
mv  mysql-5.7.18-linux-glibc2.5-x86_64  mysql-5.7.18
复制代码

(3)创建多个存放数据的目录

data文件夹是mysql用来存放数据库文件的,数据库的表数据都放在data目录。 默认没有data目录,可以手工创建data目录,在mysql-5.7.18文件夹目录下创建一个data文件夹,切换到mysql-5.7.18目录,执行创建文件夹命令

  • 在mysql-5.7.18根目录下执行命令,在MySQL安装主目录下创建四个实例存放数据的目录
mkdir  data
mkdir  data/3307
mkdir  data/3308
mkdir  data/3309
mkdir  data/3310
复制代码

(4)创建用户

创建 mysql 用户,用来执行 MySQL 的命令 mysqld ,此命令用来初始化 msyql 基础信息。 执行命令:

useradd  mysql
复制代码

(4)在MySQL的/usr/local/mysql-5.7.18/bin目录下执行命令

参数说明:

  • --initialize-insecure:初始化mysql,创建mysql的root,初始密码为空。
  • --user:执行msyqld 命令的linux用户名
  • --datadir:mysql数据文件的存放位置,分别指定刚刚创建的四个目录,目录位置参照本机的设置。
  • --basedir:msyql安装程序的目录,目录位置参照本机的设置

注意:目录要根据实际情况修改

./mysqld --initialize-insecure --basedir=/usr/local/mysql-5.7.18 --datadir=/usr/local/mysql-5.7.18/data/3307 --user=mysql

./mysqld --initialize-insecure --basedir=/usr/local/mysql-5.7.18 --datadir=/usr/local/mysql-5.7.18/data/3308 --user=mysql

./mysqld --initialize-insecure --basedir=/usr/local/mysql-5.7.18 --datadir=/usr/local/mysql-5.7.18/data/3309 --user=mysql

./mysqld --initialize-insecure --basedir=/usr/local/mysql-5.7.18 --datadir=/usr/local/mysql-5.7.18/data/3310 --user=mysql
复制代码

(5)配置MySQL的主配置文件

在data/3307, data/3308, data/3309, data/3310 四个目录下分别创建一个my.cnf文件,在四个my.cnf文件分别配置如下内容

  • [client]:表示配置MySQL的客户端
  • [mysqld]:配置MySQL服务端

注意:不同的实例下配置要修改端口号

[client]
port=3307
socket=/usr/local/mysql-5.7.18/data/3307/mysql.sock
default-character-set=utf8

[mysqld]
port=3307
socket=/usr/local/mysql-5.7.18/data/3307/mysql.sock
datadir=/usr/local/mysql-5.7.18/data/3307
log-error=/usr/local/mysql-5.7.18/data/3307/error.log
pid-file=/usr/local/mysql-5.7.18/data/3307/mysql.pid

character-set-server=utf8   # 字符编码格式
lower_case_table_names=1	# 表名是否区分大小写,默认为1,不区分大小写;0区分大小写
autocommit=1				# 事务是否自动提交,默认为1,自动提交;0不自动提交
复制代码

(6)启动MySQL

切换到/usr/local/mysql-5.7.18/bin目录下,使用 msyqld_safe 命令指定配置文件并启动MySQL服务:

  • --defaults-file:指定配置文件,
  • &:表示后台启动
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.18/data/3307/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.18/data/3308/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.18/data/3309/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.18/data/3310/my.cnf &
复制代码

(7)启动后配置

  • 登录进入每一个MySQL实例客户端,在mysql-5.7.24/bin目录下执行命令:

    • 方式一:使用用端口、主机登录(推荐使用该方式)
    ./mysql -uroot -p -P3307 -h127.0.0.1
    复制代码
    • 方式二:使用socket文件
      • -p 是指定密码,如果没有密码则可以不写 -p,
      • -S是指定sock文件,mysql.sock文件是服务器与本机客户端进行通信的ip与端口文件
    ./mysql -uroot -p -S /usr/local/mysql-5.7.24/data/3307/mysql.sock
    复制代码
  • 修改mysql的密码,其中123456是我们设置的密码:

    alter user 'root'@'localhost' identified by '123456';
    复制代码
  • 授权远程访问(这样远程客户端navicat才能访问)

    • 其中*.* 的第一个*表示所有数据库名,第二个*表示所有的数据库表
    • root@'%' 中的root表示用户名,%表示所有ip地址,%也可以指定具体的ip地址,比如root@localhost,root@192.168.10.129
    grant all privileges on *.* to root@'%' identified by '123456';
    复制代码
  • 刷新权限

    flush privileges; 
    复制代码
  • 多实例关闭,切换到/usr/local/mysql-5.7.24/bin目录下,使用 mysqladmin 命令 shutdown

    • 方式一:使用端口、主机关闭 (推荐)
    ./mysqladmin -uroot -p -P3307 -h127.0.0.1 shutdown
    复制代码
    • 方式二:使用socket文件
     ./mysqladmin -uroot -p -S /usr/local/mysql-5.7.24/data/3307/mysql.sock shutdown
    复制代码
    • 方式三:进入MySQL的客户端命令行,直接执行 shutdown

三、一主多从架构

1. 概述

当系统对数据的读取比较多时,为了分摊读的压力,可以采用一主多从架构,实现读写分离 在这里插入图片描述 这里,我们使用上面配置好的多个MySQL实例,3307为主,3308,3309,3310为从服务器

2. 配置

  • 主MySQL(3307)的配置文件my.cnf中加入(要删除掉中文注释):
    • log-bin:表示启用二进制日志,指定二进制文件名
    • server-id:表示server编号,用来唯一标识MySQL服务器,编号要唯一
    log-bin=mysql-bin
    server-id=3307
    复制代码
  • 从MySQL(3308,3309,3310)配置文件my.cnf中分别加入(注意server-id不能相同):
    server-id=3308
    复制代码

4. 主从配置

(1)创建用户

启动主MySQL服务器,登入主MySQL客户端,在主服务器上创建复制数据的账号并授权:

grant replication slave on *.* to 'copy'@'%' identified by '123456';
复制代码

创建一个MySQL用户,该用户是主服务器专门给从服务器拷贝数据用的

注意:该语句可完成授权、创建用户、修改密码操作

(2)重置主服务器状态

  • 查看主服务器状态:

    show master status;
    复制代码
    • 默认的mysql主服务器默认初始值:
      • File:mysql-bin.000001
      • Position:154
  • 如果主服务状态不是初始状态,需要重置状态:

    reset master;
    复制代码

(3)重置从服务器状态

  • 查看从服务器状态

    show slave status;
    复制代码
    • 初始状态:Empty set
  • 如果从服务器不是初始状态,建议重置一下

    stop slave; 	#停止复制,相当于终止从服务器上的IO和SQL线程
    reset slave;
    复制代码
  • 设置从服务器的master,在从服务器客户端执行:

    • master_host:主服务器ip
    • master_port:主服务器端口
    • master_user,master_password:2.4.1中创建的专门执行主从复制的用户名和密码
    • master_log_file:2.4.2中查出的File值
    • master_log_pos:2.4.2中查出的Position值,如果主服务器是初始值的话,就是154
    change master to master_host='192.168.235.128',master_user='copy',
    master_port=3306,master_password='123456',
    master_log_file='mysql-bin.000001',master_log_pos=154;
    复制代码
  • 在从机器上执行开始复制命令:

    start slave;
    复制代码

5. 验证

  • 在从服务器的客户端执行以下命令:

    • \G:表示格式化输出
    show slave status \G;
    复制代码

    显示结果: 在这里插入图片描述 注意:如果 Slave_IO_RunningSlave_SQL_Running 均为 YES,则表示主从关系正常

  • 在主服务器上创建数据库、表、数据,然后在从服务器上查看是否已经复制

  • 查看主从复制binlog日志文件内容: 在主服务器客户端执行:

    show binlog events in 'mysql-bin.000001'\G;
    复制代码

    查看结果: 在这里插入图片描述

四、多主多从架构

1. 概述

一主多从,可以缓解读的压力,但是一旦主宕机了,就不能写了,所以我们可以采用双主双从架构来改进它的不足。 在这里插入图片描述 架构规划:

  • 主master 3307 ---> 从slave 3309
  • 主master 3308 ---> 从slave 3310
  • 3307 <---> 3308 互为主从
  • 2个写节点,每个写节点下又是2个读节点

2. 环境配置

  • 在主MySQL的配置文件my.cnf中分别加入(要删除掉中文注释):

    log-bin=mysql-bin
    server-id=3307
    复制代码
  • 从MySQL配置文件my.cnf中分别加入(注意server-id不能相同):

    server-id=3309
    复制代码
  • 在第一台主服务器(3307)的配置文件my.cnf中加入:

    auto_increment_increment=2 #主键自动增长的偏移量
    auto_increment_offset=1 #不一样的地方,主键增长的起始值
    log-slave-updates
    sync_binlog=1
    复制代码
  • 而在第一台主服务器(3308)的配置文件my.cnf中加入:

    auto_increment_increment=2
    auto_increment_offset=2 #不一样的点 相当于起始值
    log-slave-updates
    sync_binlog=1
    复制代码

配置项说明:

  • auto_increment_increment:

控制主键自增的自增步长,用于防止Master与Master之间复制出现重复自增字段值,通常auto_increment_increment=n,有多少台主服务器,n 就设置为多少

  • auto_increment_offset=1:

设置自增起始值,这里设置为1,这样Master的auto_increment字段产生的数值是:1, 3, 5, 7, …等奇数ID - 注意auto_increment_offset的设置,不同的master设置不应该一样,否则就容易引起主键冲突,比如master1的offset=1,则master2的offset=2,master3的offset=3

  • log-slave-updates:

开启log-slave-updates参数后,从库从主库复制的数据会写入log-bin日志文件里。在双主模式中,log-slave-updates 配置项一定要配置,否则在master1(3307)上进行了更新数据,在master2(3308)和slave1(3309)上会更新,但是在slave2(3310)上不会更新

  • sync_binlog:

表示每几次事务提交,MySQL把binlog缓存刷进日志文件中,默认是0,最安全的是设置为1,性能也最低 - sync_binlog=0,当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘。 - sync_binlog=n,当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

3. 服务启动

进入/usr/local/mysql-5.7.24/bin目录,重启四个MySQL服务,启动时指定配置文件:

./mysqld_safe --defaults-file=/usr/local/mysql-5.7.24/data/3307/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.24/data/3308/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.24/data/3309/my.cnf &
./mysqld_safe --defaults-file=/usr/local/mysql-5.7.24/data/3310/my.cnf &
复制代码

4. 主从设置

(1)主服务器设置

  1. 登录到主服务器3307|3308的客户端:

在/usr/local/mysql-5.7.24/bin目录下执行 :

```bash
./mysql -uroot -p -P3307|3308 -h127.0.0.1
```
复制代码
  1. 在两台主服务器(3307|3308)上创建复制数据的账号并授权

    grant replication slave on *.* to 'copy'@'%' identified by '123456';
    复制代码
  2. 在两台主服务器(3307|3308)上停止复制并重置服务器状态

    reset master;
    复制代码

(2)从服务器设置

  1. 登录到从服务器(四个服务器均为从)的客户端:

在/usr/local/mysql-5.7.24/bin目录下执行 :

```bash
./mysql -uroot -p -P3307|3308|3309|3310 -h127.0.0.1
```
复制代码
  1. 在从服务器上停止复制并重置服务器状态:

    stop slave;
    reset slave;
    复制代码
  2. 设置从服务器的master(相当于是4台都需要设置)

    • 设置从服务器3308、3309的主为3307:

      change master to master_host='192.168.235.128',master_user='copy',
      master_port=3307,master_password='123456',
      master_log_file='mysql-bin.000001',master_log_pos=154;
      复制代码
    • 设置从服务器3307、3310的主为3308:

      change master to master_host='192.168.235.128',master_user='copy',
      master_port=3308,master_password='123456',
      master_log_file='mysql-bin.000001',master_log_pos=154;
      复制代码
  3. 在从机器上执行开始复制命令(4台MySQL上都执行)

    start slave;
    复制代码

5. 验证

验证同一主多从的验证方式,这里就不赘述了

六、多数据源问题

多数据源问题是指在一个项目工程中,需要连接多个数据库,以上配置了数据库的主从,在实际开发中可能会有以下几种情况

  • 读写分离:项目中使用多数据源
  • 项目中只操作主库(读和写),主从只是起到了备份作用,程序员不需要关心主从结构

我们这里主要给大家介绍Spring+Mybatis和SpringBoot+Mybatis开发模式下的多数据源问题

1. Spring+Mybatis

核心思想:基于动态数据源,在运行的时候才知道要使用哪个数据源

(1)添加依赖

<!--Spring相关的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>

<!--Mybatis相关依赖-->
<!--Mybatis框架依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!--Mybatis与Spring整合依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!--MySQL数据库连接驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.43</version>
</dependency>
<!--JDBC 数据库连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.1</version>
</dependency>
复制代码

添加resource,指定编译Mybatis映射文件

<resource>
    <directory>src/main/java</directory>
    <includes>
        <include>**/*.xml</include>
    </includes>
</resource>

复制代码

(2)创建MyThreadLocal类

在每个线程中存储一个值,该值表示该线程操作数据库使用的是哪个数据源,使用的是ThreadLocal

public class MyThreadLocal {
    private static ThreadLocal<String> local = new ThreadLocal<String>();

	//指定当前线程使用的是哪个数据源
    public static void addDataSource(String dataSourceName) {
        local.set(dataSourceName);
    }

	//获取当前线程使用的数据源
    public static String getDataSource() {
        return local.get();
    }

	//重置当前线程使用的数据源
    public static void removeDataSource() {
        local.remove();
    }
}
复制代码

ThreadLocal详解:ThreadLocal

(3)自定义数据源

  • 设计的思路是将所有数据源放到一个map集合中,通过指定map集合的key,可以动态获取不同的数据源

  • 实现一个自定义数据源,数据源对象都要实现 javax.sql.DataSource 接口,AbstractRoutingDataSource 对象的父类实现了这个接口

  • DynamicDataSource类继承AbstractRoutingDataSource抽象类,并实现抽象方法determineCurrentLookupKey()

  • 在进行数据库操作前,我们提前设置使用哪个数据库,然后在指定的数据源的时候,会自动调用这个determineCurrentLookupKey()方法,得到指定的值,调用指定的数据源。

  • 如何为determineCurrentLookupKey()动态的指定key呢?直观的想法,可能是定义一个静态变量,但是静态变量存在线程安全问题,所以我们需要为每一个线程都维护一个变量,用于指定连接的数据库是哪个,所以我们这里使用线程的副本ThreadLocal类

  • 推荐定义常量,作为动态数据源对应的key

    public static final String DATASOURCE_KEY_3307 = "3307";
    ...
    复制代码
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

//定义自定义数据源类,继承一个动态数据源的抽象父类
public class DynamicDataSource extends AbstractRoutingDataSource {
    //实现父类中的抽象方法,用于返回某个具体的数据源
    protected Object determineCurrentLookupKey() {
        return MyThreadLocal.getDataSource();
    }
}
复制代码

(4)配置数据源

  • 如何将指定的key和不同的数据源一一映射起来呢?在applicationContext-mybatis.xml对动态数据源DynamicDataSource类进行配置
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--指定数据源-->
    <bean id="masterDataSource3307" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.245.128:3307/workdb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <bean id="masterDataSource3308" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.245.128:3308/workdb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <bean id="slaveDataSource3309" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.245.128:3309/workdb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <bean id="slaveDataSource3310" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.245.128:3310/workdb"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
    <!--定义自定义动态数据源Bean -->
    <bean id="dynamicDataSource" class="com.wkcto.masterslave.datasource.DynamicDataSource">
        <!--定义默认数据源,如果没有切换数据源则使用masterDataSource作为默认数据源 -->
        <property name="defaultTargetDataSource" ref="masterDataSource"/>
        <!--配置一个Map集合,这个集合可以看做数据源的容器对象(工厂)
            Map集合的key 必须与Java业务方法中ThreadLocal中的数据内容保持一致
            Map集合的value 则为我们的具体数据源对象
         -->
        <property name="targetDataSources">
            <map>
            	<!--在java中这里可以把这里的key指定为常量
            		在这里将ThreadLoacl中的key和数据源一一对应起来
            	-->
                <entry key="3307" value-ref="masterDataSource3307"></entry>
                <entry key="3308" value-ref="masterDataSource3308"></entry>
                <entry key="3309" value-ref="slaveDataSource3309"></entry>
                <entry key="3310" value-ref="slaveDataSource3310"></entry>
            </map>
        </property>
    </bean>

    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--装载动态数据源 -->
        <property name="dataSource" ref="dynamicDataSource"/>
    </bean>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
        <property name="basePackage" value="com.wkcto.masterslave.mapper"/>
    </bean>
</beans>
复制代码

(5)动态切换数据库

  • 在操作数据库之前,选择要使用的数据库
  • 操作完数据库之后清空ThreadLocal(重置数据源)
    • 因为Tomcat使用的是线程池,一个线程用完后并不会被销毁,而是再次加入了线程池,为了避免下一次的再使用该线程时造成不必要的麻烦,需要把ThreadLoacl清空
public void addStu(Stu stu) {
   //数据源切换
    MyThreadLocal.addDataSource("master");
    stuMapper.insert(stu);
    stuMapper.insert(stu);
    //使用完之后清空ThreadLoacl
    MyThreadLocal.removeDataSource();
}

public List<Stu> selectAll() {
    MyThreadLocal.addDataSource("slave");
    List<Stu>list=stuMapper.selectAll();
    MyThreadLocal.removeDataSource();
    return list;
}
复制代码

2. SpringBoot+Mybatis

方法与Spring+Mybatis一样,只是将配置文件改为了配置类:

配置文件标签中的id为配置类中的方法名,class为方法返回值类型

import com.alibaba.druid.pool.DruidDataSource;
import com.wkcto.masterslave.datasource.DynamicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Bean
    public DruidDataSource masterDataSource3307(){
        DruidDataSource druidDataSource=new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://192.168.245.128:3307/workdb");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    @Bean
    public DruidDataSource masterDataSource3308(){
        DruidDataSource druidDataSource=new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://192.168.245.128:3308/workdb");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    @Bean
    public DruidDataSource slaveDataSource3309(){
        DruidDataSource druidDataSource=new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://192.168.245.128:3309/workdb");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    @Bean
    public DruidDataSource slaveDataSource3310(){
        DruidDataSource druidDataSource=new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://192.168.245.128:3310/workdb");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    @Bean
    public DynamicDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource){
        DynamicDataSource dataSource=new DynamicDataSource();
        dataSource.setDefaultTargetDataSource(masterDataSource);
        Map map=new HashMap();
        map.put("3307r",masterDataSource3307);
        map.put("3308",masterDataSource3308);
        map.put("3309",slaveDataSource3309);
        map.put("3310",slaveDataSource3310);
        dataSource.setTargetDataSources(map);
        return dataSource;
    }
     @Bean
     public SqlSessionFactoryBean sessionFactory(DataSource dynamicDataSource){
         SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
         factoryBean.setDataSource(dynamicDataSource);
         return factoryBean;
     }
     @Bean
     public MapperScannerConfigurer mapperScannerConfigurer(){
          MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer();
          mapperScannerConfigurer.setSqlSessionFactoryBeanName("sessionFactory");
          mapperScannerConfigurer.setBasePackage("com.wkcto.masterslave.mapper");
          return mapperScannerConfigurer;
     }
}
复制代码
文章分类
后端
文章标签