引言
前面《MySQL主从原理篇》、《MySQL主从实践篇》两章中聊明白了MySQL主备读写分离、多主多写热备等方案,但如果这些高可用架构依旧无法满足业务规模,或业务增长的需要,此时就需要考虑选用分库分表架构。
在分库分表领域中,其实有许许多多的一些落地技术栈,如TDDL、TSharding、Sharding-Sphere、MyCat、Atlas、Oceanus、Vitess.....,但经时间沉淀与岁月洗礼后,如今主流的方案也就剩下了MyCat、Sharding-Sphere两种,MyCat近几年由于某些原因,开始逐渐走下坡路,反观投入Apache怀抱的Sharding-Sphere热度逐步上升,其目前的最新版本也相对稳定。
正是由于上述原因,如今越来越多的企业选用Sharding-Sphere作为分库分表的落地方案,因此《全解MySQL专栏》中的分库分表实践篇章,同样会基于Sharding-Sphere这套技术来展开叙述~
PS:个人编写的《技术人求职指南》小册已完结,其中从技术总结开始,到制定期望、技术突击、简历优化、面试准备、面试技巧、谈薪技巧、面试复盘、选Offer方法、新人入职、进阶提升、职业规划、技术管理、涨薪跳槽、仲裁赔偿、副业兼职……,为大家打造了一套“从求职到跳槽”的一条龙服务,同时也为诸位准备了七折优惠码:3DoleNaE,近期需要找工作的小伙伴可以复制链接了解详情:
一、初识Apache-Sharding-Sphere生态
mysql set utf8_mysql set utf8_mysql set utf8
Sharding-Sphere
Apache-Sharding-Sphere的前身是当当网开源的Sharding-JDBC框架,后面引入Zookeeper作为注册中心,又研发了Sharding-Proxy中间件,贡献给Apache软件基金会后,正式整合成Sharding-Sphere生态,并且支持、兼容各种数据库,Apache-Sharding-Sphere官网上可看到的发展历程如下:
mysql set utf8_mysql set utf8_mysql set utf8
发展历程
目前最新的5.2.1版本属于其中5.x阶段的一个版本,目前支持可可拔插功能,其支持的核心功能如下:
mysql set utf8_mysql set utf8_mysql set utf8
核心功能
基本上对于《分库分表副作用篇》中聊到的各类问题,在该版本中都有对应的解决方案,如多表连查、数据分片、分布式事务、数据迁移与扩容、分布式ID.....,在预计2023年发布的6.x版本中会结合各类云容器技术,全面兼容与拥抱云生态,在预计2025年发布的7.x版本中,则会彻底落实Databases-Plus理念,支持将各类数据库作为可拔插式存储引擎使用,也意味着像MySQL的可拔插式引擎那样,在Sharding-Sphere中使用MySQL、Oracle、PgSQL、SQL-Server....等关系型数据。
其实看到这里大家会发现,Sharding-Sphere作为Apache软件基金会的顶级项目,其内部对于它的未来规划十分明确,主要就是构建一个分库分表技术的生态圈,就类似于Spring框架在J2EE中的地位一样,迄今为止已发行的5.x系列版本,已经具备应用于生产环境的能力,其中对于分库分表核心的主干功能都已实现,从官网的发展历程来看,后续版本都属于修修补补的性质,主要是为了让其生态更完善。
Apache-Sharding-Sphere总共由JDBC、Proxy、Sidecar三大核心产品组成,前两者都已具备完整形态,Sidecar还处于开发阶段,这也就代表着目前的ShardingSphere5中只有JDBC、Proxy两款产品,这两款产品即支持各自独立部署,也支持混合部署的模式,两者区别在于:
1.1、Sharding-JDBC框架简介
Sharding-JDBC的定位是一款轻量级Java框架,它会以POM依赖的形式嵌入程序,运行期间会和Java应用共享资源,这款框架的本质可以理解成是JDBC的增强版,只不过Java原生的JDBC仅支持单数据源的连接,而Sharding-JDBC则支持多数据源的管理,部署形态如下:
mysql set utf8_mysql set utf8_mysql set utf8
JDBC部署形态
Java-ORM框架在执行SQL语句时,Sharding-JDBC会以切面的形式拦截发往数据库的语句,接着根据配置好的数据源、分片规则和路由键,为SQL选择一个目标数据源,然后再发往对应的数据库节点处理。
Sharding-JDBC在整个业务系统中对性能损耗极低,但为何后面又会推出Sharding-Proxy呢?因为Sharding-JDBC配置较为麻烦,比如在分布式系统中,任何使用分库分表的服务都需要单独配置多数据源地址、路由键、分片策略....等信息,同时它也仅支持Java语言,当一个系统是用多语言异构的,此时其他语言开发的子服务,则无法使用分库分表策略。
1.2、Sharding-Proxy中间件简介
也正是由于配置无法统一管理、不支持异构系统的原因,后面又引入Sharding-Proxy来解决这两个问题,Sharding-Proxy可以将其理解成一个伪数据库,对于应用程序而言是完全透明的,它会以中间件的形式独立部署在系统中,部署形态如下:
mysql set utf8_mysql set utf8_mysql set utf8
Proxy部署形态
使用Sharding-Proxy的子服务都会以连接数据库的形式,与其先建立数据库连接,然后将SQL发给它执行,Sharding-Proxy会根据分片规则和路由键,将SQL语句发给具体的数据库节点处理,数据库节点处理完成后,又会将结果集返回给Sharding-Proxy,最终再由它将结果集返回给具体的子服务。
但Sharding-Proxy虽然可以实现分库分表配置的统一管理,以及支持异构的系统,但因为需要使用独立的机器部署,同时还会依赖Zookeeper作为注册中心,所以硬件成本会直线增高,至少需要多出3~4台服务器来部署。
同时SQL执行时,需要先发给Proxy,再由Proxy发给数据库节点,执行完成后又会从数据库返回到Proxy,再由Proxy返回给具体的应用,这个过程会经过四次网络传输的动作,因此相较于原本的Sharding-JDBC来说,性能、资源开销更大,响应速度也会变慢。
1.3、JDBC、Proxy混合部署模式
如果用驱动式分库分表,虽然能够让Java程序的性能最好,但无法支持多语言异构的系统,但如果纯用代理式分库分表,这显然会损害Java程序的性能,因此在Sharding-Sphere中也支持JDBC、Proxy做混合式部署,也就是Java程序用JDBC做分库分表,其他语言的子服务用Proxy做分库分表,部署形态如下:
mysql set utf8_mysql set utf8_mysql set utf8
混合部署形态
这种混合式的部署方案,所有的数据分片策略都会放到Zookeeper中统一管理,然后所有的子服务都去Zookeeper中拉取配置文件,这样就能很方便的根据业务情况,来灵活的搭建适用于各种场景的应用系统,这样也能够让数据源、分片策略、路由键....等配置信息灵活,可以在线上动态修改配置信息,修改后能够在线上环境中动态感知。
但Sharding-Sphere还提供了一种单机模式,即直接将数据分片配置放在Proxy中,但这种方式仅适用于开发环境,因为无法将分片配置同步给多个实例使用,也就意味着会导致其他实例由于感知不到配置变化,从而造成配置信息不一致的错误。
二、Sharding-Sphere中的核心概念
分库分表中最重要的核心概念有两个,即路由键和分片算法,这两个将决定数据分片的位置,先稍微解释一下这两个概念:
举个例子来感受一下,好比按user_id将用户表数据分片,每八百万条数据划分一张表,那在这里,user_id就是路由键,而按user_id做范围判断则属于分片算法,一张表中的所有数据都会依据这两个基础,后续对所有的读写SQL进行改写,从而定位到具体的库、表位置。
2.1、分库分表的工作流程
mysql set utf8_mysql set utf8_mysql set utf8
分库分表工作流程
在Sharding-Sphere这套技术中,无论是JDBC还是Proxy产品,工作的流程都遵循上述这个原则,里面除开上面介绍的路由键和分片算法的概念外,还有逻辑表、真实表、数据节点这三个概念:
以Java程序为例,编写业务代码时写的SQL语句,会直接基于逻辑表进行操作,逻辑表并不是一种真实存在的表结构,而是提供给Sharding-Sphere使用的,当Sharding-Sphere接收到一条操作某张逻辑表的SQL语句时,它会根据已配置好的路由键和分片算法,对相应的SQL语句进行解析,然后计算出SQL要落入的数据节点,最后再将语句发给具体的真实表上处理即可。
Sharding-Sphere-JDBC、Proxy的主要区别就在于:解析SQL语句计算数据节点的时机不同,JDBC是在Java程序中就完成了相应的计算,从Java程序中发出的SQL语句就已经是操作真实表的SQL了。而Proxy则是在Java应用之外做解析工作,它会接收程序操作逻辑表的SQL语句。然后再做解析得到具体要操作的真实表,然后再执行,同时Proxy还要作为应用程序和数据库之间,传输数据的中间人。
2.2、Sharding-Sphere中的表概念
除开上述的一些核心概念外,在Sharding-Sphere中为了解决某些问题,同时还有一些表概念,如广播表、绑定表、单表、动态表等,接着简单介绍一下这些概念。
2.2.1、绑定表
mysql set utf8_mysql set utf8_mysql set utf8
外键约束问题
在《分库分表后遗症-主外键约束》中聊到过,当多张表之间存在物理或逻辑上的主外键关系,如果无法保障同一主键值的外键数据落入同一节点,显然在查询时就会发生跨库查询,这无疑对性能影响是极大的,所以在其中也提到过可以使用绑定表的形式解决该问题。
比如前面案例中的order_id、order_info_id可以配置一组绑定表关系,这样就能够让订单详情数据随着订单数据一同落库,简单的说就是:配置绑定表的关系后,外键的表数据会随着主键的表数据落入同一个库中,这样在做主外键关联查询时,就能有效避免跨库查询的情景出现。
2.2.2、广播表
mysql set utf8_mysql set utf8_mysql set utf8
跨库Join问题
在《分库分表后遗症-跨库Join问题》中同样聊到过,当有些表需要经常被用来做连表查询时,这种频繁关联查询的表,如果每次都走跨库Join,这显然又会造成一个令人头疼的性能问题,所以对于一些经常用来做关联查询的表,就可以将其配置为广播表,广播表在有些地方也被称为同步表、网络表、全局表,但本质上表达的含义都相同,如下:
mysql set utf8_mysql set utf8_mysql set utf8
广播表
广播表是一种会在所有库中都创建的表,以系统字典表为例,将其配置为广播表之后,向其增、删、改一条或多条数据时,所有的写操作都会发给全部库执行,从而确保每个库中的表数据都一致,后续在需要做连表查询时,只需要关联自身库中的字典表即可,从而避免了跨库Join的问题出现。
2.2.3、单表
单表的含义比较简单,并非所有的表都需要做分库分表操作,所以当一张表的数据无需分片到多个数据源中时,就可将其配置为单表,这样所有的读写操作最终都会落入这一张单表中处理。
2.2.4、动态表
动态表的概念在Sharding-Sphere最新的5.x文档中已经移除了,但也可以基于分片算法去实现,所以虽然移除了动态表的概念,但也可以实现相同的效果,动态表的概念是指表会随着数据增长、或随着时间推移,不断的去创建新表,如下:
mysql set utf8_mysql set utf8_mysql set utf8
动态表
大家是否还记得之前《库内分表篇》中的实现呢?当时为了处理单月数据增长过高的问题,咱们手动实现了一套按月动态分表的方案,但在Sharding-Sphere中可以直接支持配置,无需自己去从头搭建,因此实现起来尤为简单,配置好之后会按照时间或数据量动态创建表。
2.3、Sharding-Sphere中的数据分片策略
前面聊到过,分库分表之后读写操作具体会落入哪个库中,这是根据路由键和分片算法来决定的,而Sharding-Sphere中的数据分片策略又分为:内置的自动化分片算法、用户自定义的分片算法两大类,Sharding-Sphere内置的算法涵盖取模分片、哈希分片、范围分片、时间分片等这积累常规算法,而自定义分片算法又可细分为:
综上所述,在Sharding-Sphere内部将这四种分片策略称为:Inline、Standard、Complex、Hint,分别与上述四种策略一一对应,但这四种仅代表四种策略,具体的数据分片算法,可以由使用者自身来定义(后续会结合代码实战讲解)。
2.4、Sharding-Sphere的分库方式
在Sharding-Sphere生态中,支持传统的主从集群分库,如搭建出读写分离架构、双主双写架构,同时也支持按业务进行垂直分库,也支持对单个库进行横向拓展,做到水平分库。
但通常都是用它来实现水平分库和读写分离,因为分布式架构的系统默认都有独享库的概念,也就是分布式系统默认就会做垂直分库,因此无需引入Sharding-Sphere来做垂直分库。
因此接下来会搭建一个简单的SpringBoot+MyBatis项目,结合Sharding-Sphere-JDBC实现水平分库~
三、SpringBoot整合Sharding-JDBC框架
SpringBoot作为一个脚手架框架,用于整合第三方框架十分轻松,比如目前想要引入Sharding-Sphere-JDBC来做分库分表,只需要在pom.xml中加入如下依赖即可:
org.apache.shardingsphere shardingsphere-jdbc-core-spring-boot-starter 5.2.1
目前Sharding-Sphere最新的版本就是2022.08月发布的5.2.1,因此这里先引入最新的依赖作为学习版本,如若是线上业务则可落后最新的一到两个版本,或者选择官方推荐的稳定版本。
3.1、搭建项目的基础结构
接着先在数据库中创建db_sharding_01、db_sharding_02两个库,我这里用伪集群的方式搭建水平库,毕竟线上只需要把数据库地址改为不同的机器IP即可,SQL如下:
-- 先将编码格式改为utf8mb4 set names utf8mb4; set foreign_key_checks = 0; -- 接着创建两个数据库 create databases db_sharding_01; create databases db_sharding_02;
接着分别再在两个水平库中,创建用户表、订单表、订单详情表、商品表(两张),这四张表是接下来用于测试的表,SQL如下:
-- >>>>>>>>>>创建用户表
[1]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_0.png
[2]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_1.png
[3]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_2.png
[4]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_3.png
[5]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_4.png
[6]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_5.png
[7]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_6.png
[8]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_7.png
[9]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_8.png
[10]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_9.png
[11]: https://xiaokai521-1253648611.cos.ap-beijing.myqcloud.com/20240804/09a92aff14e1700cb2c8925b9d32f06c_10.png