一次sharding-jdbc 5.0 踩坑历程
Posted 烧霞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次sharding-jdbc 5.0 踩坑历程相关的知识,希望对你有一定的参考价值。
这天霞妹又来找烧哥了。
“烧哥,帮我看下这个分库分表,升级5.0后不能用了呢?”
“Let me c c?哇,Sharding-jdbc刚推出的最新版?”
背景
关系数据库当今依然占有巨大市场份额,前期通常会存储至单一节点。为提高性能和可用性,一种方案是迁移到NoSQL ,但会有较大技术成本。另一种则考虑数据分片,按照某个维度将数据分散地存储到多个库或表,来有效的避免大数据量产生的查询瓶颈。
数据分片也有两种。垂直分片讲究专库专用,通常按业务模块划分。水平分片则是按字段,通过某种规则拆分到不同库或表。通过搭建多主多从的数据库架构,读写分离,配合水平拆分,实际场景中较为常见。
ShardingSphere则同时提供了这两种解决方案,2020.4.16成为 Apache 软件基金会的顶级项目。Sharding-jdbc作为子产品,以Jar包形式提供服务,可理解为增强版的 JDBC 驱动,能够几乎不改动代码的情况下实现架构迁移,2021.11.10推出了5.0.0版。
问题重现
首先看之前的配置文件:
pom.xml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
application.yml
spring:
shardingsphere:
datasource:
names: master1,slave1
master1:
driver-class-name: com.mysql.cj.jdbc.Driver
password:
type: com.alibaba.druid.pool.DruidDataSource
url:
username:
slave1:
driver-class-name: com.mysql.cj.jdbc.Driver
password:
type: com.alibaba.druid.pool.DruidDataSource
url:
username:
props:
sql:
show: true
sharding:
tables:
b_gcg_content:
actual-data-nodes: ds.b_gcg_content_$->0..1
table-strategy:
inline:
sharding-column: content_id
algorithm-expression: b_gcg_content_$->content_id % 2
key-generator:
column: content_id
type: SNOWFLAKE
master-slave-rules:
ds:
load-balance-algorithm-type: round_robin
master-data-source-name: master1
slave-data-source-names: slave1
主库master1,从库slave1,对content表根据content_id取模拆分为两个表。
查询,插入,运行正常。
再看之后的:
pom.yml
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.0.0</version>
</dependency>
application.yml
spring:
shardingsphere:
datasource:
names: master1,slave1
master1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl:
username:
password:
slave1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl:
username:
password:
props:
sql-show: true
rules:
sharding:
tables: # 数据分片规则配置
b_gcg_content: # 逻辑表名称
actualDataNodes: ds.b_gcg_content_$->0..1 # 由数据源名 + 表名组成(参考Inline语法规则)
tableStrategy: # 分表策略,同分库策略
standard: # 用于单分片键的标准分片场景
shardingColumn: content_id # 分片列名称
shardingAlgorithmName: my # 分片算法名称
keyGenerateStrategy: # 分布式序列策略
column: content_id # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: my # 分布式序列算法名称
shardingAlgorithms:
my: # 分片算法名称
type: INLINE # 分片算法类型
props: # 分片算法属性配置
algorithm-expression: b_gcg_content_$->content_id % 2
keyGenerators:
my: # 分布式序列算法名称
type: SNOWFLAKE # 分布式序列算法类型
readwriteSplitting:
dataSources:
ds:
loadBalancerName: my
writeDataSourceName: master1
readDataSourceNames: slave1
loadBalancers:
my: # 负载均衡算法名称
type: ROUND_ROBIN # 负载均衡算法类型
查询正常,插入时报错:
Insert statement does not support sharding table routing to multiple data nodes.
解谜
1.是否符合官方标准?(不熟悉的话常犯)
首先看到配置文件的语法,升级后有很大改变,根据官方文档挨个排查,确认格式全部正确。
2.是否新版本有缺陷?(最近经常有新闻,xx又有重大漏洞)
搜索这个报错,全网都没有。到官方仓库issue,有大把。引起的原因有很多,但都已修复。
那就下最新源码,5.0.1-SNAPSHOT,重新编译放入私服,岂不简单?
编译比较麻烦,不过最终还是成功了,更改maven版本,奇怪了,依然报错。
3.化繁为简,缩小范围,精准定位(首要思路)
报错中提到了data nodes,配置里同时有读写分离和分表,那就去掉一个,只要分表
rules:
sharding:
tables: # 数据分片规则配置
b_gcg_content: # 逻辑表名称
actualDataNodes: master1.b_gcg_content_$->0..1 # 由数据源名 + 表名组成(参考Inline语法规则)
tableStrategy: # 分表策略,同分库策略
standard: # 用于单分片键的标准分片场景
shardingColumn: content_id # 分片列名称
shardingAlgorithmName: my # 分片算法名称
keyGenerateStrategy: # 分布式序列策略
column: content_id # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: my # 分布式序列算法名称
shardingAlgorithms:
my: # 分片算法名称
type: INLINE # 分片算法类型
props: # 分片算法属性配置
algorithm-expression: b_gcg_content_$->content_id % 2
keyGenerators:
my: # 分布式序列算法名称
type: SNOWFLAKE # 分布式序列算法类型
还是报错。
那不分表总可以吧?再来
rules:
sharding:
tables: # 数据分片规则配置
b_gcg_content: # 逻辑表名称
actualDataNodes: master1.b_gcg_content # 由数据源名 + 表名组成(参考Inline语法规则)
tableStrategy: # 分表策略,同分库策略
standard: # 用于单分片键的标准分片场景
shardingColumn: content_id # 分片列名称
shardingAlgorithmName: my # 分片算法名称
keyGenerateStrategy: # 分布式序列策略
column: content_id # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: my # 分布式序列算法名称
shardingAlgorithms:
my: # 分片算法名称
type: INLINE # 分片算法类型
props: # 分片算法属性配置
algorithm-expression: b_gcg_content
keyGenerators:
my: # 分布式序列算法名称
type: SNOWFLAKE # 分布式序列算法类型
这下倒是可以,看来的确是分表有问题。
4.跟正常的对比,由外到内,等价替换(廉价快速手段)
那官方demo不会也报错吗?apache出品的不至于吧?怀疑人生。
4.x的shardingsphere-example项目已经停更,新demo合并到了主库。
但官方demo是正常的?那就蹊跷了。
难道我的表不行?换成demo的库试试,还是报错,奇葩。
再来看看上面配置,唯一的区别就在于名称改成了my,难不成名称是不能改的??
改成跟demo一样名称,竟然行了。。。
5.避坑
spring:
shardingsphere:
datasource:
names: master1,slave1
master1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl:
username:
password:
slave1:
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl:
username:
password:
props:
sql-show: true
rules:
sharding:
tables: # 数据分片规则配置
b_gcg_content: # 逻辑表名称
actualDataNodes: ds.b_gcg_content_$->0..1 # 由数据源名 + 表名组成(参考Inline语法规则)
tableStrategy: # 分表策略,同分库策略
standard: # 用于单分片键的标准分片场景
shardingColumn: content_id # 分片列名称
shardingAlgorithmName: my-table # 分片算法名称
keyGenerateStrategy: # 分布式序列策略
column: content_id # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: my-key # 分布式序列算法名称
shardingAlgorithms:
my-table: # 分片算法名称
type: INLINE # 分片算法类型
props: # 分片算法属性配置
algorithm-expression: b_gcg_content_$->content_id % 2
keyGenerators:
my-key: # 分布式序列算法名称
type: SNOWFLAKE # 分布式序列算法类型
readwriteSplitting:
dataSources:
ds:
loadBalancerName: my-load
writeDataSourceName: master1
readDataSourceNames: slave1
loadBalancers:
my-load: # 负载均衡算法名称
type: ROUND_ROBIN # 负载均衡算法类型
“霞妹可以啦”
“烧哥好棒,我这有张券,中午一起嘛”
填坑
架构师的工作就是解决各种疑难杂症,思路的锻炼来自长期实战经历。
Apache的品质总体还是可信赖的,这算个小问题。源码应该是把某些名称放在了一个map下,或者是缓存时出了岔子,有空去提个issue.
又增加一条规范:复杂配置文件中,自定义名称不应该重复。
技术访谈:Sharding-JDBC 未来将更加多样化
Sharding-JDBC 是当当网开源的适用于微服务的分布式数据访问基础类库,完整的实现了分库分表,读写分离和分布式主键功能,并初步实现了柔性事务。从 2016 年开源至今,在经历了整体架构的数次精炼以及稳定性打磨后,如今它已积累了足够的底蕴。它是如何变得如此优秀?未来,它还会带来哪些新的突破呢?本期【开源访谈】邀请到当当架构部总监张亮,为大家介绍 Sharding-JDBC 的发展历程,分享职业发展经验。
记者|开源中国
受访者|张亮
编写|@OSC源创君
问答
请简单介绍一下您自己,如,学习和工作经历
我很高兴能接受这次采访。我目前就职于当当架构部,负责公司的中间件研发和容器调度编排平台的搭建。除了处理公司内部的事务,也负责当当对外开源和分享的相关事宜。
我大学是在爱尔兰读的书,毕业后一直在外企工作。我个人对技术比较感兴趣,在技术选型方面也比较激进,但外企的技术相对稳定。因此我在 2012 年的时候开始转型做互联网,在当当之前就职于百度,我个人比较喜欢互联网这种快速迭代的节奏和浓厚的技术氛围。
当当网于 2016 年开源了 Sharding-JDBC
并取得了一致好评,选择开源的初衷是什么?
谢谢夸奖。Sharding-JDBC 开源至今虽然得到了一些关注,但是考虑到数据分片解决方案的复杂性,它未来还有大量的事情要做。
Sharding-JDBC 是从当当的一个内部系统演化而来,它的前身叫 dd-rdb,是当当的应用框架 ddframe 的一个组成模块。dd-rdb 采用 spring 的动态数据源和简单的 SQL 解析来实现分库和分表的功能,并提供 DAO 的标准化模板。随着当当对数据分片的需求越来越多,单纯的 dd-rdb 已无法再满足,因此将其标准化模板和分片功能独立出来的呼声就越来越高。
我们在做 Sharding-JDBC 之前做了很多调研,希望能够找到开源数据分片的解决方案。但调研之后发现目前开源解决方案的完整性和成熟度都难于完全满足互联网公司的需求,所以只能自研。
关于开源的初衷,主要从三方面考虑。一是希望可以让更多优秀的程序员看到我们的代码,相互促进和交流;二是在调研时受到了社区开源项目的启发,因此我们愿意回馈社区;三是来调动当当内部工程师的积极性,要知道,工程师们对内部框架的兴趣和开源框架的兴趣是完全不同的。
Sharding-JDBC 从开发伊始就以开源为目标,因此从大到整体架构设计的合理性,小到代码的可读性和优雅性,都及其花费精力雕琢,核心开发人员的积极性也非常高。
Sharding-JDBC 在 2017 年取得了哪些进展?未来的开发计划是什么?
从 2016 年开源至今,可以划分为两个阶段,这两个阶段恰好可以按照自然年去划分。
2016 年以及 2016 年以前的 Sharding-JDBC 属于初级阶段。Sharding-JDBC 虽然于 2016 年初开源,但它的核心开发基本是 2015 年完成的,初始版本大概开发了 3 个月左右。整个 2016 年,Sharding-JDBC 都在完善功能的阶段,每次大版本的发布都是一个新的功能点,主要有分库分表、配置多样化、柔性事务、读写分离和分布式主键。在 2016 年底,Sharding-JDBC 的 1.4.2 版本的发布标志着第一阶段的结束。
2017 年上半年 Sharding-JDBC 进入了一个相对静默期,因为我们觉得之前版本太过注重功能,导致最核心的解析模块难于控制。因此,我们花费了近半年的时间,以完全自研的方式重写了 SQL 解析引擎,并根据全新的解析引擎重新设计了 SQL 路由、SQL 重写和结果归并模块,让它看起来更加浑然一体。并于 2017 年的 6 月份发布了 1.5.0.M1 这一里程碑版本,使得 Sharding-JDBC 的架构得到进一步改善,稳定性和可控性也都得到了有效的提升。虽然目前 Sharding-JDBC 已经发布了 2.x 版本,但是我们愿意认为 1.5.x 版本才是 Sharding-JDBC 脱胎换骨的里程碑。
在不久之前,确切的说是两个月之前,2017年10月,Sharding-JDBC 正式发布了 2.0.0.M1 版本。在 2.x 版本中,Sharding-JDBC 完全产品化,完全摆脱掉了 dd-rdb 的束缚,将包名从 com.dangdang.ddframe.rdb 改为 io.shardingjdbc。我们希望全新的包名,让 Sharding-JDBC 能够更加中立化的发展,汲取社区更多的支持。
由于 1.5.x 使得 Sharding-JDBC 在 SQL 兼容性相关部分已经趋于成熟,2.x 我们将目光放在了微服务开发框架之上,增加了治理,配置动态化等功能。虽然当今微服务概念火热,Spring Cloud 等微服务框架也收到很多关注,但在数据层面,并没有一套行之有效的配套方案,希望 Sharding-JDBC 可以弥补这一领域的缺失。
关于未来开发的计划,先来澄清一件事。很多不是 Java 开发背景的同学会吐槽为什么 Sharding-JDBC 是一个应用层的框架,而不是独立部署的 Proxy,其实一个很重要的原因就是它的前身,dd-rdb 是应用框架。因此未来 Sharding-JDBC 也会多样化,将提供 Proxy 的版本。另外除了 Proxy,也会提供 Agent 版本。所谓的 Agent 版本与现在流行的 Service Mesh 模式较为相似,Service Mesh 通过 sidecar 去代理和管控流量,而 Sharding-JDBC 希望通过同样的sidecar的方式代理和管控 SQL,以做到非侵入和跨语言的 Data Mesh。
您现在除了架构技术还有管理方面的工作,您是如何平衡协调的?
管理架构部的工作和管理项目不同。
架构师或者中间件开发工程师,有独立自主的想法以及正确的眼光比较重要。因此在大部分的管理工作中,我会尽量花时间去沟通和把控其他工程师或架构师做事情的方向选择层面。一旦确认了方向的正确性,做事情的细节我就不再会过分关注,而是给予他们工作的自主权。工作交付之前会约定几个里程碑,通过里程碑来验收工作成果,以把控工作的方向和节奏。在管理之外,我会加入到大家之中,一同投入到开发的工作。我比较享受写代码的乐趣。
在实际工作中,您是如何推动架构工作的进行,或帮助团队架构同学的成长呢?
在当当进行底层基础架构的推动,我们主要采用的方式是兼容和增量。以 Sharding-JDBC 来举例,它是完全兼容 JDBC 协议的,因此开发人员完全不用改动他们的代码,就可以轻松使用 Sharding-JDBC,而 Sharding-JDBC 给他们带来的增量是显而易见的,即再也不用考虑数据分片的相关事宜,只要将多库多表当成一个逻辑库表使用即可。再举一个例子,Elastic-Job 是当当开源的另一个项目。当当在分布式架构向平台化迁移时,之前使用 Elastic-Job 的开发团队,可以完全使用之前的接口,业务应用无需改动,即可将其放入基于 Mesos 的作业云平台,带来的增量是应用开发再也无需关注硬件资源和监控配置,并且作业云带来了作业的自愈能力。
帮助架构同学的成长问题,主要是提供足够开放的平台。现在各种社区分享非常多,如果想去学习,想去进步,并不会发愁找不到资源,因此我能做的就是让大家保持开放的心态,参与交流,参与分享。
听说您在如何写出具有展现力的代码方面有较多研究。能否给大家传授一些技巧?
推荐两本如何写代码的书籍,《重构》和《代码整洁之道》,我个人是从读这两本书,遵循其中的一些理念,然后渐渐的觉得写好代码是一件这么有意思的事情。
我所认同的代码分为两个级别。
级别一是我给自己的代码定制的准入标准,主要要求两点,可读和整洁。可读要求代码没有阅读障碍,像读自然语言一样阅读代码;整洁包括风格保持一致,无多余和重复代码等。
级别二则更难达到一些,评判的标准是优雅。它不但要求代码能够像自然语言一样易懂,还能够像读诗或散文一样心旷神怡,使代码的整体结构和具体细节浑然一体。
从事架构相关工作多年,您拥有非常丰富的职业经验,能否为中国的开发人员提供一些关于职业发展方面的建议?
我觉得技术人员应该保持对技术的好奇心和独立思考的能力。
IT技术发展太快,无论是深度还是广度,想 100% 了解是不可能的,但是也没必要自己为自己设置一道墙,来划分什么是该学的,什么是不该学的。技术的积累不是一次性的,经过大量的积累才能够在一定的条件下得到催化和爆发。因此,请保持强烈的好奇心,尽量多关注一下为什么。
独立思考的能力也是技术人员的必备素质。它也可以等同于耐心和敬畏精神。面对一项技术,如果它短期内展现的行为与自己的预期不符合,不应该急躁和草率的认为是别人的问题,最好静下心来从自己的方面多找找原因,如是否理解的有偏差等。现在社区很发达,很多经验不多的工程师也可以接触到各种大牛。希望处在快速成长中的工程师能够通过自己的思考而提出真正有价值的问题,而不是 Google 到的问题。
来源:开源问答
注明:本文已经授权转载,如想转载,请联系原作者。
微信ID: LightenArch
长按左侧二维码关注
以上是关于一次sharding-jdbc 5.0 踩坑历程的主要内容,如果未能解决你的问题,请参考以下文章