在今年的上旬,有幸学习了李运华老师的《从0开始学架构》,碰巧最近参加技术群组织的ShardingSphere的源码的分享小组。所以想结合运华老师的学习方法结合我在项目中的经历复盘一下当时我是如何思考和学习ShardingSphere的。
1 “学习金字塔”理论
在我介绍李运华老师的开源系统学习方法之前,我觉得我们应该了解一下何为学习金字塔理论。
美国学者、著名的学习专家爱德加·戴尔在1946年最先提出该理论,美国缅因州的国家训练实验室做过类似的研究,并提出了“学习金字塔”理论。
虽然我从各方信息了解到图中所示的数据并未通过严谨的实证研究推出,但是金字塔理论可以说是认知发展理论、信息加工理论、人本主义学习理论和建构主义理论在教学方面的具体化,通过运用金字塔理论,最终使学习到的知识向能力转化。
运华老师通过自身的学习和10多年在研发上的经验总结出的开源系统学习方法就是结合金字塔理论而成的方法论,通过运用这个学习方法使我们在开源系统的运用上做得更好。
2 李运华老师的开源系统学习方法
老师在专栏中把学习方法分为以下5个步骤
2.1 概要学习
-
目的
初步掌握基本信息、技术本质、应用场景
- 方法
看文档、看文章
- 技巧
官方文档的Introduction和Get started
2.2 安装运行
-
目的
熟悉基本使用,包括安装配置、架构模式
- 方法
对照官方文档搭建简单的环境
- 技巧
研究命令行和配置项
2.3 模拟场景
-
目的
通过模拟案例掌握如何应用
- 方法
模拟不同的业务场景,设计对应的方案,能写Demo的就写Demo尝试
- 技巧
结合自己的业务
2.4 深入研究
-
目的
从技术深度和技术宽度两个维度来全方位的学习
- 方法
看文档、看源码、看文章等
- 技巧
1. 链式学习法
2. 比较学习法
2.4.1 源码怎么看
- 跨语言的看不懂就别看
不懂对应的语言的别去看源码
- 带着目的去看
先按照链式学习法学习相关的原理和方案,再去看源码是如何实现原理或者方案的
- 看核心实现
1. 不要逐行去看,看关键实现
2. 先看网上已有的文章
2.5 分享知识
-
目的
通过分享帮助自己体系化和加深理解,增强记忆
- 方法
写文章、培训、演讲
- 技巧
1. 梳理补充、提炼总结官方文档
2. 对比类似系统
3 我的ShardingSphere学习过程
3.1 什么是Apache ShardingSphere
通过官网我们可以知道
Apache ShardingSphere 产品定位为
Database Plus
,旨在构建多模数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。ShardingSphere 站在数据库的上层视角,关注他们之间的协作多于数据库自身。
连接
、增量
和可插拔
是 Apache ShardingSphere 的核心概念。
连接
:通过对数据库协议、SQL 方言以及数据库存储的灵活适配,快速的连接应用与多模式的异构数据库;增量
:获取数据库的访问流量,并提供流量重定向(数据分片、读写分离、影子库)、流量变形(数据加密、数据脱敏)、流量鉴权(安全、审计、权限)、流量治理(熔断、限流)以及流量分析(服务质量分析、可观察性)等透明化增量功能;可插拔
:项目采用微内核 + 三层可插拔模型,使内核、功能组件以及生态对接完全能够灵活的方式进行插拔式扩展,开发者能够像使用积木一样定制属于自己的独特系统。
3.1.1 ShardingSphere-JDBC是什么
定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
- 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
- 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
- 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。
即通过引入ShardingSphere-JDBC在项目中并进行初始化后我们既可以使用Apache ShardingSphere的增量功能,从官网提供文档来看,ShardingSphere-JDBC支持几乎目前市面使用的基于JDBC的ORM框架、数据库连接池和数据库。从具体实施者的角度来看,较好的兼容性的开源框架容易避开兼容性问题带来的工作延误。从架构师的角度来看,较好的兼容性可以方便日后对整个框架的拓展。
3.1.2 ShardingSphere-Proxy是什么
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL(兼容 openGauss 等基于 PostgreSQL 的数据库)版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。
- 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用;
- 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。
3.1.3 总结
通过GitHub的发版信息我们可以知道目前Apache ShardingSphere的主要产品是ShardingSphere-JDBC和ShardingSphere-Proxy,ShardingSphere-JDBC 采用无中心化架构,与应用程序共享资源,适用于 Java 开发的高性能的轻量级 OLTP 应用; ShardingSphere-Proxy 提供静态入口以及异构语言的支持,独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。通过ShardingSphere-JDBC和ShardingSphere-Proxy的混合使用,使我们在构建分库分表的系统之余,不需要花费更多的精力在研究如何对分库分表的数据进行访问。
3.2 试运行ShardingSphere-Proxy
运华老师的安装运行这个步骤对象适合是对可执行的应用进行了解,这里我就贴一下我在项目上线后自己尝试ShardingSphere-Proxy的内容配置,由于ShardingSphere在5.0之后对配置进行了修改,我这里贴出的是基于ShardingSphere-Proxy 5.0的运行尝试
apache-shardingsphere-5.0.0-shardingsphere-proxy
配置并成功启动shardingsphere-proxy
测试使用shardingsphere-proxy进行查询
22:34:46.247 [Connection-1-ThreadExecutor] INFO ShardingSphere-SQL - Logic SQL: select * from eshop_address where city_code=0
22:34:46.247 [Connection-1-ThreadExecutor] INFO ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
22:34:46.247 [Connection-1-ThreadExecutor] INFO ShardingSphere-SQL - Actual SQL: ds_0 ::: select * from eshop_address_0 where city_code=0 ORDER BY address_id ASC
22:34:46.247 [Connection-1-ThreadExecutor] INFO ShardingSphere-SQL - Actual SQL: ds_0 ::: select * from eshop_address_1 where city_code=0 ORDER BY address_id ASC
3.2.1 总结
ShardingSphere-Proxy作为数据库中间件,从目前来看它的对标产品是MyCat,他的配置内容和ShardingSphere-JDBC基本一致,但是从官网了解到ShardingSphere-Proxy没有官方的集群方案,在实际业务使用中实现proxy端的高可用可能就需要额外的开发,我感觉公司没有到一定富裕的开发能力,ShardingSphere-Proxy可能会适合搭建用于给DBA管理数据库内容和内部给统计部门进行数据结构化分析。
3.3 搭建ShardingSphere-JDBC-Demo,模拟项目场景
更多情况下开源框架的运行和场景步骤模拟是同时进行的
3.3.1 搭建模拟场景
2021-12-21 21:03:12.207 INFO 33418 --- [nio-8080-exec-1] ShardingSphere-SQL : Logic SQL: select a.address_id,a.user_id,a.address_name,
a.country_code,
a.phone,a.province_code,
a.city_code,
a.region_code,
a.detail,a.create_user_id,a.create_time,a.modify_user_id,a.modify_time
from eshop_address a
where a.city_code=?
and a.region_code=?
2021-12-21 21:03:12.207 INFO 33418 --- [nio-8080-exec-1] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
2021-12-21 21:03:12.207 INFO 33418 --- [nio-8080-exec-1] ShardingSphere-SQL : Actual SQL: db0 ::: select a.address_id,a.user_id,a.address_name,
a.country_code,
a.phone,a.province_code,
a.city_code,
a.region_code,
a.detail,a.create_user_id,a.create_time,a.modify_user_id,a.modify_time
from eshop_address_0 a
where a.city_code=?
and a.region_code=? ::: [0, 2]
通过事先配置的路由规则查找指定库指定表
在实际开发当中一般是多个项目使用同一个基础组件,因此在做架构演进的时候我们要考虑到对不同项目的影响,新追加的内容在完成新的需求下还要尽量少的影响旧项目,因此在实际把ShardingSphere-JDBC引入基础组件的时候不会像我演示代码那样直接重写datasource的初始化,而是通过@ConditionalOnProperty、@ConditionalOnBeand的方式来控制初始化。
3.3.2 小结
ShardingSphere-JDBC是一款SDK的数据库中间件,在应用上已经满足当时我们需要对每个接入集团数据进行分库的需求,只要当我们在系统设计中对数据库设计加入集团区别标识字段,当查询的时候携带识别标识就能在查询内容的时候实现集团识别,从而拉取数据。而高可用方面因为他是SDK引入到服务当中,因此只要我们的服务实现了高可用,那它就天然是高可用的。
ShardingSphere-JDBC在项目中并进行初始化后我们既可以使用Apache ShardingSphere的增量功能,从官网提供文档来看,ShardingSphere-JDBC支持几乎目前市面使用的基于JDBC的ORM框架、数据库连接池和数据库。从具体实施者的角度来看,较好的兼容性的开源框架容易避开兼容性问题带来的工作中延误。从架构师的角度来看,较好的兼容性可以方便日后对整个框架的拓展。
3.4 善用网络资源、参与兴趣小组,深入学习
几乎所有情况下我们在架构进行演进的时候都会使用成熟的开源系统,这就代表它肯定成功落地,互联网上会有对应的内容分享,如果没有互联网上的内容只能死啃源码了。
3.4.1 深入学习ShardingSphere
我们项目使用的ShardingSphere算是数据库方面比较火的中间件,因此视频还是文字都有比较多的内容,我们通过互联网的内容我们能很快的了解到ShardingSphere的分片引擎结构为解析引擎、路由引擎、改写引擎、执行引擎和归并引擎:
再通过我们在源码上的学习和交流群众的交流,我们可以得到一次SQL在路由引擎中源码
PartialSQLRouteExecutor
public RouteContext route(final LogicSQL logicSQL, final ShardingSphereMetaData metaData) {
RouteContext result = new RouteContext();
for (Entry<ShardingSphereRule, SQLRouter> entry : routers.entrySet()) {
if (result.getRouteUnits().isEmpty()) {
result = entry.getValue().createRouteContext(logicSQL, metaData, entry.getKey(), props);
} else {
entry.getValue().decorateRouteContext(result, logicSQL, metaData, entry.getKey(), props);
}
}
if (result.getRouteUnits().isEmpty() && 1 == metaData.getResource().getDataSources().size()) {
String singleDataSourceName = metaData.getResource().getDataSources().keySet().iterator().next();
result.getRouteUnits().add(new RouteUnit(new RouteMapper(singleDataSourceName, singleDataSourceName), Collections.emptyList()));
}
return result;
}
通过源码发现进行路由的时候,会根据我们的配置进行路由运算,举我们demo的例子为例它采用的是InlineShardingAlgorithm(基于行表达式的分片算法)得到目标库后,进行查询的时候就会通过目标库的的tag去映射出对应的读写分离数据库。
显然修改github上的源码重新打包是一种解决方案,但是我们通过学习知道ShardingSphere可以通过SPI实现拓展,下面我会通过实现一个可能的需求来进行展示。
3.4.2 预演需求
这里有个很常见的需求就是提供的数据库可能性能不一样,在做读写分离的时候,希望读库的时候可以根据实际数据库性能进行加权的负载均衡。
目前我们通过分析源码可知目前框架只提供了轮询和随机两种负载均衡方式
因此我们需要通过在META-INF/services/org.apache.shardingsphere.readwritesplitting.spi.ReplicaLoadBalanceAlgorithm进行SPI来扩展我们自己的负载均衡方法,详细可以参考
这样可以用更优雅和兼容的方式来实现,也避免后续的版本升级会因为前任的暴力实现而带来太多的阻碍。
3.4.3 小结
一般来说一个开源框架的使用进行到第三步就够了,为什么还有第四步,在我看来有两面:在公,如性能调优、特殊功能拓展一般只能通过公司内部实现,没有深入的了解是无法进行的,如果我没去了解ShardingSphere的SPI拓展,可能会绕很大的圈去实现它。在私,优秀的开源框架都拥有优秀的设计,我们通过深入学习这些框架能扩展自己的设计思路。
3.5 内化知识形成内容
在教授他人的过程中,自己必定要真正掌握这个知识,还要有能力透过语言的呈现进行讲解,是知识向能力转化,因此我除了写下这篇博客以外我还写下了:
记开源系统落地-我是如何在工作中应用ShardingSphere-JDBC
3.5.1 小结
第五步则是第四步的延伸,随着逐步深入的学习,我们能慢慢的勾勒出整个架构的全貌,当我们选择给组内进行培训和外部交流的时候,我们就更需要充分理解我们所使用的框架。最终使自己成为这个框架的专家。
4 后记
运华老师的开源系统学习方法是一个不断迭代、不断深化的过程。在不同场景使用后,我们会提出各种猜想和问题,通过对源码和原理的学习我们可以解答这样的猜想和问题,通过在公司和博客上的分享,可以加深我们对系统的理解,通过周而复此的循环来提高我们技术的层次。希望在这样的学习循环中,我最终能成为独当一面的架构师。
参考:https://shardingsphere.apache.org/document/current/cn/overview/