写点什么

springboot 多数据源配合 docker 部署 mysql 主从实现读写分离

  • 2021 年 11 月 11 日
  • 本文字数:3275 字

    阅读完需:约 11 分钟


记录下上的 position 和 file 在创建 MySQL slave 配置时会用到


到这里 mysql master 已经配置完成了


  • 3、使用 docker 运行 mysql slave


docker run --name mysql-slave --privileged=true -v F:\dockerV\mysql-slave:/var/lib/mysql -p 3308:3306 --link mysql-master:master -e MYSQL_ROOT_PASSWORD=654321 -d mysql:5.7.23


mysql slave 的参数主要和 master 有两点不同


  • 所映射的宿主机的端口号不能与 master 容器相同,因为其已经被 master 容器占用;

  • 必须加上--link 参数,其后指定了当前容器所要连接的容器,mysql-master 表示所要连接的容器的名称,master 表示为该容器起的一个别名,通俗来讲,就是 slave 容器通过这两个名称都可以访问到 master 容器。这么做的原因在于,如果 master 与 slave 不在同一个 docker network 中,那么这两个容器相互之间是没法访问的。


docker exec -it mysql-slave /bin/bash #进入mysql salve容器


vim mysqld.cnf #修改cnf文件,添加 server-id 表示slave服务标识,如果此salve需要作为其他mysql的主,那么就需要配置log-bin=mysql-bin


service MySQL restart # 重启mysql服务时会使得docker容器停止,我们还需要docker start mysql-slave启动容器


docker start mysql-master #启动容器


mysql -uroot -proot #连接mysql服务


配置 mysql slave 使其以 slave 模式运行


change master to master_host='172.17.0.2', master_user='slaveaccount', master_password='654321', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos=2272, master_connect_retry=30;


注意:这一步主要在 slave 是配置 master 的信息,包括 master 的 地址、端口、账号、密码、log 文件、log 文件偏移量、重试。下面介绍这些参数值从何获取


  • 获取 master_host,使用 docker inspect mysql-master 查看 master 容器元数据。其中 IPAdress 是 master_host



  • 获取 master_port,是运行 master 映射容器内部的端口,这里就是 3306

  • 获取 master_user 和 master_password,是第一步中在 master 中创建的 slave 账号

  • 获取 master_log_file 和 master_log_pos,是配置 master 中最后一步使用 show master status 获取的文件和偏移量

  • 获取 master_connect_retry,是 slave 重试连接 master 动作前的休眠时间,单位 s,默认 60s


start slave; #以slave模式运行


show salve status \G; #查看slave的状态



可以看到,Slave_IO_Running:YES 和 Slave_SQL_Running:YES 证明此时主从复制已经就绪,slave 配置到此完成


  • 4、验证主从复制效果

  • 这里我们在连接 master,并创建一张表,添加数据,在从库查看数据是否同步。

  • 在主库创建数据,并在从库查看数据是否同步成功。


mysql> create database test;


Query OK, 1 row affected (0.01 sec)


mysql> use test;


Database changed


mysql> create table t_user(id bigint, name varchar(255));


Query OK, 0 rows affected (0.02 sec)


mysql> insert into t_user(id, name) value (1, 'cgg');


Query OK, 1 row affected (0.01 sec)


mysql> select * from test.t_user;


+------+------+


| id | name |


+------+------+


| 1 | cgg |


+------+------+


1 row in set (0.00 sec)


  • 5、mysql 主从复制原理



  • 主库 db 的更新事件(update、insert、delete)被写到 binlog

  • 主库创建一个 binlog dump thread,把 binlog 的内容发送到从库

  • 从库启动并发起连接,连接到主库

  • 从库启动之后,创建一个 I/O 线程,读取主库传过来的 binlog 内容并写入到 relay log

  • 从库启动之后,创建一个 SQL 线程,从 relay log 里面读取内容,从 Exec_Master_Log_Pos 位置开始执行读取到的更新事件,将更新内容写入到 slave 的 db

二、springboot 项目多数据源配置,实现读写分离

  • 1、主从多数据源配置

  • yml 配置


server:


port: 8888


servlet:


encoding:


charset: UTF-8


force: true


enabled: true


spring:


datasource:


driver-class-name: com.mysql.cj.jdbc.Driver


type: com.alibaba.druid.pool.DruidDataSource


url: jdbc:mysql://127.0.0.1:3307/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=false


username: root


password: 654321


slave:


datasource:


driver-class-name: com.mysql.cj.jdbc.Driver


type: com.alibaba.druid.pool.DruidDataSource


url: jdbc:mysql://127.0.0.1:3308/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=false


username: root


password: 654321


  • 数据源配置


/**


  • @author cgg


**/


@Configuration


@EnableTransactionManagement


public class DynamicDataSourceConfig {


@Bean(name = "slaveDatasource")


@ConfigurationProperties(prefix = "slave.datasource")


public DataSource dbSlave() {


return DruidDataSourceBuilder.create().build();


}


@Bean(name = "dataSource")


@ConfigurationProperties(prefix = "spring.datasource")


public DataSource dbMaster() {


return DruidDataSourceBuilder.create().build();


}


@Bean


@Primary


public DataSource multipleDataSource(@Qualifier("dataSource") DataSource db,


@Qualifier("slaveDatasource") DataSource slaveDatasource) {


DynamicDataSource dynamicDataSource = new DynamicDataSource();


Map<Object, Object> targetDataSources = new HashMap<>(16);


targetDataSources.put("dataSource", db);


targetDataSources.put("slaveDatasource", slaveDatasource);


dynamicDataSource.setTargetDataSources(targetDataSources);


//设置默认数据源为从库,如果写操作业务多,可以默认设置为主库


dynamicDataSource.setDefaultTargetDataSource(slaveDatasource);


return dynamicDataSource;


}


@Bean("sqlSessionFactory")


public SqlSessionFactory sqlSessionFactory() throws Exception {


MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();


sqlSessionFactory.setDataSource(multipleDataSource(dbSlave(), dbMaster()));


MybatisConfiguration configuration = new MybatisConfiguration();


configuration.setJdbcTypeForNull(JdbcType.NULL);


configuration.setMapUnderscoreToCamelCase(true);


configuration.setCacheEnabled(false);


sqlSessionFactory.setConfiguration(configuration);


sqlSessionFactory.setMapperLocations((new PathMatchingResourcePatternResolver()).getResources(DEFAULT_MAPPER_LOCATION));


PaginationInterceptor paginationInterceptor = new PaginationInterceptor();


paginationInterceptor.setOverflow(false);


paginationInterceptor.setLimit(-1);


paginationInterceptor.setCountSqlParser(tenantSqlParserCountOptimize());


Interceptor[] plugins = new Interceptor[]{new ShardTableInterceptor(), paginationInterceptor};


sqlSessionFactory.setPlugins(plugins);


return sqlSessionFactory.getObject();


}


}


/**


  • 动态数据源

  • @author cgg


**/


@Slf4j


public class DynamicDataSource extends AbstractRoutingDataSource {


@Override


protected Object determineCurrentLookupKey() {


return DbContextHolder.getDbType();


}


}


  • 2、配置切面控制主从数据源切换、读从库写主库,实现读写分离

  • aop 切面


/**


  • @author cgg


**/


@Component


@Order(value = -100)


@Slf4j


@Aspect


public class DataSourceSwitchAspect {


//master 包下的操作都是操作主库业务


@Pointcut("execution(* com.master...(..))")


private void db1Aspect() {


}


//slave 包下的操作都是操作从库业务


@Pointcut("execution(* com.slave..(..))")


private void db2Aspect() {


}


@Before("db1Aspect()")


public void


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


dbMaster() {


log.debug("切换到 Master 数据源...");


DbContextHolder.setDbType("dataSource");


}


@Before("db2Aspect()")


public void dbSlave() {


log.debug("切换到 Slave 数据源...");


DbContextHolder.setDbType("slaveDatasource");


}


}


/**


  • @author cgg


**/


public class DbContextHolder {

评论

发布
暂无评论
springboot多数据源配合docker部署mysql主从实现读写分离