• MySQL搭建主从复制集群,实现读写分离


     目录

    一、准备

    二、配置

    2.1 配置主库

    修改配置文件/etc/my.cnf

    重启服务

    为主库再创建一个账户并授权

    查看状态

    2.2 配置从库

    修改配置文件/etc/my.cnf

    重启mysql服务

    配置需要同步的主机

    启动salve同步

    查看是否同步

    三、测试主从复制是否生效

    四、读写分离案例

    4.1 背景

    4.2 Sharding-JDBC介绍

    4.3 项目测试前期准备

    数据库准备

    导入依赖

    配置文件

    代码生成器

    添加依赖

    添加配置文件规则

    4.4 验证

    创建测试方法,包含增删改查

    打上断点,以DEBUG的方式启动

    发送请求测试

    五、主从复制数据不同步的问题



    一、准备

    准备两台服务器,为每台都安装好mysql。

    此时的两台服务器它们的mysql之间没有半毛钱关系,各自是独立的。

    如果不会安装,后续我也会出一篇安装教程。请关注我的【编程环境安装】专栏。

    二、配置

    2.1 配置主库

    修改配置文件/etc/my.cnf

    追加如下:

    1. [mysqld]
    2. log-bin=mysql-bin
    3. server-id=110

    以后在进行增删改操作的时候,它都会进行记录日志。

    重启服务

    systemctl restart mysqld

    为主库再创建一个账户并授权

    1. CREATE USER '主库用户名'@'%' IDENTIFIED BY '主库用户名密码';
    2. GRANT REPLICATION SLAVE ON *.* TO '主库名称'@'%';
    3. ALTER USER '主库用户名'@'%' IDENDIFIED WITH mysql_native_password BY '主库用户名密码';
    4. flush privileges;

     这一步是为主库创建一个用户,并为这个用户授予复制从库的权限。

    查看状态

    我们先记住这个File和Position的值

    记得现在就不要再在主库操作了,否则这个位置就会发生变化。

    2.2 配置从库

    修改配置文件/etc/my.cnf

    增加配置:服务器的唯一标识

    server-id=111

     

    重启mysql服务

    systemctl restart mysqld

     

    配置需要同步的主机

    1. CHANGE MASTER TO
    2. MASTER_HOST='主机的ip地址',
    3. MASTER_USER='主机刚才创建的用户名',
    4. MASTER_PASSWORD='主机用户名的密码',
    5. MASTER_LOG_FILE='主机上记录的File值',
    6. MASTER_LOG_POS=主机上记录的Position值;

     

    启动salve同步

    start slave;

    查看是否同步

    show slave status;

     很乱啊,我们把它复制到文本编辑器上来看看

    至此,主从复制集群就搭建完成了!

    三、测试主从复制是否生效

    我们通过navicat连接上这两个数据库,通过操作看看,它们的变化。

    在主库创建一个数据库。

    四、读写分离案例

    4.1 背景

    如果系统的访问量增大,既要查询又要写入,单台数据库已经不能满足访问压力了。

    这个时候就可以考虑主从复制。将数据库拆分成主库和从库。主库主要负责处理事务性的增删改操作,从库负责处理查询操作。

    但是我们的系统怎么知道,我们的操作是查询操作,还是增删改操作?又怎么根据操作的类型选择主库还是从库呢?

    这个时候就用到了Sharding-JDBC!

    4.2 Sharding-JDBC介绍

    Sharding-JDBC定位为轻量级的Java框架,在Java的jdbc层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可以理解为增强版的jdbc驱动,完全兼容jdbc和各种orm框架。

    • 适用于任何基于JDBC的ORM框架,如:JPA,Hibernate,Mybatis,Spring JDBC Template或直接使用JDBC。

    • 支持任何第三方的数据库连接池,如:DBCP,C3P0,Druid,HikariCP等。

    • 支持任意实现JDBC规范的数据库。目前支持Mysql,Oracle,SQL server,PostgreSQL以及任何遵顼SQL92标准的数据库。

    4.3 项目测试前期准备

    在使用分库分表之前,先要搭建好主从复制的数据库。

    然后我们先创建一个web项目:

    数据库准备

    1. CREATE TABLE `user` (
    2. `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
    3. `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '姓名',
    4. `age` tinyint DEFAULT NULL COMMENT '年龄',
    5. `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '住址',
    6. PRIMARY KEY (`id`)
    7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户'

     

    导入依赖

    作为web项目基本的一些依赖,没什么好讲的。

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starterartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-testartifactId>
    8. <scope>testscope>
    9. dependency>
    10. <dependency>
    11. <groupId>org.springframework.bootgroupId>
    12. <artifactId>spring-boot-starter-webartifactId>
    13. dependency>
    14. <dependency>
    15. <groupId>org.projectlombokgroupId>
    16. <artifactId>lombokartifactId>
    17. dependency>
    18. <dependency>
    19. <groupId>com.alibabagroupId>
    20. <artifactId>fastjsonartifactId>
    21. <version>1.2.76version>
    22. dependency>
    23. <dependency>
    24. <groupId>com.baomidougroupId>
    25. <artifactId>mybatis-plus-boot-starterartifactId>
    26. <version>3.5.1version>
    27. dependency>
    28. <dependency>
    29. <groupId>com.baomidougroupId>
    30. <artifactId>mybatis-plus-generatorartifactId>
    31. <version>3.5.1version>
    32. dependency>
    33. <dependency>
    34. <groupId>org.apache.velocitygroupId>
    35. <artifactId>velocity-engine-coreartifactId>
    36. <version>2.1version>
    37. dependency>
    38. <dependency>
    39. <groupId>mysqlgroupId>
    40. <artifactId>mysql-connector-javaartifactId>
    41. dependency>
    42. <dependency>
    43. <groupId>com.alibabagroupId>
    44. <artifactId>druid-spring-boot-starterartifactId>
    45. <version>1.2.12version>
    46. dependency>

    配置文件

    目前只配置了端口号和mybatisplus的相关配置

    连数据源连接我们都没有配置,这是因为后面要使用sharding-jdbc

    1. server.port=8080
    2. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    3. mybatis-plus.global-config.db-config.logic-delete-value=1
    4. mybatis-plus.global-config.db-config.logic-not-delete-value=0
    5. mybatis-plus.configuration.map-underscore-to-camel-case=true
    6. mybatis-plus.global-config.db-config.id-type=assign_id

    代码生成器

    使用代码生成器,快速生成刚才创建的表的controller、entity、service、mapper等文件。

     

    这些做好以后,下面开始使用到sharding-jdbc!

    添加依赖

    1. <dependency>
    2. <groupId>org.apache.shardingspheregroupId>
    3. <artifactId>sharding-jdbc-spring-boot-starterartifactId>
    4. <version>4.0.0-RC1version>
    5. dependency>

    添加配置文件规则

    1. # 定义数据源的名字,有几个填写几个,通过.隔开,名字随便取,但是后面配置主从数据源需要根据此名字进行设置
    2. spring.shardingsphere.datasource.names=master-haha,slave-haha
    3. # 数据源1
    4. spring.shardingsphere.datasource.master-haha.type=com.alibaba.druid.pool.DruidDataSource
    5. spring.shardingsphere.datasource.master-haha.driver-class-name=com.mysql.cj.jdbc.Driver
    6. spring.shardingsphere.datasource.master-haha.url=jdbc:mysql://192.168.17.110:3306/db_test1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    7. spring.shardingsphere.datasource.master-haha.username=root
    8. spring.shardingsphere.datasource.master-haha.password=123456
    9. # 数据源2
    10. spring.shardingsphere.datasource.slave-haha.type=com.alibaba.druid.pool.DruidDataSource
    11. spring.shardingsphere.datasource.slave-haha.driver-class-name=com.mysql.cj.jdbc.Driver
    12. spring.shardingsphere.datasource.slave-haha.url=jdbc:mysql://192.168.17.111:3306/db_test1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    13. spring.shardingsphere.datasource.slave-haha.username=root
    14. spring.shardingsphere.datasource.slave-haha.password=123456
    15. # 配置从库负载均衡策略:轮询。就是说从库实际不一定只有一台,当每次查询操作进来的时候,轮询去查每台从库。
    16. spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
    17. # 设置最终数据源的名称 其实就是spring中bean对象的名称
    18. spring.shardingsphere.masterslave.name=dataSource
    19. # 指定主库数据源名称
    20. spring.shardingsphere.masterslave.master-data-source-name=master-haha
    21. # 指定从库数据源名称 从库如果有多个,通过逗号隔开
    22. spring.shardingsphere.masterslave.slave-data-source-names=slave-haha
    23. # 开启控制台的sql显示,默认是false
    24. spring.shardingsphere.props.sql.show=true

    以上规则定义好了,它查询就会去从库(每台从库轮询查),增删改去主库。

    但是,最后还需要修改一下spring的bean覆盖策略,默认是不允许同名bean的。

    为什么要修改呢?因为我们定义的bean的名称和druid中定义的bean的名字重复了。

    而spring中默认不允许同名的bean的。

    所以需要在配置文件中再加上一段配置

    1. # 允许bean定义覆盖,后创建的bean会覆盖前面的同名bean对象
    2. spring.main.allow-bean-definition-overriding=true

    否则启动项目会报错

     ok,下面我们创建一些测试方法来验证一下。

    4.4 验证

    创建测试方法,包含增删改查

    注意这里引入了一个DataSource对象,虽然实际业务代码并没有使用到,但是我们只是想看看它到底是谁。

    1. @RestController
    2. @RequestMapping("/user")
    3. @Slf4j
    4. public class UserController {
    5. @Autowired
    6. private DataSource dataSource;
    7. @Autowired
    8. private UserService userService;
    9. @PostMapping
    10. public User save(User user){
    11. userService.save(user);
    12. return user;
    13. }
    14. @DeleteMapping("/{id}")
    15. public void delete(@PathVariable Long id){
    16. userService.removeById(id);
    17. }
    18. @PutMapping
    19. public User update(User user){
    20. userService.updateById(user);
    21. return user;
    22. }
    23. @GetMapping("/{id}")
    24. public User getById(@PathVariable Long id){
    25. User user = userService.getById(id);
    26. return user;
    27. }
    28. @GetMapping("/list")
    29. public List list(User user){
    30. LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    31. queryWrapper.eq(user.getId() != null,User::getId,user.getId());
    32. queryWrapper.eq(user.getName() != null,User::getName,user.getName());
    33. List list = userService.list(queryWrapper);
    34. return list;
    35. }
    36. }

    打上断点,以DEBUG的方式启动

    断点的位置我们如图

    发送请求测试

    我们可以通过postman来发送请求试试,我这里使用apifox来测试。

    先来查询一下

     我们发现这个datasource是shardingjdbc提供的

    接着放行该请求

    我们通过控制台看到,这条sql是从库去查询的!

    ok,再发送添加请求试试。

    我们直接放行,发现添加操作是主库执行的。

    ok,下面我们再检查一下数据库

    ok,主库也同步到了从库了。

    五、主从复制数据不同步的问题

    案例:误操作向从库添加了数据。

    我通过navicat向从库的user表中生成假数据1w条,这肯定不会同步到主库的。

    因为是主库写,从库读。不能乱来。

    这就导致从库出现问题。我们通过show slave status;命令查看从库是否同步,发现sql转储线程停掉了。那肯定就不能继续同步数据了。

    这个时候解决办法就是重新同步。具体方法时间关系就不再赘述了。附上思维导图:

    MySQL主从复制那些事儿| ProcessOn免费在线作图,在线流程图,在线思维导图

    文章花费了我大量心血和时间,

    吃水不忘挖井人,如果对你有帮助,别忘了三连,点赞收藏评论 ~ 

  • 相关阅读:
    软件测试(一)
    python连接自己的机器人接口在微信聊天
    iceberg简介004_iceberg和其他数据湖框架的对比---​​数据湖Apache Iceberg工作笔记0004
    社交电商:流量红利已尽,裂变营销是最低成本的获客之道
    Window下Mysql8.0 怎样恢复被删除的Root
    【C/C++】全排列及素数环问题
    如何为预先数据库创建自定义SQL Server复制
    从0开始学杂项 第三期:隐写分析(2) PNG图片隐写
    23.CF911G Mass Change Queries 动态开点权值线段树+线段树合并
    第14章总结:lambda表达式与处理
  • 原文地址:https://blog.csdn.net/YuanFudao/article/details/132894623