分库分表

分库分表背景及解决方案

背景及问题

用户增多单库承受不住使用主从架构分担请求,业务复杂,写入请求增多,提高程序性能,分库分表读写分离。

解决方案

垂直拆分:分为垂直分库和垂直分表

1)垂直分库:比如一个库中保存用户和订单,由于量都非常大,可以分成两个库分别来保存用户和订单信息。

2)垂直分表:比如一张表保存了用户信息,其中还有用户介绍,可以把用户介绍这种大文本单独设计一张表,进行关联,需要的时候进行关联查询。

水平拆分:分为水平分库和水平分表

1)水平分库:单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。

2)水平分表:针对数据量巨大的单张表(比如订单表),按照规则把一张表的数据切分到多张表里面去。

水平分表规则:

1) RANGE(时间、地域、大小)

2) HASH(ID取模)

3) 站内信(维度,即自己只能看见自己,单请求在一个库)

4) 用户表(范围、ID HASH均匀分布)

5) 流水表(时间维度)

主键规则:

1) UUID

2) 雪花算法

数据一致性:

1)强一致性:XA协议

2)最终一致性:TCC、saga、Seata

数据库扩容:

1)成倍增加数据节点,实现平滑扩容

2)成倍扩容以后,表中的部分数据请求已被路由到其他节点上面,可以清理掉

业务层改造:

1)基于代理层方式:Mycat、Sharding-Proxy、MySQL Proxy

2)基于应用层方式:Sharding-jdbc

分库后面临的问题:

1) 事务问题

2) 跨库跨表的join问题

3) 数据库扩容、维护成本变高

ShardingSphere

Apache ShardingSphere是一款开源的分布式数据库中间件组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立的产品组成。ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的 计算和存储能力,而并非实现一个全新的关系型数据库。

img

1) Sharding-JDBC:被定位为轻量级Java框架,在Java的JDBC层提供的额外服务,以jar包形式使用。

2) Sharding-Proxy:被定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版 本,用于完成对异构语言的支持。

3) Sharding-Sidecar:被定位为Kubernetes或Mesos的云原生数据库代理,以DaemonSet的形式代 理所有对数据库的访问。

img

img

Sharding-JDBC

Sharding-JDBC定位为轻量级Java框架,在Java的JDBC层提供的额外服务。可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM 框架的使用。

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

2) 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。

3) 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。

Sharding-JDBC主要功能

1) 数据分片:分库分表、读写分离、分片策略、分布式主键

2) 分布式事务:标准化事务接口、XA强一致性事务、柔性事务

3) 数据库治理:配置动态、服务治理、数据脱敏、链路追踪

Sharding-JDBC 内部结构

img

1) 黄色部分表示的是Sharding-JDBC的入口API,采用工厂方法的形式提供。

2) 蓝色部分表示的是Sharding-JDBC的配置对象,提供灵活多变的配置方式。

3) 红色部分表示的是内部对象,由Sharding-JDBC内部使用,应用开发者无需关注。

Sharding-JDBC初始化流程

1) 根据配置的信息生成Configuration对象

2) 通过Factory会将Configuration对象转化为Rule对象

3) 通过Factory会将Rule对象与DataSource对象封装

4) Sharding-JDBC使用DataSource进行分库分表和读写分离操作

Sharding-JDBC 使用过程

1) 引入maven依赖

2) 规则配置,Java,YAML,Spring命名空间和Spring Boot Starter四种方式配置

3) 通过ShardingDataSourceFactory工厂和规则配置对象获取ShardingDataSource,创建DataSource。

数据分片核心概念

表概念

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

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

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

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

5) 广播表 广播表会在不同的数据节点上进行存储,存储 的表结构和数据完全相同,比如省份,字典表信息

分片算法

1) 精确分片算法PreciseShardingAlgorithm

2) 范围分片算法RangeShardingAlgorithm

3) 复合分片算法ComplexKeysShardingAlgorithm

4) Hint分片算法HintShardingAlgorithm

分片策略

1) 标准分片策略StandardShardingStrateg

2) 复合分片策略ComplexShardingStrategy

3) 行表达式分片策略InlineShardingStrateg

4) Hint分片策略HintShardingStrategy

5) 不分片策略NoneShardingStrategy

分片策略配置

1) 数据源分片策略

2) 表分片策略

数据分片流程

img

1) SQL解析:SQL解析分为词法解析和语法解析。先通过词法解析器将SQL拆分为一个个不可再分的单词。再使 用语法解析器对SQL进行理解,并最终提炼出解析上下文。

2) 查询优化:负责合并和优化分片条件,如OR等。

3) SQL路由:根据解析上下文匹配用户配置的分片策略,并生成路由路径。

4) SQL改写:将SQL改写为在真实数据库中可以正确执行的语句。

5) SQL执行:通过多线程执行器异步执行SQL。

6) 结果归并:将多个执行结果集归并以便于通过统一的JDBC接口输出。

数据分片SQL使用规范

SQL使用规范

1) 支持路由至单数据节点时,目前MySQL数据库100%全兼容,路由至多数据节点时,全面支持DQL、DML、DDL、DCL、TCL。

2) 路由至多数据节点不支持CASE WHEN、HAVING、UNION (ALL)。

3) 支持分页子查询,但其他子查询有限支持,无论嵌套多少层,只能解析第一层。

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

5) 不支持包含schema的SQL。

6) 当分片键处于运算表达式或函数中的SQL时,将采用全路由的形式获取结果。

不支持的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由于分页查询较为复杂,仅部分支持

数据分片Inline行表达式

前面分片策略说到了行表达式分片策略InlineShardingStrateg,他就是采用Inline行表达式进行分片的配置,简化数据节点和分片算法配置信息。完成配置简化、配置一体化。

语法格式

使用${ 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

均匀分布的数据节点

img

用行表达式:

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

自定义的数据节点

img

用行表达式:

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

分片算法配置,根据分片键进行计算的方式,返回相应的真实数据源或真实表名称。

ds${id % 10} 或者 ds$->{id % 10} ->结果为:ds0、ds1、ds2… ds9

分布式主键

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

img

读写分离及架构设计方案

读写分离是通过主从的配置方式,将查询请求均匀的分散到多个数据副本,进一步的提升系统的处理能力。

img

主从架构:读写分离,目的是高可用、读写扩展。主从库内容相同,根据SQL语义进行路由。

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

读写分离虽然可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题,包括多个主库之间的数据一致性,以及主库与从库之间的数据一致性的问题。

架构设计方案

方案一(第一阶段->一主两从):数据量不是很多的情况下,我们可以将数据库进行读写分离,以应对高并发的需求,通过水平扩展从库,来缓解查询的压力。

img

方案二(第二阶段->主从加分表):数据量达到500万的时候,数据量预估千万级别,将数据进行分表存储。

img

方案三(最终阶段->分库分表加读写分离):如果方案二不满足需求,数据量继续扩大,这时考虑分库分表,将数据存储在不同数据库的不同表中。

img

ShardingSphere读写分离模块的主要设计目标是让使用方尽量像使用一个数据库一样使用主从数据库集群,即规则让关联数据在同一个库中,避免跨库调用。

ShardingSphere核心功能

1) 提供一主多从的读写分离配置。仅支持单主库,可以支持独立使用,也可以配合分库分表使用

2) 独立使用读写分离,支持SQL透传。不需要SQL改写流程

3) 同一线程且同一数据库连接内,能保证数据一致性。如果有写入操作,后续的读操作均从主库读取。

4) 基于Hint的强制主库路由。可以强制路由走主库查询实时数据,避免主从同步数据延迟。

ShardingSphere不支持项

1) 主库和从库的数据同步

2) 主库和从库的数据同步延迟

3) 主库双写或多写

4) 跨主库和从库之间的事务的数据不一致。建议在主从架构中,事务中的读写均用主库操作,使用Hint强制路由。

强制路由Hint

分片条件并不存在于SQL,而存在于外部业务逻辑,使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作,比如事务操作,要求读写都在主库。

使用步骤:

1) 编写分库或分表路由策略,实现HintShardingAlgorithm接口

2) 在配置文件指定分库或分表策略

3) 在代码执行查询前使用HintManager指定执行策略值

数据脱敏

数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护,其实就是加密。

img

脱敏配置四部分:数据源配置,加密器配置,脱敏表配置以及查询属性配置

img

数据源配置:指DataSource的配置信息

加密器配置:指使用什么加密策略进行加解密。目前ShardingSphere内置了两种加解密策略: AES/MD5

脱敏表配置:指定哪个列用于存储密文数据(cipherColumn)、哪个列用于存储明文数据 (plainColumn)以及用户想使用哪个列进行SQL编写(logicColumn)

查询属性的配置:当底层数据库表里同时存储了明文数据、密文数据后,该属性开关用于决定是直 接查询数据库表里的明文数据进行返回,还是查询密文数据通过Encrypt-JDBC解密后返回。

加密策略解析

ShardingSphere提供了两种加密策略用于数据脱敏,该两种策略分别对应ShardingSphere的两种加解 密的接口,即Encryptor和QueryAssistedEncryptor。

Encryptor:提供encrypt(), decrypt()两种方法对需要脱敏的数据进行加解密。

QueryAssistedEncryptor:即使是相同的数据,如两个用户的密码相同,它们在数据库里存储的脱敏数据也应当是不一样的。

分布式事务理论CAP和BASE

CAP(强一致性)

布鲁尔定理。对于共享数据系统,最多只能同时拥有CAP其中的两个

img

BASE(最终一致性)

基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。它的核心思想是即使无法做到强一致性(CAP 就是强一致性),但应用可以采用适合的方式达到最终一致性。

BA指的是基本业务可用性,支持分区失败;

S表示柔性状态,也就是允许短时间内不同步;

E表示最终一致性,数据最终是一致的,但是实时是不一致的。

分布式事务模式2PC和3PC

2PC模式(强一致性)

两阶段提交,就是将事务的提交过程分为两个阶段来进行处理。事务的发起者称协调者,事务的执行者称参与者。

1) 阶段 1:准备阶段 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。 各参与者执行事务操作,但不提交事务,将 undo 和 redo 信息记入事务日志中。 如参与者执行成功,给协调者反馈 yes;如执行失败,给协调者反馈 no。

2) 阶段 2:提交阶段 如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息; 否则,发送提交(commit)消息。

2PC 方案存在的问题

1) 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。

2) 可靠性问题:如果协调者存在单点故障问题,如果协调者出现故障,参与者将一直处于锁定状态。

3) 数据一致性问题:在阶段 2 中,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。

3PC模式(强一致性)

两阶段提交的改进版本,引入超时机制,将两阶段的准备阶段拆分为 2 个阶段,插入了一个 preCommit 阶段。

1) 阶段1:canCommit 协调者向参与者发送 commit 请求,参与者如果可以提交就返回 yes 响应,否则返回 no 响应。

2) 阶段2:preCommit 协调者根据阶段 1 canCommit 参与者的反应情况执行预提交事务或中断事务操作。 参与者均反馈 yes:协调者向所有参与者发出 preCommit 请求,参与者收到 preCommit 请求后,执行事务操作,但不提交;将 undo 和 redo 信息记入事务日志 中;各参与者向协调者反馈 ack 响应或 no 响应,并等待最终指令。 任何一个参与者反馈 no或等待超时:协调者向所有参与者发出 abort 请求,无论收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

3) 阶段3:do Commit 该阶段进行真正的事务提交,根据阶段 2 preCommit反馈的结果完成事务提交或中断操作。

Sharding-JDBC整合XA原理

ShardingSphere整合XA事务时,分离了XA事务管理和连接池管理,这样接入XA时,可以做到对业务的零侵入,而且ShardingSphere集成后,可保证分片后跨库事务强一致性,XA本身也是强一致性的。

img

执行步骤:

1) Begin(开启XA全局事务):调用具体的XA事务管理器开启XA的全局事务。

2) 执行物理SQL:将所有SQL操作,标记为XA事务。

3) Commit/rollback(提交XA事务):收集所有投票,全部收到提交,否则回滚。

Sharding-JDBC整合Saga原理

ShardingSphere是基于反向SQL技术实现的反向补偿操作,它将对数据库进行更新操作的SQL自动生成反向SQL,并交由Saga-actuator引擎执行。

img

执行步骤:

1) Init(Saga引擎初始化)

2) Begin(开启Saga全局事务):记录了所有子事务的正向SQL和逆向SQL

3) 执行物理SQL

4) Commit/rollback(提交Saga事务):生成Saga执行引擎所需的调用链路图,然后进行提交或回滚

Sharding-JDBC整合Seata原理

分布式事务的实现目前主要分为两阶段的XA强事务和BASE柔性事务。

img

整合Seata AT事务时,需要把TM,RM,TC的模型融入到ShardingSphere 分布式事务的SPI的生态中。在数据库资源上,Seata通过对接DataSource接口,让JDBC操作可以同TC进行RPC通信。

img

执行步骤:

1) Init(Seata引擎初始化):包含Seata柔性事务的应用启动时,用户配置的数据源会按seata.conf的配置,适配成Seata事务所 需的DataSourceProxy,并且注册到RM中。

2) Begin(开启Seata全局事务):TM控制全局事务的边界,TM通过向TC发送Begin指令,获取全局事务ID,所有分支事务通过此全 局事务ID,参与到全局事务中;全局事务ID的上下文存放在当前线程变量中。

3) 执行分片物理SQL :处于Seata全局事务中的分片SQL通过RM生成undo快照,并且发送participate指令到TC,加入到全局事务中。ShardingSphere的分片物理SQL是按多线程方式执行,因此整合Seata AT事务时, 需要在主线程和子线程间进行全局事务ID的上下文传递,这同服务间的上下文传递思路完全相同。

4) Commit/rollback(提交Seata事务):提交Seata事务时,TM会向TC发送全局事务的commit和rollback指令,TC根据全局事务ID协调所有分支事务进行commit和rollback。

ShardingSphere SPI 加载

Apache ShardingSphere所有通过SPI方式载入的功能模块:

1) SQL解析接口:用于规定用于解析SQL的ANTLR语法文件

2) 数据库协议接口:用于Sharding-Proxy解析与适配访问数据库的协议

3) 数据脱敏接口:用于规定加解密器的加密、解密、类型获取、属性设置等方式

4) 分布式主键接口:用于规定如何生成全局性的自增、类型获取、属性设置等。

5) 分布式事务接口:用于规定如何将分布式事务适配为本地事务接口。

6) XA事务管理器接口:用于规定如何将XA事务的实现者适配为统一的XA事务接口。

7) 注册中心接口:用于规定注册中心初始化、存取数据、更新数据、监控等行为。

ShardingSphere 编排治理

提供配置中心/注册中心(以及规划中的元数据中心)、配置动态化、数据库熔断禁用、 调用链路等治理能力。

1) 配置中心:将配置集中于配置中心,可以更加有效进行管理,并且支持数据源、表与分片及读写分离策略的动态切换。

2) 注册中心:存放运行时的动态/临时状态数据,比如可用的proxy的实例,需要禁用或熔断的datasource实例。可以提供熔断数据库访问程序对数据库的访问和禁用从库的访问的编排治理能力。

3) 支持外部配置中心和注册中心扩展,基于SPI机制,比如Zookeeper、Nocas等等。

4) 应用性能监控:对分布式系统的性能诊断,包含调用链展示,应用拓扑分析等。

Sharding-Proxy

Sharding-Proxy是ShardingSphere的第二个产品,定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持,也就是一个代理组件,可以对其他语言使用,不只是JAVA,便于扩展,不过Sharding-Proxy 默认不支持hint。

img

Sharding-Proxy使用过程:

1) 下载Sharding-Proxy的最新发行版;

2) 解压缩后修改conf/server.yaml和以config-前缀开头的文件,进行分片规则、读写分离规则配置 编辑%SHARDING_PROXY_HOME%\conf\config-xxx.yaml 编辑%SHARDING_PROXY_HOME%\conf\server.yaml

3) 引入依赖jar 如果后端连接MySQL数据库,需要下载MySQL驱动, 解压缩后将mysql-connector-java5.1.48.jar拷贝到${sharding-proxy}\lib目录。 如果后端连接PostgreSQL数据库,不需要引入额外依赖。

4) Linux操作系统请运行bin/start.sh,Windows操作系统请运行bin/start.bat启动Sharding-Proxy。 使用默认配置启动:${sharding-proxy}\bin\start.sh 配置端口启动:${sharding-proxy}\bin\start.sh ${port}

5) 使用客户端工具连接。如: mysql -h 127.0.0.1 -P 3307 -u root -p root

------ 本文结束感谢您的阅读 ------
请我一杯咖啡吧!
itingyu 微信打赏 微信打赏