SpringCloud进阶--MySQL主从复制、分库分表、读写分离的实现
注释掉 mysqlx-bind-address 127.0.0.1然后重启MySQL服务。配置主库给刚才创建的用户分配一个主从复制的权限即可grant replication slave on *.* to test; flush privileges;这时主库就搭建完成了然后开始进行从库配置配置从库配置文件# 这里需要将server-id配置为其他的值默认1所有MySQL主从实例的id必须唯一不然会开启失败 server-id 2进入数据库输入change replication source to SOURCE_HOST192.168.0.8,SOURCE_USERtest,SOURCE_PASSWORD123456,SOURCE_LOG_FILEBINLOG.000004,SOURCE_LOG_POS591SOURCE_HOST -- 主库地址SOURCE_LOG_FILE-- 二进制日志就是主库的日志文件在主库输入 show master status 可以查到SOURCE_LOG_POS -- 日志的偏移地址在主库输入 show master status 可以查到执行完后输入start replica;这时从库就启动了!输入show replica status\G;最关键的就是Replica_IO_Running和Replica_SQL_Running必须同时为yes才可以实际上从库会创建2个线程一个线程复制与主库进行通信获取二进制日志暂时存放到一个中间表Relay_Log中另一个线程将中间表保存的二进制日志信息进程执行然后插入到从库中。此时就配置完成了。在主库创建一个数据库从库就会跟着创建一个数据库.那要是主库挂了会怎么样?这时IO线程处于重连状态,等待主库恢复运行.主库恢复之后,它的日志表名变了,此时需要重新配置从库,不然从库使用的还是之前的错误日志表.分库分表大型互联网系统中,单台MySQL的存储容量无法满足需求,这时就需要扩容.我们通过多台实例进行扩容,可以将数据分散,让多台主机共同保存数据.那么如何进行拆分?垂直拆分:将数据库中的所有表,按照业务功能拆分到各个数据库中,而对于一张表,也可以通过外键之类的机制,将其拆分成多个表.水平拆分:水平拆分针对的是数据,不是表.让多个具有相同表的数据库协同存放一部分数据,相当于把数据分散到各个节点上.实现这样的拆分操作,目前已经有一些解决方案了,比如mycat(数据库中间件,相当于挂了一层代理,再通过mycat进行分库分表操作数据库,只需要连接就能使用)或者Sharding JDBC(应用程序中字节对sql语句进行解析,然后转换成分库分表操作,需要自己编写一些逻辑代码),这里以Sharding JDBC为例!Sharding JDBCSharding-JDBC 它定位为轻量级Java框架在Java的JDBC层提供的额外服务。它使用客户端直连数据库以jar包形式提供服务无需额外部署和依赖可理解为增强版的JDBC驱动完全兼容JDBC和各种ORM框架。适用于任何基于JDBC的ORM框架如JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。支持任何第三方的数据库连接池如DBCP, C3P0, BoneCP, Druid, HikariCP等。支持任意实现JDBC规范的数据库。目前支持MySQLOracleSQLServerPostgreSQL以及任何遵循SQL92标准的数据库。分库实现具体实现水平拆分步骤如下:新建一个spring boot项目.先导入依赖:dependency groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core-spring-boot-starter/artifactId version5.1.0/version /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version4.0.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency编写配置文件spring: shardingsphere: datasource: # 有几个数据源就写几个名称数字的形式 names: ds0,ds1 # 每个数据源的具体配置 ds0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/yyds?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneUTC username: root password: 123456 ds1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/yyds?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneUTC username: root password: 123456编写实体类和Mapper接口AllArgsConstructor Data public class User { int id; String name; String password; }Mapper public interface UserMapper { Select(select * from user where id #{id}) User getUserById(int id); Insert(insert into user (id, name, password) values (#{id}, #{name}, #{password})) int addUser(User user); }这些都是常规操作,编写代码的关注点依然放在业务上.现在需要编写配置文件,告诉Sharding JDBC要如何进行分片.(现在是两个数据库都有user表,我们的目标是将用户信息分别存放在这两个数据库中)spring: shardingsphere: rules: sharding: tables: # 这里填写表名程序中对这张表的所有操作 都会采用下面的路由方案 user: # 实际的路由数据节点我们要分两个库每个库一个表所以是两个数据节点格式是 数据源名称.表名 # 也可以使用表达式的方式来写比如ds$-{0..1}.user actual-data-nodes: ds0.user,ds1.user # 分库策略配置 database-strategy: # 这里选择标准的分库策略也可以配置复杂的分库策略具体可以参考官方文档 standard: # 参与分片运算的字段通常是主键下面的算法会根据这个字段的值来决定路由到哪个库 sharding-column: id # 选择分片算法下面我们会自定义一个分片算法命名为my-alg sharding-algorithm-name: my-alg sharding-algorithms: # 自定义的分片算法命名为my-alg my-alg: # 算法类型(更多类型可以查询官方文档)这里选择MOD算法MOD算法会根据分片字段的值对分片数量根据2取模来决定路由到哪个库 type: MOD props: shard-count: 2 props: # 开启SQL日志输出方便我们观察SQL路由的情况 sql-show: true这时就完成分库了,然后接可以进行业务开发了.分表实现和查询假如数据库中有test_01 和test_02两张表,表结构一样.我们希望能根据id取模运算的结果分别放到这两个不同的表中.首先需要明白两个概念:什么是逻辑表,什么是真实表逻辑表:相同结构的水平拆分数据库(表)的逻辑名称,是sql中表的逻辑标识,例如:订单数据根据主键拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名就是t_order真实表:真实存在的表,就是上面的t_order_0到t_order_9实现的时候,不用修改任何业务逻辑代码,只需要做相应的配置即可:spring: shardingsphere: rules: sharding: tables: test: actual-data-nodes: ds0.test$-{0..1} # 配置分表策略 table-strategy: standard: sharding-column: id sharding-algorithm-name: my-alg sharding-algorithms: my-alg: # INLINE方式自定义表达式来决定路由到哪个库表达式中可以使用分片字段的值来进行计算 type: INLINE props: algorithm-expression: test$-{id % 2} # 查询也可以根据分片策略来进行如果使用的是范围查询依然会进行全量查询,这里先关闭 allow-range-query-with-inline-sharding: false查询的时候,如果是根据id获取一行数据,实际上是去两张表里面查.如果是范围查询,也就是返回结果是一个list集合的查询,需要配置allow-range-query-with-inline-sharding: true实际执行的sql是把两张表的查询结果union all起来,比如:select * from test where id 10 -- 就是执行下面的sql select * from test_0 where id 10 union all select * from test_1 where id 10分布式序列算法在复杂分布式系统中,特别是微服务架构中,需要对大量数据和消息进行唯一标识.随着数据增多,分库分表成为常见方案,对数据分库分表后需要有一个唯一id来标识一条数据或消息(如订单号,事件编号等)此时需要一个能够生成全局唯一id的算法。这个id有以下要求必须全局唯一尽可能保持有效这样会提升索引的查询效率满足以上需求的方案有uuid是一组32位数的16禁止数字随机构成全局唯一 但无需。雪花算法Snowflake:生成一个64bit大小的整型id需要使用Long类型。int类型装不下雪花算法虽然具备了唯一性和有序性但是依然有缺点第一就是时间问题如果机器时间出现倒退那么就会导致生成重复的id并且节点容量只有1024个如果是超大规模集群也存在隐患。这里让Sharding JDBC用雪花算法为我们生成主键先把表中的主键字段类型改为bigint修改配置文件添加对应配置spring: shardingsphere: rules: sharding: tables: user: actual-data-nodes: ds0.user,ds1.user # 分库策略配置 database-strategy: # 这里选择标准的分库策略也可以配置复杂的分库策略具体可以参考官方文档 standard: # 参与分片运算的字段通常是主键下面的算法会根据这个字段的值来决定路由到哪个库 sharding-column: id # 选择分片算法下面我们会自定义一个分片算法命名为my-alg sharding-algorithm-name: my-alg key-generate-strategy: column: id key-generator-name: my-gen key-generators: # 自定义的主键生成算法命名为my-gen my-gen: # 算法类型这里选择SNOWFLAKE算法SNOWFLAKE算法会生成全局唯一的ID适合分布式环境 type: SNOWFLAKE props: # SNOWFLAKE算法需要配置一个worker-id通常是一个唯一的数字可以根据实际情况来设置确保在分布式环境中不同的实例有不同的worker-id worker-id: 666 sharding-algorithms: # 自定义的分片算法命名为my-alg my-alg: # 算法类型这里选择MOD算法MOD算法会根据分片字段的值对分片数量取模来决定路由到哪个库 type: MOD props: shard-count: 2 props: # 开启SQL日志输出方便我们观察SQL路由的情况 sql-show: true此时就完成了主键生成策略设置读写分离之前实现的MySQL主从复制模式我们可以将主库作为写操作从库作为只读操作在实现了主从复制模式的基础上进行配置在从库的配置文件中设置 read-only 1,这样从库就只能读数据了。然后重启从库配置Sharding JDBCspring: shardingsphere: rules: # 配置读写分离 readwrite-splitting: data-sources: #名称随便写 user-db: type: static props: # 配置写库只能写一个 write-data-source-name: ds0 # 配置读库可以配置多个逗号分隔 read-data-source-names: ds1 load-balancer-name: my-load load-balancers: # 自定义的负载均衡算法命名为my-load my-load: type: round_robin