分库分表实战--- ShardingSphere实战 第一部分

前言 实战背景介绍

背景描述

  • 刚开始我们的系统只用了单机数据库
  • 随着用户的不断增多,考虑到系统的高可用和越来越多的用户请求,我们开始使用数据库主从架构
  • 当用户量级和业务进一步提升后,写请求越来越多,这时我们开始使用了分库分表

遇到的问题

  • 用户请求量太大
    单服务器TPS、内存、IO都是有上限的,需要将请求打散分布到多个服务器
  • 单库数据量太大
    单个数据库处理能力有限;单库所在服务器的磁盘空间有限;单库上的操作IO有瓶颈
  • 单表数据量太大
    查询、插入、更新操作都会变慢,在加字段、加索引、机器迁移都会产生高负载,影响服务

如何解决

  • 垂直拆分

    • 垂直分库
      微服务架构时,业务切割得足够独立,数据也会按照业务切分,保证业务数据隔离,大大提升了数据库的吞吐能力
      在这里插入图片描述
    • 垂直分表
      表中字段太多且包含大字段的时候,在查询时对数据库的IO、内存会受到影响,同时更新数据时,产生的binlog文件会很大,MySQL在主从同步时也会有延迟的风险
      在这里插入图片描述
  • 水平拆分

    • 水平分表
      针对数据量巨大的单张表(比如订单表),按照规则把一张表的数据切分到多张表里面去。但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈。
      在这里插入图片描述
    • 水平分库
      将单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。 水平分库分表能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等的瓶颈
      在这里插入图片描述
  • 水平分库规则
    不跨库、不跨表,保证同一类的数据都在同一个服务器上面。
    数据在切分之前,需要考虑如何高效的进行数据获取,如果每次查询都要跨越多个节点,就需要谨慎使用。

  • 水平分表规则

    • RANGE
      时间:按照年、月、日去切分。例如order_2020、order_202005、order_20200501
      地域:按照省或市去切分。例如order_beijing、order_shanghai、order_chengdu
      大小:从0到1000000一个表。例如1000001-2000000放一个表,每100万放一个表

    • HASH
      用户ID取模
      不同的业务使用的切分规则是不一样,就上面提到的切分规则

    • 站内信
      用户维度:用户只能看到发送给自己的消息,其他用户是不可见的,这种情况下是按照用户ID hash分库,在用户查看历史记录翻页查询时,所有的查询请求都在同一个库内

    • 用户表

      • 范围法:以用户ID为划分依据,将数据水平切分到两个数据库实例,如:1到1000W在一张表,1000W到2000W在一张表,这种情况会出现单表的负载较高
      • 按照用户ID HASH尽量保证用户数据均衡分到数据库中
        如果在登录场景下,用户输入手机号和验证码进行登录,这种情况下,登录时是
        不是需要扫描所有分库的信息?
        最终方案:用户信息采用ID做切分处理,同时存储用户ID和手机号的映射的关系
        表(新增一个关系表),关系表采用手机号进行切分。可以通过关系表根据手机
        号查询到对应的ID,再定位用户信息。
    • 流水表
      时间维度:可以根据每天新增的流水来判断,选择按照年份分库,还是按照月份分库,甚至也可以按照日期分库

    • 订单表
      求职者(下面统称C端用户)投递企业(下面统称B端用户)的职位产生的记录称
      之为订单表。在线上的业务场景中,C端用户看自己的投递记录,每次的投递到了哪个状态,B端用户查看自己收到的简历,对于合适的简历会进行下一步沟通,同一个公司内的员工可以协作处理简历。
      如何能同时满足C端和B端对数据查询,不进行跨库处理?
      最终方案:为了同时满足两端用户的业务场景,采用空间换时间,将一次的投递记录存为两份,C端的投递记录以用户ID为分片键,B端收到的简历按照公司ID为分片键

      在这里插入图片描述

  • 主键选择

    • UUID:本地生成,不依赖数据库,缺点就是作为主键性能太差
    • SNOWFLAKE:百度UidGenerator、美团Leaf、基于SNOWFLAKE算法实现
  • 数据一致性

    • 强一致性:XA协议
    • 最终一致性:TCC、saga、Seata
  • 数据库扩容

    • 成倍增加数据节点,实现平滑扩容
    • 成倍扩容以后,表中的部分数据请求已被路由到其他节点上面,可以清理掉
  • 业务层改造

    • 基于代理层方式:Mycat、Sharding-Proxy、MySQL Proxy
    • 基于应用层方式:Sharding-jdbc
  • 分库后面临的问题

    • 事务问题:一次投递需要插入两条记录,且分布在不同的服务器上,数据需要保障一致性。
    • 跨库跨表的join问题
      • 全局表(字典表):基础数据/配置数据,所有库都拷贝一份
      • 字段冗余:可以使用字段冗余就不用join查询了
      • 系统层组装:可以在业务层分别查询出来,然后组装起来,逻辑较复杂
    • 额外的数据管理负担和数据运算压力:数据库扩容、维护成本变高

ShardingSphere实战

ShardingSphere

Apache ShardingSphere是一款开源的分布式数据库中间件组成的生态圈。它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。

ShardingSphere项目状态如下:
在这里插入图片描述

ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。
在这里插入图片描述

  • Sharding-JDBC:被定位为轻量级Java框架,在Java的JDBC层提供的额外服务,以jar包形式使用。
  • Sharding-Proxy:被定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。
  • Sharding-Sidecar:被定位为Kubernetes或Mesos的云原生数据库代理,以DaemonSet的形式代理所有对数据库的访问。

在这里插入图片描述
Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar三者区别如下:
在这里插入图片描述

ShardingSphere安装包下载
在这里插入图片描述
使用Git下载工程:

git clone https://github.com/apache/incubator-shardingsphere.git

Sharding-JDBC

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

  • 适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
    在这里插入图片描述
Sharding-JDBC主要功能:
  • 数据分片
    • 分库、分表
    • 读写分离
    • 分片策略
    • 分布式主键
  • 分布式事务
    • 标准化的事务接口
    • XA强一致性事务
    • 柔性事务
  • 数据库治理
    • 配置动态化
    • 编排和治理
    • 数据脱敏
    • 可视化链路追踪
Sharding-JDBC 内部结构:

在这里插入图片描述

  • 图中黄色部分表示的是Sharding-JDBC的入口API,采用工厂方法的形式提供。 目前有
    ShardingDataSourceFactory和MasterSlaveDataSourceFactory两个工厂类。
    • ShardingDataSourceFactory支持分库分表、读写分离操作
    • MasterSlaveDataSourceFactory支持读写分离操作
  • 图中蓝色部分表示的是Sharding-JDBC的配置对象,提供灵活多变的配置方式。
    ShardingRuleConfiguration是分库分表配置的核心和入口,它可以包含多个TableRuleConfiguration和MasterSlaveRuleConfiguration。
    • TableRuleConfiguration封装的是表的分片配置信息,有5种配置形式对应不同的
      Configuration类型。
    • MasterSlaveRuleConfiguration封装的是读写分离配置信息。
  • 图中红色部分表示的是内部对象,由Sharding-JDBC内部使用,应用开发者无需关注。ShardingJDBC通过ShardingRuleConfiguration和MasterSlaveRuleConfiguration生成真正供ShardingDataSource和MasterSlaveDataSource使用的规则对象。ShardingDataSource和MasterSlaveDataSource实现了DataSource接口,是JDBC的完整实现方案。
Sharding-JDBC初始化流程:
  • 根据配置的信息生成Configuration对象
  • 通过Factory会将Configuration对象转化为Rule对象
  • 通过Factory会将Rule对象与DataSource对象封装
  • Sharding-JDBC使用DataSource进行分库分表和读写分离操作
Sharding-JDBC 使用过程:
  • 引入maven依赖

    <dependency> 
    	<groupId>org.apache.shardingsphere</groupId> 
    	<artifactId>sharding-jdbc-core</artifactId> 
    	<version>${latest.release.version}</version> 
    </dependency>
    

    注意: 请将${latest.release.version}更改为实际的版本号。

  • 规则配置
    Sharding-JDBC可以通过Java,YAML,Spring命名空间和Spring Boot Starter四种方式配置,开发者可根据场景选择适合的配置方式。

  • 创建DataSource
    通过ShardingDataSourceFactory工厂和规则配置对象获取ShardingDataSource,然后即可通过DataSource选择使用原生JDBC开发,或者使用JPA, MyBatis等ORM工具。

    DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, props);
    

数据分片剖析实战

核心概念
  • 表概念

    • 真实表
      数据库中真实存在的物理表。例如b_order0、b_order1

    • 逻辑表
      在分片之后,同一类表结构的名称(总成)。例如b_order。

    • 数据节点
      在分片之后,由数据源和数据表组成。例如ds0.b_order1

    • 绑定表
      指的是分片规则一致的关系表(主表、子表),例如b_order和b_order_item,均按照
      order_id分片,则此两个表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,可以提升关联查询效率。

      b_order:b_order0、b_order1 
      b_order_item:b_order_item0、b_order_item1 
      select * from b_order o join b_order_item i on(o.order_id=i.order_id) where o.order_id in (10,11);
      

      如果不配置绑定表关系,采用笛卡尔积关联,会生成4个SQL

      select * from b_order0 o join b_order_item0 i on(o.order_id=i.order_id) where o.order_id in (10,11); 
      select * from b_order0 o join b_order_item1 i on(o.order_id=i.order_id) where o.order_id in (10,11); 
      select * from b_order1 o join b_order_item0 i on(o.order_id=i.order_id) where o.order_id in (10,11); 
      select * from b_order1 o join b_order_item1 i on(o.order_id=i.order_id) where o.order_id in (10,11);
      

      如果配置绑定表关系,生成2个SQL

      select * from b_order0 o join b_order_item0 i on(o.order_id=i.order_id) where o.order_id in (10,11); 
      select * from b_order1 o join b_order_item1 i on(o.order_id=i.order_id) where o.order_id in (10,11);
      
    • 广播表
      在使用中,有些表没必要做分片,例如字典表、省份信息等,因为他们数据量不大,而且这种表可能需要与海量数据的表进行关联查询。广播表会在不同的数据节点上进行存储,存储的表结构和数据完全相同。

  • 分片算法(ShardingAlgorithm)
    由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。目前提供4种分片算法。

    • 精确分片算法PreciseShardingAlgorithm
      用于处理使用单一键作为分片键的=与IN进行分片的场景。
    • 范围分片算法RangeShardingAlgorithm
      用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。
    • 复合分片算法ComplexKeysShardingAlgorithm
      用于处理使用多键作为分片键进行分片的场景,多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。
    • Hint分片算法HintShardingAlgorithm
      用于处理使用Hint行分片的场景。对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释两种方式使用。
  • 分片策略(ShardingStrategy)
    分片策略包含分片键和分片算法,真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略。

    • 标准分片策略StandardShardingStrategy
      只支持单分片键,提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。
      提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
      PreciseShardingAlgorithm是必选的,RangeShardingAlgorithm是可选的。但是SQL中使用了范围操作,如果不配置RangeShardingAlgorithm会采用全库路由扫描,效率低。
    • 复合分片策略ComplexShardingStrategy
      支持多分片键。提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
    • 行表达式分片策略InlineShardingStrategy
      只支持单分片键。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发。如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
    • Hint分片策略HintShardingStrategy
      通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。
    • 不分片策略NoneShardingStrategy
      不分片的策略。
  • 分片策略配置
    对于分片策略存有数据源分片策略和表分片策略两种维度,两种策略的API完全相同。

    • 数据源分片策略
      用于配置数据被分配的目标数据源。
    • 表分片策略
      用于配置数据被分配的目标表,由于表存在与数据源内,所以表分片策略是依赖数据源分片策略结果的。
流程剖析

ShardingSphere 3个产品的数据分片功能主要流程是完全一致的,如下图所示。
在这里插入图片描述

  • SQL解析
    SQL解析分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。
    Sharding-JDBC采用不同的解析器对SQL进行解析,解析器类型如下:
    • MySQL解析器
    • Oracle解析器
    • SQLServer解析器
    • PostgreSQL解析器
    • 默认SQL解析器
  • 查询优化
    负责合并和优化分片条件,如OR等。
  • SQL路由
    根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。
  • SQL改写
    将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。
  • SQL执行
    通过多线程执行器异步执行SQL。
  • 结果归并
    将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。
SQL使用规范
  • SQL使用规范
    • 支持项

      • 路由至单数据节点时,目前MySQL数据库100%全兼容,其他数据库完善中。

      • 路由至多数据节点时,全面支持DQL、DML、DDL、DCL、TCL。支持分页、去重、排序、分组、聚合、关联查询(不支持跨库关联)。以下用最为复杂的查询为例:

        SELECT select_expr [, select_expr ...] 
        FROM table_reference [, table_reference ...] 
        [WHERE predicates] 
        [GROUP BY {col_name | position} [ASC | DESC], ...] 
        [ORDER BY {col_name | position} [ASC | DESC], ...] 
        [LIMIT {[offset,] row_count | row_count OFFSET offset}]
        
    • 不支持项(路由至多数据节点)
      不支持CASE WHEN、HAVING、UNION (ALL)

    • 支持分页子查询,但其他子查询有限支持,无论嵌套多少层,只能解析至第一个包含数据表的子查询,一旦在下层嵌套中再次找到包含数据表的子查询将直接抛出解析异常。
      例如,以下子查询可以支持:

      SELECT COUNT(*) FROM (SELECT * FROM b_order o)
      

      以下子查询不支持:

      SELECT COUNT(*) FROM (SELECT * FROM b_order o WHERE o.id IN (SELECT id FROM b_order WHERE status = ?))
      

      简单来说,通过子查询进行非功能需求,在大部分情况下是可以支持的。比如分页、统计总数等;而通过子查询实现业务查询当前并不能支持。

    • 由于归并的限制,子查询中包含聚合函数目前无法支持。

    • 不支持包含schema的SQL。因为ShardingSphere的理念是像使用一个数据源一样使用多数据源,因此对SQL的访问都是在同一个逻辑schema之上。

    • 当分片键处于运算表达式或函数中的SQL时,将采用全路由的形式获取结果。
      例如下面SQL,create_time为分片键:

      SELECT * FROM b_order WHERE to_date(create_time, 'yyyy-mm-dd') = '2020- 05-05';
      

      由于ShardingSphere只能通过SQL字面提取用于分片的值,因此当分片键处于运算表达式或函数中时,ShardingSphere无法提前获取分片键位于数据库中的值,从而无法计算出真正的分片值。

不支持的SQL示例:

INSERT INTO tbl_name (col1, col2,) VALUES(1+2, ?,) //VALUES语句不支持运算 表达式

INSERT INTO tbl_name (col1, col2,) SELECT col1, col2,FROM tbl_name WHERE col3 = ? //INSERT .. SELECT 

SELECT COUNT(col1) as count_alias FROM tbl_name GROUP BY col1 HAVING count_alias > ? //HAVING 

SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2 //UNION 

SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2 //UNION ALL 

SELECT * FROM ds.tbl_name1 //包含schema 

SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name //同时使用普通聚合函数 和DISTINCT 

SELECT * FROM tbl_name WHERE to_date(create_time, ‘yyyy-mm-dd’) = ? //会导致 全路由
  • 分页查询
    完全支持MySQL和Oracle的分页查询,SQLServer由于分页查询较为复杂,仅部分支持.
    • 性能瓶颈:
      查询偏移量过大的分页会导致数据库获取数据性能低下,以MySQL为例:

      SELECT * FROM b_order ORDER BY id LIMIT 1000000, 10
      

      这句SQL会使得MySQL在无法利用索引的情况下跳过1000000条记录后,再获取10条记录,其性能可想而知。 而在分库分表的情况下(假设分为2个库),为了保证数据的正确性,SQL会改写为:

      SELECT * FROM b_order ORDER BY id LIMIT 0, 1000010
      

      即将偏移量前的记录全部取出,并仅获取排序后的最后10条记录。这会在数据库本身就执行很慢的情况下,进一步加剧性能瓶颈。 因为原SQL仅需要传输10条记录至客户端,而改写之后的SQL则会传输1,000,010 * 2的记录至客户端。

    • ShardingSphere的优化:
      ShardingSphere进行了以下2个方面的优化。

      • 首先,采用流式处理 + 归并排序的方式来避免内存的过量占用。
      • 其次,ShardingSphere对仅落至单节点的查询进行进一步优化。
    • 分页方案优化:
      由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案:

      SELECT * FROM b_order WHERE id > 1000000 AND id <= 1000010 ORDER BY id
      

      或通过记录上次查询结果的最后一条记录的ID进行下一页的查询:

      SELECT * FROM b_order WHERE id > 1000000 LIMIT 10
      
其他功能
  • Inline行表达式
    InlineShardingStrategy:采用Inline行表达式进行分片的配置。
    Inline是可以简化数据节点和分片算法配置信息。主要是解决配置简化、配置一体化。
    语法格式:
    行表达式的使用非常直观,只需要在配置中使用 e x p r e s s i o n 或 { expression }或 expression->{ expression }标识行表达式即可。例如:

    ${begin..end} 表示范围区间 
    ${[unit1, unit2, unit_x]} 表示枚举值
    

    行表达式中如果出现多个 或 {}或 ->{}表达式,整个表达式结果会将每个子表达式结果进行笛卡尔(积)组合。例如,以下行表达式:

    ${['online', 'offline']}_table${1..3} 
    $->{['online', 'offline']}_table$->{1..3}
    

    最终会解析为:

    online_table1, online_table2, online_table3, 
    offline_table1, offline_table2, offline_table3
    

    数据节点配置:
    对于均匀分布的数据节点,如果数据结构如下:
    在这里插入图片描述
    用行表达式可以简化为:

    db${0..1}.b_order${1..2} 
    或者db$->{0..1}.b_order$->{1..2}
    

    对于自定义的数据节点,如果数据结构如下:
    在这里插入图片描述
    用行表达式可以简化为:

    db0.b_order${0..1},db1.b_order${2..4}
    

    分片算法配置:
    行表达式内部的表达式本质上是一段Groovy代码,可以根据分片键进行计算的方式,返回相应的真实数据源或真实表名称。

    ds${id % 10} 
    或者ds$->{id % 10}
    

    结果为:ds0、ds1、ds2… ds9

  • 分布式主键
    ShardingSphere不仅提供了内置的分布式主键生成器,例如UUID、SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。

  • 内置主键生成器:

    • UUID
      采用UUID.randomUUID()的方式产生分布式主键。
    • SNOWFLAKE
      在分片规则配置模块可配置每个表的主键生成策略,默认使用雪花算法,生成64bit的长整型数据。
  • 自定义主键生成器:

    • 自定义主键类,实现ShardingKeyGenerator接口

    • 按SPI规范配置自定义主键类
      在Apache ShardingSphere中,很多功能实现类的加载方式是通过SPI注入的方式完成的。
      注意:在resources目录下新建META-INF文件夹,再新建services文件夹,然后新建文件的名字为org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator,打开文件,复制自定义主键类全路径到文件中保存。

    • 自定义主键类应用配置

      #对应主键字段名 
      spring.shardingsphere.sharding.tables.t_book.key-generator.column=id 
      #对应主键类getType返回内容 
      spring.shardingsphere.sharding.tables.t_book.key- generator.type=LEARNKEY
      

读写分离剖析实战

读写分离是通过主从的配置方式,将查询请求均匀的分散到多个数据副本,进一步的提升系统的处理能力。
在这里插入图片描述
主从架构:读写分离,目的是高可用、读写扩展。主从库内容相同,根据SQL语义进行路由。

分库分表架构:数据分片,目的读写扩展、存储扩容。库和表内容不同,根据分片配置进行路由。

将水平分片和读写分离联合使用,能够更加有效的提升系统性能, 下图展现了将分库分表与读写分离一同使用时,应用程序与数据库集群之间的复杂拓扑关系。

在这里插入图片描述
读写分离虽然可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题,包括多个主库之间的数据一致性,以及主库与从库之间的数据一致性的问题。 并且,读写分离也带来了与数据分片同样的问题,它同样会使得应用开发和运维人员对数据库的操作和运维变得更加复杂。

读写分离应用方案

在数据量不是很多的情况下,我们可以将数据库进行读写分离,以应对高并发的需求,通过水平扩展从库,来缓解查询的压力。如下:
在这里插入图片描述

分表+读写分离

在数据量达到500万的时候,这时数据量预估千万级别,我们可以将数据进行分表存储。
在这里插入图片描述

分库分表+读写分离

在数据量继续扩大,这时可以考虑分库分表,将数据存储在不同数据库的不同表中,如下:
在这里插入图片描述
透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库集群,是ShardingSphere读写分离模块的主要设计目标。

主库、从库、主从同步、负载均衡

  • 核心功能
    • 提供一主多从的读写分离配置。仅支持单主库,可以支持独立使用,也可以配合分库分表使用
    • 独立使用读写分离,支持SQL透传。不需要SQL改写流程
    • 同一线程且同一数据库连接内,能保证数据一致性。如果有写入操作,后续的读操作均从主库读取。
    • 基于Hint的强制主库路由。可以强制路由走主库查询实时数据,避免主从同步数据延迟。
  • 不支持项
    • 主库和从库的数据同步
    • 主库和从库的数据同步延迟
    • 主库双写或多写
    • 跨主库和从库之间的事务的数据不一致。建议在主从架构中,事务中的读写均用主库操作。

强制路由剖析实战

在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。

HintManager主要使用ThreadLocal管理分片键信息,进行hint强制路由。在代码中向HintManager添加的配置信息只能在当前线程内有效。

Hint使用场景:
  • 数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中
  • 读写分离操作,如果强制在主库进行某些数据操作
Hint使用过程:
  • 编写分库或分表路由策略,实现HintShardingAlgorithm接口

    public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Integer> {
    	@Override 
    	public Collection<String> doSharding(Collection<String> collection, HintShardingValue<Integer> hintShardingValue) {
    		  //添加分库或分表路由逻辑 
    	} 
    }
    
  • 在配置文件指定分库或分表策略

    #强制路由库和表 
    spring.shardingsphere.sharding.tables.b_order.database- 
    strategy.hint.algorithm-class-name=com.learn.hint.MyHintShardingAlgorithm 
    spring.shardingsphere.sharding.tables.b_order.table-strategy.hint.algorithm- 
    class-name=com.learn.hint.MyHintShardingAlgorithm 
    spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=ds$-> 
    {0..1}.b_order$->{0..1}
    
  • 在代码执行查询前使用HintManager指定执行策略值

    @Test//路由库和表 
    public void test(){ 
    	HintManager hintManager = HintManager.getInstance(); 	
    	hintManager.addDatabaseShardingValue("b_order",1); 
    	hintManager.addTableShardingValue("b_order",1); 
    	List<Order> list = orderRepository.findAll(); 
    	hintManager.close(); 
    	list.forEach(o -> { 
    		System.out.println(o.getOrderId()+" "+o.getUserId()+" "+o.getOrderPrice()); 
    	}); 
    }
    

    在读写分离结构中,为了避免主从同步数据延迟及时获取刚添加或更新的数据,可以采用强制路由走主库查询实时数据,使用hintManager.setMasterRouteOnly设置主库路由即可。

明天接着聊数据脱敏剖析实战、 分布式事务剖析实战、SPI 加载剖析、编排治理剖析和Sharding-Proxy实战,今天先到这里。

CSDN社区 《博客新星》活动,官方大力扶持新人创作,只要参与其中并发布原创就有机会获得官方奖品:精品日历、新程序员杂志、CSDN帆布包、CSDN定制款手机壳,快来参与吧!链接直达 https://bbs.csdn.net/topics/605597781

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Captain Leo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值