单一架构

  • 一个应用服务器和一个数据库

image.png
缺点:如果出现硬件故障或自然灾害可能会导致数据全部丢失,造成一定的损失

定时备份数据?
如果数据库存储的数据太多,比如 G 级别的数据,每次备份耗时很长且备份要占用大量的空间

主从架构

了解

也叫主备架构

  • master/slave
  • 或 master/backup

使用场景:从节点仅用来做备份,不对外提供服务
image.png
原理:主节点使用 binlog 记录操作日志,将其同步给从节点进行备份(单向异步)

菊花链:每个节点也可以有从节点,树状结构,高度无限制

小实验

Mysql:8.0
OS:ubuntu 20.04

准备

配置每个节点的同步信息,保证 server-id 唯一
配置文件:/etc/mysql/mysql.conf.d/mysqld.cnf
比如主节点的配置:
image.png
从节点配置一样,只用保证 server-id 两两不相同即可

主节点

查看 server-id 是否配置成功
mysql> show variables like 'server_id';
image.png

查看作为主节点时的节点状态
mysql> show master status;
image.png
File 和 Position 两个属性在从节点上要用到(告诉从节点从哪里开始同步)

从节点

指定要同步的主节点信息,包括主机信息、连接信息、文件、开始位置

  1. change master to
  2. master_host='192.168.241.128',
  3. master_user='root',
  4. master_password='mysql123!ROOT',
  5. master_log_file='mysql-bin.000001',
  6. master_log_pos=157;

image.png

开启同步
mysql> start slave;

  • mysql> stop slave;

查看从节点状态
mysql> show slave status\G;
image.png

非上图结果,且 Slave_IO_Running 为 NO,且出错:Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work. 原因:虚拟机克隆导致多个机器的 /var/lib/mysql/auto.cnf文件相同,而这个文件记录 server-uuid,多机的 uuid 不能相同,应该把该文件删掉,再次启动 mysql 时会自动生成 操作:删掉“克隆机”中的 /var/lib/mysql/auto.cnf,重启 mysql,发现每个机器又生成了一个 auto.cnf 且内容不同 注意:重启 mysql 不用再配置上边的【指定主节点信息】和【开始同步】 配置完后,重新启动,成功

验证

在主节点(192.168.241.128)上创建数据库、创建表、添加记录
image.png

查看从节点(192.168.241.129),发现他已经同步成功
image.png

如果尝试修改从节点,发现并不能同步到主节点

MySQL集群

主从架构扩展与 MySQL 集群:https://blog.csdn.net/qq_45453266/article/details/113871739
MySQL 各集群方案:https://www.cnblogs.com/lgx211/p/12456859.html

读写分离架构

使用MyCat实现读写分离

基于上边的【主从架构】

MyCat 官网:http://www.mycat.org.cn/(mycat2)
使用 mycat 1.6 http://www.mycat.org.cn/mycat1.html(目前已有 mycat2,功能更丰富;mycat1 比较稳定)
⭐MyCat 1 wiki:https://github.com/MyCATApache/Mycat-Server/wiki

读写分离架构:让从库分担一部分服务,减轻主节点的并发压力。一般来说读操作由从节点来处理,写操作由主节点处理。

准备两台 mysql 5.7,且配置成主从架构(master:192.168.241.128,slave:192.168.241.129)
下载 mycat 1.6.x,解压
修改 schema.xml

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <!--定义mycat逻辑库,dataNode属性记录数据节点名(一个标签)-->
  5. <schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="300" dataNode="myDataNode"></schema>
  6. <!--定义数据节点,name属性即数据节点的名字,dataHost属性记录真实主机名(一个标签),-->
  7. <!-- database属性记录真实的库,即我们的库(这里product是一个商品库)-->
  8. <dataNode name="myDataNode" dataHost="myDataHost" database="product" />
  9. <!--定义数据主机,name属性即数据主机的名字-->
  10. <dataHost name="myDataHost" maxCon="1000" minCon="10" balance="1"
  11. writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100">
  12. <!--心跳检测-->
  13. <heartbeat>select user()</heartbeat>
  14. <!--主节点-->
  15. <writeHost host="hostMaster1" url="192.168.241.128:3306" user="root" password="mysql123!ROOT">
  16. <!--从节点-->
  17. <readHost host="hostSlave1" url="192.168.241.129:3306" user="root" password="mysql123!ROOT" />
  18. </writeHost>
  19. </dataHost>
  20. </mycat:schema>

修改 server.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mycat:server SYSTEM "server.dtd">
  3. <mycat:server xmlns:mycat="http://io.mycat/">
  4. <system>
  5. <property name="defaultSqlParser">druidparser</property>
  6. <property name="charset">utf8</property>
  7. </system>
  8. <!--配置用户信息,name属性指定用户名,password属性指定密码,schemas指定schemas.xml中定义的schema.name-->
  9. <user name="root">
  10. <property name="password">mycat123!ROOT</property>
  11. <property name="schemas">mycatdb</property>
  12. </user>
  13. </mycat:server>

编写业务类(注意声明式事务传播策略的使用)

  1. @Service
  2. public class ProductService {
  3. @Autowired
  4. ProductMapper productMapper;
  5. /*
  6. 这个业务方法不使用事务,会去从库查询。
  7. 如何验证?修改从库数据,使其区别于主库,查询数据,看来自哪里
  8. 注:如果使用默认的REQUIRED传播策略,则会查询主库。因为会使用事务,被判定为可能包含修改操作
  9. */
  10. @Transactional(propagation = Propagation.SUPPORTS)
  11. public List<Product> findAll() {
  12. return productMapper.findAll();
  13. }
  14. /*
  15. 这个业务方法会使用事务,且为 update 语句,会去主库操作
  16. 如何验证?主从库都发生了这个修改,说明修改的是主库
  17. */
  18. @Transactional(propagation = Propagation.REQUIRED)
  19. public int decreaseStore(Integer id) {
  20. return productMapper.decreaseStore(id);
  21. }
  22. }

测试查询和修改

  1. @SpringBootTest(classes = Main.class)
  2. @RunWith(SpringRunner.class)
  3. public class T {
  4. @Autowired
  5. ProductService productService;
  6. @Test
  7. public void t1() {
  8. productService.findAll().forEach(System.out::println);
  9. }
  10. @Test
  11. public void t2() {
  12. System.out.println(productService.decreaseStore(1));
  13. }
  14. }

注意点 1:需要使用 5.x 版本的 mysql 驱动(MySQL驱动版本和MySQL版本最好都是 5.x,不然很难配)
image.png
注意点 2:配置连接信息时,需指定 mycat 地址,其端口号为 8066,数据库为 schema.name

  1. spring:
  2. datasource:
  3. type: com.alibaba.druid.pool.DruidDataSource
  4. url: jdbc:mysql://192.168.241.130:8066/mycatdb?characterEncoding=UTF-8&useSSL=false
  5. driver-class-name: com.mysql.jdbc.Driver
  6. username: root
  7. password: mycat123!ROOT
  8. mybatis:
  9. mapper-locations: classpath:/mybatis/mapper/*Mapper.xml
  10. type-aliases-package: im.engure.entity

原本我的机器上装的是 mysql 8,但是 mysql 1.6 默认使用 5.x 版本的驱动,mycat 连接 mysql 8.x 产生了很多的兼容性问题 于是在一台 docker 上搭建了两台 5.7 版本的 mysql,用他们两个搭建主从复制架构。思路:使用 —link 建立容器关联,部分命令如下图

  1. 创建两个容器。第一个为 master,第二个为 slave,且后者依赖前者

image.png

  1. 在 salve 上指定 master,不通过 ip 指定(写死了),而是通过容器名指定(因为上边用了 —link=xxx)

image.png

源代码:https://gitee.com/engureguo/mybatis-cache-with-redis 的 check-mycat