⽬录
⼀、ShardingSphere和Sharding-JDBC概述
1.1、ShardingSphere简介 1.2、Sharding-JDBC简介1.3、Sharding-JDBC作⽤1.4、ShardingSphere规划线路图1.5、ShardingSphere三种产品的区别⼆、数据库中间件
2.1、数据库中间件简介2.2、Sharding-JDBC和MyCat区别三、Sharding-JDBC+MyBatisPlus实现读写分离
3.0、项⽬代码结构和建表SQL语句3.1、引⼊Maven依赖3.2、yml⽂件配置3.3、UserEntity实体类3.4、UserMapper接⼝3.5、UserService类3.6、UserController类3.7、项⽬启动测试四、HikariCP连接池使⽤遇到的两个Bug
五、读写分离架构,经常出现的读延迟的问题如何解决? 参考链接:
前⾔:博客我⽤AOP+AbstractRoutingDataSource实现了MySQL读写分离,⾃⼰写代码实现判断该使⽤哪个数据源挺⿇烦的。Sharding-JDBC 是 Apache 旗下的 ShardingSphere 中的⼀款轻量级产品,引⼊ jar 即可完成读写分离的需求,可以理解为增强版的 JDBC,现在被使⽤的较多。使⽤Sharding-JDBC配置MySQL读写分离,优点在于数据源完全有Sharding-JDBC托管,写操作⾃动执⾏master库,读操作⾃动执⾏slave库。不需要程序员在程序中关注这个实现,⽐你⾃⼰去配多数据源简单多了。
⼀、ShardingSphere和Sharding-JDBC概述
1.1、ShardingSphere简介
在介绍Sharding-JDBC之前,有必要先介绍下Sharding-JDBC的⼤家族ShardingSphere。在介绍ShardingSphere之后,相信⼤家会对ShardingSphere的整体架构以及Sharding-JDBC扮演的⾓⾊会有更深的了解。
ShardingSphere是后来规划的,最开始是只有 Sharding-JDBC ⼀款产品,基于客户端形式的分库分表。后⾯发展变成了现在的Apache ShardingSphere(Incubator) ,它是⼀套开源的分布式数据库中间件解决⽅案组成的⽣态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独⽴,却⼜能够混合部署配合使⽤的产品组成。它们均提供标准化的数据分⽚、分布式事务和数据库治理功能,可适⽤于如Java同构、异构语⾔、容器、云原⽣等各种多样化的应⽤场景。
ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利⽤关系型数据库的计算和存储能⼒,⽽并⾮实现⼀个全新的关系型数据库。 它与NoSQL和NewSQL是并存⽽⾮互斥的关系。NoSQL和NewSQL作为新技术探索的前沿,放眼未来,拥抱变化,是⾮常值得推荐的。反之,也可以⽤另⼀种思路看待问题,放眼未来,关注不变的东西,进⽽抓住事物本质。 关系型数据库当今依然占有巨⼤市场,是各个公司核⼼业务的基⽯,未来也难于撼动,我们⽬前阶段更加关注在原有基础上的增量,⽽⾮。
1.2、Sharding-JDBC简介
定位为轻量级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。
1.3、Sharding-JDBC作⽤
1.4、ShardingSphere规划线路图
1.5、ShardingSphere三种产品的区别
⼆、数据库中间件
透明化读写分离所带来的影响,让使⽤⽅尽量像使⽤⼀个数据库⼀样使⽤主从数据库,是读写分离数据库中间件的主要功能。
2.1、数据库中间件简介
数据库中间件可以简化对读写分离以及分库分表的操作,并隐藏底层实现细节,可以像操作单库单表那样操作多库多表,主流的设计⽅案主要有两种:
服务端代理:需要独⽴部署⼀个代理服务,该代理服务后⾯管理多个数据库实例,在应⽤中通过⼀个数据源与该代理服务器建⽴连接,由该代理去操作底层数据库,并返回相应结果。优点是⽀持多语⾔,对业务透明,缺点是实现复杂,实现难度⼤,同时代理需要确保⾃⾝⾼可⽤
客户端代理:在连接池或数据库驱动上进⾏⼀层封装,内部与不同的数据库建⽴连接,并对SQL进⾏必要的操作,⽐如读写分离选择⾛主库还是从库,分库分表select后如何聚合结果。优点是实现简单,天然去中⼼化,缺点是⽀持语⾔较少,版本升级困难⼀些常见的数据库中间件如下:
Cobar:阿⾥开源的关系型数据库分布式服务中间件,已停更DRDS:脱胎于Cobar,全称分布式关系型数据库服务MyCat:开源数据库中间件,⽬前更新了MyCat2版本
Atlas:Qihoo 360公司Web平台部基础架构团队开发维护的⼀个基于MySQL协议的数据中间层项⽬,同时还有⼀个NoSQL的版本,叫Pikatddl:阿⾥巴巴⾃主研发的分布式数据库服务
Sharding-JDBC:ShardingShpere的⼀个⼦产品,⼀个轻量级Java框架
2.2、Sharding-JDBC和MyCat区别
1)mycat是⼀个中间件的第三⽅应⽤,sharding-jdbc是⼀个jar包2)使⽤mycat时不需要改代码,⽽使⽤sharding-jdbc时需要修改代码Mycat(proxy中间件层):
Sharding-jdbc(TDDL为代表的应⽤层):
可以看出sharding-jdbc作为⼀个组件集成在应⽤内,⽽mycat则作为⼀个独⽴的应⽤需要单独部署。从架构上看sharding-jdbc更符合分布式架构的设计,直连数据库,没有中间应⽤,理论性能是最⾼的(实际性能需要结合具体的代码实现,理论性能可以理解为上限,通过不断优化代码实现,逐渐接近理论性能)。同时缺点也很明显,由于作为组件存在,需要集成在应⽤内,意味着作为使⽤⽅,必须要集成到代码⾥,使得开发成本相对较⾼;另⼀⽅⾯,由于需要集成在应⽤内,使得需要针对不同语⾔(java、C、PHP……)有不同的实现(事实上sharding-jdbc⽬前只⽀持Java),这样组件本⾝的维护成本也会很⾼。最终将应⽤场景限定在由Java开发的应⽤这⼀种场景下。
Sharding-JDBC较于MyCat,我认为最⼤的优势是:sharding-jdbc是轻量级的第三⽅⼯具,直连数据库,没有中间应⽤,我们只需要在项⽬中引⽤指定的jar包即可,然后根据项⽬的业务需要配置分库分表或者读写分离的规则和⽅式。
三、Sharding-JDBC+MyBatisPlus实现读写分离
3.0、项⽬代码结构和建表SQL语句
(1) 项⽬代码结构
(2) 建表SQL语句
DROP TABLE IF EXISTS `user`;CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT, `account` varchar(45) NOT NULL, `nickname` varchar(18) NOT NULL, `password` varchar(45) NOT NULL,
`headimage_url` varchar(45) DEFAULT NULL, `introduce` varchar(45) DEFAULT NULL, PRIMARY KEY (`user_id`),
UNIQUE KEY `account_UNIQUE` (`account`), UNIQUE KEY `nickname_UNIQUE` (`nickname`)
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8;
3.1、引⼊Maven依赖
3.2、yml⽂件配置 Spring Boot 2.x中,对数据源的选择也紧跟潮流,默认采⽤了⽬前性能最佳的 spring: shardingsphere: datasource: names: master,slave # 数据源名字 master: type: com.zaxxer.hikari.HikariDataSource # 连接池 driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://xxxxxx:3306/test?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=utf-8 #主库地址 username: root password: xxxxxx hikari: maximum-pool-size: 20 #最⼤连接数量 minimum-idle: 10 #最⼩空闲连接数 max-lifetime: 0 #最⼤⽣命周期,0不过期。不等于0且⼩于30秒,会被重置为默认值30分钟.设置应该⽐mysql设置的超时时间短 idle-timeout: 30000 #空闲连接超时时长,默认值600000(10分钟) connection-timeout: 60000 #连接超时时长 data-source-properties: prepStmtCacheSize: 250 prepStmtCacheSqlLimit: 2048 cachePrepStmts: true useServerPrepStmts: true slave: type: com.zaxxer.hikari.HikariDataSource # 连接池 driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://xxxxxx:3306/test?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=utf-8 #从库地址 username: root password: xxxxxx hikari: maximum-pool-size: 20 minimum-idle: 10 max-lifetime: 0 idle-timeout: 30000 connection-timeout: 60000 data-source-properties: prepStmtCacheSize: 250 prepStmtCacheSqlLimit: 2048 cachePrepStmts: true useServerPrepStmts: true masterslave: load-balance-algorithm-type: round_robin # 负载均衡算法,⽤于配置从库负载均衡算法类型,可选值:ROUND_ROBIN(轮询),RANDOM(随机) name: ms # 最终的数据源名称 master-data-source-name: master # 主库数据源名称 slave-data-source-names: slave # 从库数据源名称列表,多个逗号分隔 props: sql: show: true # 在执⾏SQL时,会打印SQL,并显⽰执⾏库的名称,默认false 3.3、UserEntity实体类 package com.hs.sharingjdbc.entity; import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.Data; @Data //定义表名:当数据库名与实体类名不⼀致或不符合驼峰命名时,需要在此注解指定表名@TableName(value = \"user\")public class UserEntity { //指定主键⾃增策略:value与数据库主键列名⼀致,若实体类属性名与表主键列名⼀致可省略value @TableId(type = IdType.AUTO) private Integer user_id; private String account; private String nickname; private String password; private String headimage_url; private String introduce;} 3.4、UserMapper接⼝ 编写的接⼝需要继承 BaseMapper接⼝,该接⼝源码定义了⼀些通⽤的操作数据库⽅法, 单表⼤部分 CRUD 操作都能直接搞定,相⽐原⽣的MyBatis,效率提⾼了很多 package com.hs.sharingjdbc.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.hs.sharingjdbc.entity.UserEntity; public interface UserMapper extends BaseMapper // int insert(T entity);// // int deleteById(Serializable id);// // int deleteByMap(@Param(\"cm\") Map // int delete(@Param(\"ew\") Wrapper // int deleteBatchIds(@Param(\"coll\") Collection extends Serializable> idList);// // int updateById(@Param(\"et\") T entity);// // int update(@Param(\"et\") T entity, @Param(\"ew\") Wrapper // T selectById(Serializable id);// // List // List // T selectOne(@Param(\"ew\") Wrapper // Integer selectCount(@Param(\"ew\") Wrapper // List // List
Copyright © 2019- stra.cn 版权所有 赣ICP备2024042791号-4
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务