Sharding-JDBC
2021-08-23 17:38:36 3 举报
AI智能生成
Sharding-JDBC
作者其他创作
大纲/内容
分库分表
描述
单机数据库
主从架构
用户不断增多,考虑到系统的高可用和越来越多的用户请求
用户量级和业务进一步提升后,写请求越来越多
问题
用户请求量太大
单服务器TPS、内存、IO都是有上限,需要将请求打散分布到多个服务器
单库数据量太大
单个数据库处理能力有限
单库所在服务器的磁盘空间有限
单库上的操作IO有瓶颈
单表数据量太大
查询、插入、更新操作都会变慢,再加字段、索引、机器迁移都会产生高负载,影响服务
解决
垂直拆分
垂直分库
微服务架构时,业务切割得足够独立,数据也会按照业务切分,保证业务数据隔离,大大提升了数据库的吞吐能力
垂直分表
表中字段太多且包含大字段时,在查询时对数据库的IO、内存有影响,同时更新数据时,产生的binlog文件会很大,MySQL在主从同步时也会有延迟的风险
水平拆分
水平分表
针对数据量巨大的单张表,按照规则把一张表的数据切分到多张表里面去
但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈
规则
RANGE
时间
地域
大小
HASH
用户ID取模
水平分库
将单张表的数据切分到多个服务器上去,每个服务器具有对应的库与表,只是表中数据集合不同
水平分库分表能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等的瓶颈
不跨库、不跨表,保证同一类数据都在同一个服务器上面
数据在切分之前,需要考虑如何高效的进行数据获取
不同业务的切分规则不一样,如
站内信
用户维度:按照用户ID hash分库
用户表
范围法:以用户ID为划分依据,将数据水平切分到两个数据库
会出现单表负载较高
按照用户ID Hash尽量保证用户数据均衡分到数据库中
流水表
时间维度:根据每天新增的流水来判断,按年份|月份|日期分库
订单表
采用空间换时间,将一次投递记录存为两份,C端的投递记录以用户ID为分片键,B端收到的简历按照公司ID为分片键
主键选择
UUID
SNOWFLAKE
数据一致性
强一致性
XA协议
最终一致性
TCC、saga、Seata
数据库扩容
成倍增加数据节点,实现平滑扩容
成倍扩容后,表中的部分数据请求已被路由到其他节点,需要清理数据
业务层改造
基于代理层
MyCat、Sharding-Proxy、MySQL Proxy
基于应用层
Sharding-JDBC
分库后问题
事务问题
一次事务涉及多个服务器,数据需要保障一致性
跨库跨表的join问题
全局表(字典表)
基础数据/配置数据,所有库都拷贝一份
字段冗余
可以使用字段冗余就不同join查询了
系统层组装
可以在业务层分别查询,然后组装起来,逻辑较复杂
额外的数据管理负担和数据运算压力
数据库扩容、维护成本变高
ShardingSphere
概念
Apache_ShardingSphere是一款开源的分布式数据库中间件组成的生态圈
它由Sharding-JDBC、Sharding-Proxy、Sharding-Sidecar(规划中)这3款相互独立的产品组成
它们均提供标准化的数据分片、分布式事务和数据库治理能力,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景
发展
Version 1.x
JDBC + Sharding
Version 2.x
Database Orchestration
Version 3.x
Proxy
Version 4.x
Apache Release
定位
为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库
为轻量级Java框架,在Java的JDBC层提供的额外服务,以jar包形式使用
Sharding-Proxy
为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持
Sharding-Sidecar
为Kubernetes或Mecos的云原生数据库代理,以DaemonSet的形式代理所有数据库的访问
简介
介绍
定位为轻量级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
主要功能
数据分片
读写分离
分片策略
分布式主键
分布式事务
标准化的事务接口
XA强一致性事务
柔性事务
数据库治理
配置动态化
编排和治理
数据脱敏
可视化链路追踪
内部结构
入口API
ShardingDataSourceFactory
支持分库分表、读写分离操作
MasterSlaveDataSourceFactory
支持读写分离操作
配置对象
提供灵活多变的配置方式
ShardingRuleConfiguration
分库分表配置的核心和入口
对应多个TableRuleConfiguration
TableRuleConfiguration
封装的是表的分片配置信息,有5中配置形式对应不同的Configuration类型
每个对应一个ShardingStrategyConfiguration
MasterSlaveRuleConfiguration
封装的是读写分离配置信息
内部对象
内部使用,应用开发者无需关注
通过ShardingRuleConfiguration和MasterSlaveRuleConfiguration生成真正供ShardingDataSource和MasterSlaveDateSource使用的规则对象
ShardingDateSource和MasterSlaveDataSource实现了DataSource接口,是JDBC的完整实现方案
初始化流程
根据配置信息生成Configuration对象
根据Factory会将Configuration转化为Rule对象
通过Factory会将Rule对象与DataSource对象封装
Sharding-JDBC使用DataSource进行分库分表和读写分离操作
使用过程
1. 引入依赖
org.apache.shardingsphere.sharding-jdbc-core
2. 规则配置
可以通过Java,Yaml,Spring命名空间和Spring-Boot-Starter四种方式配置,可根据场景选择适合的配置方式
3. 创建DataSource
通过ShardingDataSourceFactory工厂和规则配置对象获取ShardingDataSource,然后即可通过DateSource选择使用原生JDBC开发,还是使用JPA、MyBatis等ORM工具
核心概念
表
真实表
数据库真实存在的表
逻辑表
分片后,同一类表结构的总称
数据节点
分片后,由数据源和数据表组成
绑定表
指的是分片规则一致的关系表(如:主表、子表)
绑定表之间的多表关联查询不会出现笛卡尔积关联,可以提升关联查询效率
广播表
在使用中,有些表没必要做分片,如字典表等,因为数据量不大,且这种表可能需要与海量数据的表进行关联查询
广播表会在不同的数据节点上进行存储,存储的表结构和数据完全相同
分片算法
由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法
目前提供4种分片算法
精确分片算法
PreciseShardingAlgorithm
用于处理使用单一键值作为分片键的 =、In进行分片的场景
范围分片算法
RangeShardingAlgorithm
景
复合分片算法
ComplexKeysShardingAlgorithm
用于处理使用多键作为分片键进行分片的场景
多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度
Hint分片算法
HintShardingAlgorithm
用于处理使用Hint行分片的场景
对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL_Hint灵活的注入分片字段
如:内部系统,按照员工登录主键分库,而数据库中并无此字段
SQL_Hint支持通过Java API和SQL注释两种方式使用
分片策略包含分片键和分片算法,真正可用于分片操作的是分片键+分片算法,也就是分片策略
目前提供5中分片策略
标准分片策略
StandardShardingStrategy
持
提供Precise和Range两个分片算法
Precise是必选的,Range是可选的
但是SQL中使用了范围操作,如果不配置Range会采用全库路由扫描,效率低
复合分片策略
ComplexShardingStrategy
由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度
行表达式分片策略
InlineShardingStrategy
只支持单分片键,使用Groovy表达式,提供对SQL中的=、In的分片操作支持
对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发
Hint分片策略
HintShardingStrategy
通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略
不分片策略
NoneShardingStrategy
不分片的策略
配置
对于分片策略存有数据源分片策略和表分片策略两种维度,两种策略的API完全相同
数据源分片策略
用于配置数据被分配的目标数据源
表分片策略
用于配置数据被分配的目标表,由于表存在于数据源内,所以表分片策略是依赖数据源分片策略结果的
流程剖析
1. SQL解析
分为词法解析和语法解析
先通过词法解析器将SQL拆分为一个个不可再分的单词
再使用语法解析器对SQL进行理解,并最终提炼出解析上下文
采用不同的解析器对SQL进行解析
MySQL解析器
Oracle解析器
SQLServer解析器
PostgreSQL解析器
默认SQL解析器
2. 查询优化
负责合并和优化分片条件,如OR
3. SQL路由
根据解析上下文匹配用户配置的分片策略,并生成路由路径
目前支持分片路由和广播路由
4. SQL改写
将SQL改写为在真实数据库中可以正确执行的语句
SQL改写分为正确性改写和优化改写
5. SQL执行
通过多线程执行器异步执行SQL
6. 结果归并
将多个执行结果集归并以便于通过统一的JDBC接口输入
包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式
使用规范
SQL使用规范
支持项
路由至单数据节点时,目前MySQL数据库100%全兼容,其他数据库完善中
路由至多数据节点时,全面支持DQL、DML、DDL、DCL、DTL
支持分页、去重、排序、分组、聚合、关联查询(不支持跨库关联)
不支持项
不支持CASE When、Having、Union(ALL)
支持分页子查询,但其他子查询有限支持,无论嵌套多少层,只能解析第一个包含数据表的子查询,一旦在下层嵌套中再次找到包含数据表的子查询将直接抛出解析异常
简单来说,通过子查询进行非功能需求,大部分情况是可以支持的
如分页、统计总数等;而通过子查询实现业务查询当前并不能支持
由于归并的限制,子查询中包含聚合函数目前无法支持
不支持包含schema的SQL
因为ShardingSphere的理念是像使用一个数据源一个使用多数据源,因此对SQL的访问都是在同一个逻辑schema上
当分片键处于运算表达式或函数中的SQL时,将采用全路由的形式获取结果
由于ShardingSphere只能通过SQL字面提取用于分片的值,因此当分片键处于运算表达式或函数中时,无法提前获取分片键处于数据库中的值,从而无法计算出真正的分片值
分页查询
完全支持MySQL和Oracle的分页查询,SQLServer由于分页查询较为复杂,仅部分支持
性能瓶颈
查询偏移量过大的分页会导致数据库获取数据性能低下
优化
ShardingSpherer进行了2个方面的优化
采用流式处理+归并排序的方式来避免内存的过量占用
对仅落至单节点的查询进行进一步优化
方案优化
由于Limit不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案
d
其他功能
Inline行表达式
InlineShardingStrategy采用Inline行表达式进行分片的配置
Inline可以简化数据节点和分片算法配置信息
主要是解决配置简化、配置一体化
语法
在配置中使用${expression}或$-{expression}标识行表达式即可
行表达式中如果出现多个${}或$-{}表达式,整个表达式结果会将每个子表达式结果进行笛卡尔组合
分片算法配置
行表达式内部的表达式本质上是一段Groovy代码,可以根据分片键进行计算的方式,返回相应的真实数据源或真实表名称
ShardingSphere不仅提供了内置的分布式主键生成器,如UUID,SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器
内置主键生成器
采用UUID.randomUUID()的方式产生分布式主键
在分片规则配置模块可配置每个表的主键生成策略,默认使用雪花算法,生成64bit的长整型数据
自定义主键生成器
自定义主键类,实现ShardingKeyGenerator接口
按SPI规范配置自定义主键类
在META-INF/services下
自定义主键类应用配置
读写分离是通过主从的配置方式,将查询请求均匀的分散到多个数据副本,进一步的提升系统的处理能力
读写分离,目的是高可用、读写扩展
主从库内容相同,根据SQL语义进行路由
分库分表架构
数据分片,目的是读写扩展、存储扩容
库、表内容不同,根据分片配置进行路由
将水平分片和读写分离联合使用,可以更加有效的提升系统性能
读写分离虽然可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题,包括多个主库之间的数据不一致,以及主库和从库之间的数据一致性的问题
读写分离也带来了与数据分片同样的问题,会使得应用开发和运维人员对数据库的操作和运维变得更加复杂
应用方案
在数据量不是很多的情况下
可以将数据库进行读写分离,以应对高并发的需求,通过水平扩展从库,来缓解查询的压力
在数据量达到500W的时候
这时数据量预估千万级别,可以将数据进行分表存储
数据量继续扩大
可以考虑分库分表,将数据存储在不同数据库的不同表中
目标
透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库集群,是ShardingSphere读写分离模块的主要设计目标
核心功能
提供一主多从的读写分离配置
仅支持单主库,可以支持独立使用,也可以配合分库分表使用
独立使用读写分离,支持SQL透传
不需要SQL改写流程
同一线程且同一数据库连接内,能保证数据一致性
如果有写操作,后续的读操作均从主库读取
基于Hint的强制主库路由
可以强制路由走主库查询实时数据,避免主从同步数据延迟
主库和从库的数据同步
主库和从库的数据同步延迟
主库双写或多写
跨主库和从库之间的事务的数据不一致
建议在主从架构中,事务中的读写均用主库操作
强制路由
在一些应用场景中,分片条件并不存在与SQL,而存在与外部业务逻辑,因此需要提供一种通过在外部业务代码中指定路由配置的方式,在ShardingSphere中叫做Hint
如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作
场景
数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中
读写分离操作,如果强制在主库进行某些数据操作
1. 编写分库或分表路由策略,实现HintShardingAlgorithm接口
2. 在配置文件指定分库或分表策略
3. 在代码执行查询前使用HintManager指定执行策略
在读写分离结构中,为了避免主从同步数据延迟及时获取刚添加或更新的数据,可以采用强制路由走主库查询实时数据,使用hintManager.setMasterRouteOnly设置主库路由即可
HintManager
主要使用ThreadLocal管理分片键信息,进行hint强制路由
在代码中向HintManager添加的配置信息只能在当前线程内有效
数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护
涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号等个人信息,按照规定,都需要进行数据脱敏
数据脱敏模块属于ShardingSphere分布式治理这一核心功能下的子功能模块
在更新操作时,它通过对用户输入的SQL进行解析,并依据用户提供的脱敏配置对SQL进行改写,从而实现对原文数据进行加密,并将密文数据存储到底层数据库
在查询数据时,它从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用户
Apache_ShardingSphere自动化&透明化了数据脱敏过程,让用户无需关注数据脱敏的实现细节,像使用普通数据那样使用脱敏数据
整体架构
ShardingSphere提供的Encrypt-JDBC和业务代码部署在一起。业务方需要面向Encrypt-JDBC进行JDBC编码
Encrypt-JDBC将用户发起的SQL进行拦截,并通过SQL语法解析器进行解析、理解SQL行为,再依据用户传入的脱敏规则,找出需要脱敏的字段和所使用的加解密器对目标字段进行加解密处理后,再与底层数据库进行交互
脱敏规则
数据源配置
指DataSource的配置信息
加密器配置
使用什么加密策略进行加解密
目前内置了两种加解密策略
AES/MD5
脱敏表配置
cipherColumn
用于存储密文数据
真实列,选填
plainColumn
存储明文数据
logicColumn
进行SQL编写
逻辑列,面向用户编程,必填
查询数据配置
当底层数据库表里同时存储了明文/密文数据,该属性开关用于决定是直接查询数据库表里的明文数据进行返回,还是查询密文数据通过Encrypt-JDBC解密后返回
query.with.cipher.column
是否使用加密列查询,默认true
处理流程
ShardingSphere将逻辑列与明文/密文列进行了列名映射
加密策略
ShardingSphere提供了两种加密策略用于数据脱敏,分别对应两种加解密接口
Encryptor
通过提供encrypt()、decrypt()对需要脱敏的数据进行加解密
在用户进行Insert、Delete、Update时,会按照用户配置,对SQL进行解析、改写、路由,并会调用encrypt()将数据加密后存储到数据库,查询时,调用decrypt()将取出的数据进行逆向解密
当前,ShardingSphere针对这种方案提供了两种具体实现类,用户只需配置即可使用
MD5 不可逆
AES 可逆
QueryAssistedEncryptor
相对于第一种方案,更为安全和复杂
理念是:即使是相同的数据,在数据库里存储的脱敏数据也应当是不一样的
更有利于保护用户信息,防止撞库成功
提供三种函数进行实现
encrypt()
此阶段,用户设置某个变动种子,如时间戳
针对原始数据+变动种子组合的内容进行加密
decrypt()
依据之前规定的加密算法,利用种子数据进行解密
queryAssistedEncrypt()
用于生成辅助查询列,用于原始数据的查询过程
针对这种方案没有提供具体实现类,而是抽象成接口,提供给用户自行实现,ShardingSphere将调用用户提供的该方案的具体实现类进行数据脱敏
0 条评论
下一页