系列导航

springBoot项目打jar包

1、springboot工程新建(单模块)

2、springboot创建多模块工程

3、springboot连接数据库

4、SpringBoot连接数据库引入druid

5、SpringBoot连接数据库引入mybatis

6、SpringBoot-mybatis分页实现pagehelper

7、SpringBoot-mybatis-plus引入

8、SpringBoot 事务

9、SpringBoot-mybatis-druid多源数据多源数据

10、SpringBoot-mybatis-plus-druid多源数据

11、SpringBoot-mybatis-plus-druid多源数据事务

12、SpringBoot-mybatis-plus-ehcache

13、SpringBoot-配置文件里密码加密

14、SpringBoot-easyexcel导出excle

完结

本文介绍如何在mybatis-plus上使用多源数据,本来以为mybatis-plus上多源数据和mybatis差不多实际操作后发现真还是不太一样。mybatis-plus的配置多源数据的大致思路就是,利用了切面的思想,访问那个mapper就把当前数据连接切换到对应的数据源上。

1数据库中创建表

zy数据库:

  1. CREATE TABLE TEST_BLOCK_T
  2. (
  3. BLOCK_ID VARCHAR2(10 BYTE) PRIMARY KEY, --编码
  4. BLOCK_NAME VARCHAR2(200 BYTE) --资源名称
  5. );
  6. Insert into TEST_BLOCK_T (BLOCK_ID, BLOCK_NAME) Values ('1', 'java');
  7. COMMIT;

yc数据库:

  1. CREATE TABLE TEST_USER_T
  2. (
  3. USER_ID VARCHAR2(10 BYTE) PRIMARY KEY,
  4. NAME VARCHAR2(200 BYTE)
  5. );
  6. Insert into TEST_USER_T (USER_ID, NAME) Values ('1', '张三');
  7. COMMIT;

2、pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.example</groupId>
  6. <artifactId>demo</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <name>demo</name>
  9. <description>Demo project for Spring Boot</description>
  10.  
  11. <properties>
  12. <java.version>1.8</java.version>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  15. <spring-boot.version>2.1.17.RELEASE</spring-boot.version>
  16. </properties>
  17.  
  18. <dependencies>
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-web</artifactId>
  22. </dependency>
  23.  
  24. <dependency>
  25. <groupId>org.springframework.boot</groupId>
  26. <artifactId>spring-boot-starter-test</artifactId>
  27. <scope>test</scope>
  28. </dependency>
  29.  
  30. <!-- oracle驱动 -->
  31. <dependency>
  32. <groupId>com.oracle</groupId>
  33. <artifactId>ojdbc6</artifactId>
  34. <version>11.2.0.3</version>
  35. </dependency>
  36.  
  37. <!-- 集成druid -->
  38. <dependency>
  39. <groupId>com.alibaba</groupId>
  40. <artifactId>druid-spring-boot-starter</artifactId>
  41. <version>1.1.10</version>
  42. </dependency>
  43.  
  44. <!--集成mybatis-plus -->
  45. <dependency>
  46. <groupId>com.baomidou</groupId>
  47. <artifactId>mybatis-plus-boot-starter</artifactId>
  48. <version>2.1.9</version>
  49. </dependency>
  50.  
  51. <!-- aop切面 -->
  52. <dependency>
  53. <groupId>org.springframework.boot</groupId>
  54. <artifactId>spring-boot-starter-aop</artifactId>
  55. </dependency>
  56.  
  57. <!-- 省略get/set等方法 日志打印 -->
  58. <dependency>
  59. <groupId>org.projectlombok</groupId>
  60. <artifactId>lombok</artifactId>
  61. <optional>true</optional>
  62. </dependency>
  63.  
  64. </dependencies>
  65.  
  66. </project>

3、      application.properties配置

  1. # 应用名称
  2. spring.application.name=demo
  3. # 应用服务 WEB 访问端口
  4. server.port=8080
  5.  
  6. spring.aop.proxy-target-class=true
  7. spring.aop.auto=true
  8.  
  9. spring.datasource.druid.db1.url=jdbc:oracle:thin:@192.168.1.100:1521:orcl
  10. spring.datasource.druid.db1.username=zy
  11. spring.datasource.druid.db1.password=123
  12. spring.datasource.druid.db1.driver-class-name=oracle.jdbc.OracleDriver
  13. spring.datasource.druid.db1.initialSize=5
  14. spring.datasource.druid.db1.minIdle=5
  15. spring.datasource.druid.db1.maxActive=20
  16.  
  17. spring.datasource.druid.db2.url=jdbc:oracle:thin:@192.168.1.100:1521:orcl
  18. spring.datasource.druid.db2.username=yc
  19. spring.datasource.druid.db2.password=123
  20. spring.datasource.druid.db2.driver-class-name=oracle.jdbc.OracleDriver
  21. spring.datasource.druid.db2.initialSize=5
  22. spring.datasource.druid.db2.minIdle=5
  23. spring.datasource.druid.db2.maxActive=20
  24.  
  25. #开启sql打印
  26. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4、文件目录

主要源码:

MybatisPlusConfig.java

DataSourceSwitchAspect.java

5、源码

  1. package com.example.demo.config;
  2.  
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Before;
  6. import org.aspectj.lang.annotation.Pointcut;
  7. import org.springframework.core.annotation.Order;
  8. import org.springframework.stereotype.Component;
  9.  
  10. @Component
  11. @Order(value = -100)
  12. @Slf4j
  13. @Aspect
  14. public class DataSourceSwitchAspect {
  15.  
  16. @Pointcut("execution(* com.example.demo.mapper.db1..*.*(..))")
  17. private void db1Aspect() {
  18. }
  19.  
  20. @Pointcut("execution(* com.example.demo.mapper.db2..*.*(..))")
  21. private void db2Aspect() {
  22. }
  23.  
  24. @Before("db1Aspect()")
  25. public void db1() {
  26. log.info("切换到db1 数据源...");
  27. DbContextHolder.setDbType(DBTypeEnum.db1);
  28. }
  29.  
  30. @Before("db2Aspect()")
  31. public void db2() {
  32. log.info("切换到db2 数据源...");
  33. DbContextHolder.setDbType(DBTypeEnum.db2);
  34. }
  35.  
  36. }
  1. package com.example.demo.config;
  2.  
  3. public class DbContextHolder {
  4.  
  5. private static final ThreadLocal contextHolder = new ThreadLocal<>();
  6. /**
  7. * 设置数据源
  8. * @param dbTypeEnum
  9. */
  10. public static void setDbType(DBTypeEnum dbTypeEnum) {
  11. contextHolder.set(dbTypeEnum.getValue());
  12. }
  13.  
  14. /**
  15. * 取得当前数据源
  16. * @return
  17. */
  18. public static String getDbType() {
  19. return (String) contextHolder.get();
  20. }
  21.  
  22. /**
  23. * 清除上下文数据
  24. */
  25. public static void clearDbType() {
  26. contextHolder.remove();
  27. }
  28. }
  1. package com.example.demo.config;
  2.  
  3. public enum DBTypeEnum {
  4.  
  5. db1("db1"), db2("db2") ;
  6. private String value;
  7.  
  8. DBTypeEnum(String value) {
  9. this.value = value;
  10. }
  11.  
  12. public String getValue() {
  13. return value;
  14. }
  15. }
  1. package com.example.demo.config;
  2.  
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  4.  
  5. public class DynamicDataSource extends AbstractRoutingDataSource {
  6.  
  7. @Override
  8. protected Object determineCurrentLookupKey() {
  9. return DbContextHolder.getDbType();
  10. }
  11. }
  1. package com.example.demo.config;
  2.  
  3. import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
  4. import com.baomidou.mybatisplus.MybatisConfiguration;
  5. import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
  6. import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
  7. import org.apache.ibatis.plugin.Interceptor;
  8. import org.apache.ibatis.session.SqlSessionFactory;
  9. import org.apache.ibatis.type.JdbcType;
  10. import org.springframework.beans.factory.annotation.Qualifier;
  11. import org.springframework.boot.context.properties.ConfigurationProperties;
  12. import org.springframework.context.annotation.Bean;
  13. import org.springframework.context.annotation.Configuration;
  14. import org.springframework.context.annotation.Primary;
  15. import org.springframework.transaction.annotation.EnableTransactionManagement;
  16.  
  17. import javax.sql.DataSource;
  18. import java.util.HashMap;
  19. import java.util.Map;
  20.  
  21. @EnableTransactionManagement
  22. @Configuration
  23. public class MybatisPlusConfig {
  24.  
  25. @Bean
  26. public PaginationInterceptor paginationInterceptor() {
  27. PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
  28. paginationInterceptor.setLocalPage(true);
  29. return paginationInterceptor;
  30. }
  31.  
  32. @Bean(name = "db1")
  33. @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
  34. public DataSource db1() {
  35. return DruidDataSourceBuilder.create().build();
  36. }
  37.  
  38. @Bean(name = "db2")
  39. @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
  40. public DataSource db2() {
  41. return DruidDataSourceBuilder.create().build();
  42. }
  43.  
  44. /**
  45. * 动态数据源配置
  46. *
  47. * @return
  48. */
  49. @Bean
  50. @Primary
  51. public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
  52. @Qualifier("db2") DataSource db2) {
  53. DynamicDataSource dynamicDataSource = new DynamicDataSource();
  54. Map<Object, Object> targetDataSources = new HashMap<>();
  55. targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
  56. targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
  57. dynamicDataSource.setTargetDataSources(targetDataSources);
  58. dynamicDataSource.setDefaultTargetDataSource(db2);
  59. return dynamicDataSource;
  60. }
  61.  
  62. @Bean("sqlSessionFactory")
  63. public SqlSessionFactory sqlSessionFactory() throws Exception {
  64. MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
  65. sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
  66.  
  67. MybatisConfiguration configuration = new MybatisConfiguration();
  68. configuration.setJdbcTypeForNull(JdbcType.NULL);
  69. configuration.setMapUnderscoreToCamelCase(true);
  70. configuration.setCacheEnabled(false);
  71. sqlSessionFactory.setConfiguration(configuration);
  72. //添加分页功能
  73. sqlSessionFactory.setPlugins(new Interceptor[]{
  74. paginationInterceptor()
  75. });
  76. return sqlSessionFactory.getObject();
  77. }
  78.  
  79. }
  1. package com.example.demo.controller;
  2.  
  3. import com.example.demo.service.ManySourceService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.*;
  6.  
  7. @RestController
  8. @RequestMapping("/hello")
  9. public class ManySourceController {
  10.  
  11. @Autowired
  12. ManySourceService manySourceService;
  13.  
  14. @GetMapping("/getZyBlock")
  15. @ResponseBody
  16. public String test1() {
  17. return manySourceService.getZyBlock();
  18.  
  19. }
  20.  
  21. @GetMapping("/getYcUser")
  22. @ResponseBody
  23. public String test2() {
  24. return manySourceService.getYcUser();
  25.  
  26. }
  27.  
  28. @PostMapping("/insertZyBlock")
  29. @ResponseBody
  30. public String test3() {
  31. return manySourceService.insertZyBlock();
  32.  
  33. }
  34.  
  35. @PostMapping("/insertYcUser")
  36. @ResponseBody
  37. public String test4() {
  38. return manySourceService.insertYcUser();
  39.  
  40. }
  41.  
  42. @PostMapping("/insertMany")
  43. @ResponseBody
  44. public String test5() {
  45. return manySourceService.insertMany();
  46.  
  47. }
  48.  
  49. }
  1. package com.example.demo.domain.db1;
  2.  
  3. import com.baomidou.mybatisplus.annotations.TableId;
  4. import com.baomidou.mybatisplus.annotations.TableName;
  5.  
  6. @TableName(value = "TEST_BLOCK_T")
  7. public class Block {
  8. private static final long serialVersionUID = 1L;
  9.  
  10. @TableId
  11. private String blockId;
  12. /**
  13. * $field.comment。
  14. */
  15. private String blockName;
  16.  
  17. public String getBlockId() {
  18. return blockId;
  19. }
  20.  
  21. public void setBlockId(String blockId) {
  22. this.blockId = blockId;
  23. }
  24.  
  25. public String getBlockName() {
  26. return blockName;
  27. }
  28.  
  29. public void setBlockName(String blockName) {
  30. this.blockName = blockName;
  31. }
  32.  
  33. @Override
  34. public String toString() {
  35. return "TEST_BLOCK_T{" +
  36. "blockId='" + blockId + '\'' +
  37. ", blockName='" + blockName + '\'' +
  38. '}';
  39. }
  40. }
  1. package com.example.demo.domain.db2;
  2.  
  3. import com.baomidou.mybatisplus.annotations.TableId;
  4. import com.baomidou.mybatisplus.annotations.TableName;
  5.  
  6. @TableName(value = "TEST_USER_T")
  7. public class User {
  8. private static final long serialVersionUID = 1L;
  9.  
  10. @TableId
  11. private String userId;
  12. /**
  13. * $field.comment。
  14. */
  15. private String name;
  16.  
  17. public String getUserId() {
  18. return userId;
  19. }
  20.  
  21. public void setUserId(String userId) {
  22. this.userId = userId;
  23. }
  24.  
  25. public String getName() {
  26. return name;
  27. }
  28.  
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32.  
  33. @Override
  34. public String toString() {
  35. return "User{" +
  36. "userId='" + userId + '\'' +
  37. ", name='" + name + '\'' +
  38. '}';
  39. }
  40. }
  1. package com.example.demo.mapper.db1;
  2.  
  3. import com.baomidou.mybatisplus.mapper.BaseMapper;
  4. import com.example.demo.domain.db1.Block;
  5.  
  6. public interface BlockMapper extends BaseMapper<Block> {
  7.  
  8. }
  1. package com.example.demo.mapper.db2;
  2.  
  3. import com.baomidou.mybatisplus.mapper.BaseMapper;
  4. import com.example.demo.domain.db2.User;
  5.  
  6. public interface UserMapper extends BaseMapper<User> {
  7.  
  8. }
  1. package com.example.demo.service;
  2.  
  3. import com.example.demo.domain.db1.Block;
  4. import com.example.demo.domain.db2.User;
  5. import com.example.demo.mapper.db1.BlockMapper;
  6. import com.example.demo.mapper.db2.UserMapper;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. import org.springframework.transaction.annotation.Transactional;
  10.  
  11. @Service
  12. public class ManySourceService {
  13.  
  14. @Autowired
  15. BlockMapper blockMapper;
  16.  
  17. @Autowired
  18. UserMapper userMapper;
  19.  
  20. //获取zy库中的block中的数据
  21. public String getZyBlock() {
  22. return blockMapper.selectById("99999").toString();
  23.  
  24. }
  25.  
  26. //获取yc库中的user中的数据
  27. public String getYcUser() {
  28. return userMapper.selectById("2").toString() ;
  29. }
  30.  
  31. public String insertZyBlock() {
  32. Block block = new Block();
  33. block.setBlockId("99999");
  34. block.setBlockName("PHP");
  35. return blockMapper.insert(block)+"";
  36. }
  37.  
  38. public String insertYcUser() {
  39. User user = new User();
  40. user.setUserId("2");
  41. user.setName("李四");
  42. return userMapper.insert(user)+"";
  43. }
  44.  
  45. //@Transactional 加了事务会报错
  46. public String insertMany() {
  47. Block block = new Block();
  48. block.setBlockId("99999");
  49. block.setBlockName("PHP");
  50. blockMapper.insert(block) ;
  51.  
  52. User user = new User();
  53. user.setUserId("2");
  54. user.setName("李四");
  55. userMapper.insert(user) ;
  56. return "1";
  57. }
  58.  
  59. }
  1. package com.example.demo;
  2.  
  3. import org.mybatis.spring.annotation.MapperScan;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6.  
  7. @SpringBootApplication
  8. @MapperScan("com.example.demo.mapper.db*")
  9. public class DemoApplication {
  10.  
  11. public static void main(String[] args) {
  12. SpringApplication.run(DemoApplication.class, args);
  13. }
  14.  
  15. }

6、启动项目访问项目

清空yc库里的表TEST_USER_T 和 zy库里的表TEST_BLOCK_T

(1)访问http://localhost:8080/hello/insertZyBlock 成功插入数据到TEST_BLOCK_T

(2)访问http://localhost:8080/hello/insertYcUser成功插入数据到TEST_USER_T

说明分别向不同数据源的数据库的插入没有问题。

(3)访问http://localhost:8080/hello/getZyBlock

(4)访问http://localhost:8080/hello/getYcUser

说明分别从不同数据源的数据库的查询没有问题。

再次清空yc库里的表TEST_USER_T 和 zy库里的表TEST_BLOCK_T

(5)访问http://localhost:8080/hello/insertMany

一个方法中向TEST_USER_T 和TEST_BLOCK_T插入数据插入成功数据库里的数据生成了。

日志中看到同一个方法中向不同的数据库中插入数据程序会自动的切换数据源。

注:下面就不能加事务Transactional了,这里估计是我没有用对,如果有能解决的道友可以反馈一下。

//@Transactional 加了事务会报错
public String insertMany()

10、SpringBoot-mybatis-plus-druid多源数据的更多相关文章

  1. spring boot 学习(五)SpringBoot+MyBatis(XML)+Druid

    SpringBoot+MyBatis(xml)+Druid 前言 springboot集成了springJDBC与JPA,但是没有集成mybatis,所以想要使用mybatis就要自己去集成. 主要是 ...

  2. 12.SpringBoot+MyBatis(XML)+Druid

    转自:https://www.cnblogs.com/MaxElephant/p/8108342.html 主要是在Spring Boot中集成MyBatis,可以选用基于注解的方式,也可以选择xml ...

  3. 搭建Springboot+mybatis+redis+druid

    2019独角兽企业重金招聘Python工程师标准>>> 准备工作 JDK:1.8 使用技术:SpringBoot.Dubbo.Mybatis.Druid 开发工具:Intelj ID ...

  4. shardingsphere多数据源(springboot + mybatis+shardingsphere+druid)

    org.springframeword.boot:spring-boot-starer-web: 2.0.4release io.shardingsphere:sharding-jdbc-spring ...

  5. 基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建

    基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建 前言 最近做回后台开发,重新抓起以前学过的SSM(Spring+Sp ...

  6. SpringBoot+Mybatis+ Druid+PageHelper 实现多数据源并分页

    前言 本篇文章主要讲述的是SpringBoot整合Mybatis.Druid和PageHelper 并实现多数据源和分页.其中SpringBoot整合Mybatis这块,在之前的的一篇文章中已经讲述了 ...

  7. 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务

    文章来自: https://blog.csdn.net/qq_29242877/article/details/79033287 在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据 ...

  8. springboot+mybatis+druid+atomikos框架搭建及测试

    前言 因为最近公司项目升级,需要将外网数据库的信息导入到内网数据库内.于是找了一些springboot多数据源的文章来看,同时也亲自动手实践.可是过程中也踩了不少的坑,主要原因是我看的文章大部分都是s ...

  9. 【优雅写代码系统】springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码

    目录 spring基本搭建 整合mybatis pom配置 mybatis配置 设置数据源 设置sqlsessionfactory 设置扫描 设置开启事务 资源放行 测试 结果 思考&& ...

  10. SpringBoot 使用yml配置 mybatis+pagehelper+druid+freemarker实例

    SpringBoot 使用yml配置 mybatis+pagehelper+druid+freemarker实例 这是一个简单的SpringBoot整合实例 这里是项目的结构目录 首先是pom.xml ...

随机推荐

  1. 7-8次PTA和期末成绩总结

    (1)前言:总结之前所涉及到的知识点.题量.难度等情况 课程成绩统计程序-3在第二次的基础上修改了计算总成绩的方式(修改类结构,将成绩类的继承关系改为组合关系,成绩信息由课程成绩类和分项成绩类组成,课 ...

  2. [ABC309G] Ban Permutation

    Problem Statement Find the number, modulo $998244353$, of permutations $P=(P_1,P_2,\dots,P_N)$ of $( ...

  3. DHorse v1.5.0 发布,基于 k8s 的发布平台

    版本说明 新增特性 支持同一机器部署多个DHorse服务: 支持Next..NET应用部署: 优化Node.Nuxt应用构建和部署的性能: 默认使用fabric8客户端与k8s集群交互,可以通过指定参 ...

  4. 【2016】CloneCD和IsoBuster配合使用以提取VCD中的文件

    **笔记记录于:2016-11-24 ** 本文章仅供用于技术研究用途,请勿利用文章内容操作用于违反法律的事情. 起因: 公司老总让我提取下VCD中的文件以备份下,但是把光碟放进DVD光驱中发现只有几 ...

  5. NLP复习之N元文法

    N元文法的统计 二元概率方程: \[P(w_n|w_{n-1}) = \frac{C(w_{n-1}w_n)}{C(w_{n-1})} \] 三元概率估计方程: \[P(w_n|w_{n-2},w_{ ...

  6. pytest框架中conftest.py的作用

    conftest.py 是 pytest 框架中的一个特殊文件.它允许你为测试提供自定义的配置和钩子函数.以下是 conftest.py 的主要作用: 提供全局配置:你可以在 conftest.py ...

  7. NetCore高级系列文章04---async、await原理揭秘

    async.await本质上是C#提供的语法糖,编译器编译后是状态机的调用. 先看如下的一段代码,要main方法中调用了三个await方法 将此dll进行反编译为4.0的代码如下: 可见到两个Main ...

  8. Python——第五章:模块(Module)、自定义模块、第3方开源模块、包(Package)

    什么是模块(Module)? 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护. 为了编写可维护的代码,我们把很多代码按功能分组,分别放到不同的文件里,这 ...

  9. 云图说|应用编排服务AOS,助力应用上云自动化

    如今,企业想要扩大业务.进行创新,上云已经成为了必经之路.应用编排服务AOS为企业提供应用上云的自动化能力,支持编排华为云上的主流云服务,将复杂的云服务资源配置和应用部署配置通过模板描述,从而实现在华 ...

  10. Materialize MySQL引擎:MySQL到Click House的高速公路

    摘要: MySQL到ClickHouse数据同步原理及实践 引言 熟悉MySQL的朋友应该都知道,MySQL集群主从间数据同步机制十分完善.令人惊喜的是,ClickHouse作为近年来炙手可热的大数据 ...