sharding-jdbc分库连接数优化 | 京东物流技术团队

news2024/12/28 19:15:15

一.背景:

配运平台组的快递订单履约中心(cp-eofc)及物流平台履约中心(jdl-uep-ofc)系统都使用了ShardingSphere生态的sharding-jdbc作为分库分表中间件, 整个集群采用只分库不分表的设计,共16个MYSQL实例,每个实例有32个库,集群共512个库.

当每增加一台客户端主机,一个MYSQl实例最少要增加32个连接(通常都会使用连接池,根据配置的最大连接数,这个连接数可能会放大5~10倍).并且通常一个系统都会分为web,provider,worker等多个应用,这些应用共用一套数据源.随着应用机器数的增加,MYSQL实例的连接数会很快达到上限,这就对系统的扩容造成了阻碍,无法横向的增加机器数,只能纵向的提高机器的配置来应对流量的增长.

作为京东物流的核心系统,业务增长迅速,系统所承接的流量也是逐渐增加,所以急需解决这个制约系统扩展的瓶颈点.

二.分库分表的相关概念介绍

2.1 为什么要分库分表

2.1.1 分库

随着业务的发展,单库中的数据量不断增加,数据库的QPS会越来越高,对数据库的读写耗时也会相应的增长,这时单库的读写性能必然会成为系统的瓶颈点.这时可以通过将单个数据库拆分为多个数据库的方法,来分担数据库的压力,提升性能.同时多个数据库分布在不同的机器上也提高了数据库的可用性.

2.1.2 分表

随着单表数据量的增加,对于数据的查询和更新,即使在数据库底层有一定的优化,但是随着量变必定会引起质变,导致性能急剧下降.这时可以通过分表的方法,将单表数据按一定规则水平拆分到多个表中,减小单表的数据量,提升系统性能.

2.2 sharding-jdbc简介

ShardingSphere

是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成.他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。

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。

我们先看下ShardingSphere官网给出的基于Spring命名空间的规则配置示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:sharding="http://shardingsphere.io/schema/shardingsphere/sharding" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://shardingsphere.io/schema/shardingsphere/sharding 
                        http://shardingsphere.io/schema/shardingsphere/sharding/sharding.xsd 
                        ">
    <!-数据源ds0->
    <bean id="ds0" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/ds0" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>
    <!-数据源ds1->
    <bean id="ds1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/ds1" />
        <property name="username" value="root" />
        <property name="password" value="" />
    </bean>
    
    <!-分片策略->
    <sharding:inline-strategy id="databaseStrategy" sharding-column="user_id" algorithm-expression="ds$->{user_id % 2}" />
    <sharding:inline-strategy id="orderTableStrategy" sharding-column="order_id" algorithm-expression="t_order$->{order_id % 2}" />
    <sharding:inline-strategy id="orderItemTableStrategy" sharding-column="order_id" algorithm-expression="t_order_item$->{order_id % 2}" />
    
    <!-sharding数据源配置->
    <sharding:data-source id="shardingDataSource">
        <sharding:sharding-rule data-source-names="ds0,ds1">
            <sharding:table-rules>
                <sharding:table-rule logic-table="t_order" actual-data-nodes="ds$->{0..1}.t_order$->{0..1}" database-strategy-ref="databaseStrategy" table-strategy-ref="orderTableStrategy" />
                <sharding:table-rule logic-table="t_order_item" actual-data-nodes="ds$->{0..1}.t_order_item$->{0..1}" database-strategy-ref="databaseStrategy" table-strategy-ref="orderItemTableStrategy" />
            </sharding:table-rules>
        </sharding:sharding-rule>
    </sharding:data-source>
</beans>

配置总结:

1.需要配置多个数据源ds0,ds1;

2.分片策略中配置分片键(sharding-column)和分片表达式(algorithm-expression)需符合groovy语法;

3.在sharding数据源中sharding:table-rule标签中配置逻辑表名(logic-table),库分片策略(database-strategy-ref)和表分片策略(table-strategy-ref),actual-data-node属性由数据源名 + 表名组成,以小数点分隔,用于广播表;

三.问题分析与解决方案

3.1 问题分析

正如文章开头提到的目前我们的MYSQL集群架构如下,16个MYSQL实例,每个实例有32个库,集群共512个库.当客户端主机启动后与MYSQL_0实例中的32个库连接,分别会建立32个数据源,连接池配置的最大连接数为5,也就是说极端情况下一个客户端与一个MYSQL实例最多会建立32*5=160个连接数.对于物流的一些核心系统在大促时扩容上百台是很常见的,所以很快单个实例的最大连接数就会触达上限.

目前客户端连接连接数据库集群形式如图所示:

3.2 可行方案

我们的目标就是降低单个MYSQL实例的连接数,其中我们共探讨了几种方案如下:

3.2.1 单实例不分库只分表

这样一个客户端与单个数据库实例只需通过一个连接池连接,大大降低了连接数.但这种方案改变了现有的分片规则,需要新建一套数据库集群,根据新规则同步历史数据和增量数据,还有新旧数据验证,但难度和风险最高的还是线上切换过程,可能会造成数据不一致,且一旦出问题回滚方案也会非常复杂.

3.2.2 使用支持弹性扩展的数据库

使用京东的jed,tidb等支持弹性扩展的数据库,将数据同步到新库中,这类数据库的优势是开发人员只需关注业务,不需要再去处理数据库连接这些底层细节.

3.2.3 使用sharding-proxy

Sharding-Proxy的定位是透明化的数据库代理,我们可以在服务器上部署一套Sharding-Proxy,客户端只需连接proxy服务,再由proxy服务器连接MYSQL集群,这样MYSQL集群的连接数只与proxy服务器的数量有关,与客户端解耦.

3.2.4 通过改造sharding-jdbc

理论上我们只要获取数据库实例上某个库的连接,我们就可以通过"库名.表名"的方式访问这台实例上其他库中的数据(当然前提是用户要拥有要访问库的权限),我们是否可以通过改造sharding-jdbc来实现这种访问方式?

以上几种方案,3.2.1和3.2.2都需要新建数据库,同步历史和增量数据,还涉及线上切换数据源,3.2.3需要部署一套proxy服务,并且为了高可用必定要以集群方式部署,这三种方案工作量和风险都较高,我们基于成本最小原则,最终选择改造sharding-jdbc的方案.

3.3 探究sharding-jdbc

3.3.1 工作流程

sharding-jdbc的工作流程可以分为以下步骤:

  • sql解析-词法解析和语法解析;

  • sql路由-根据解析上下文匹配数据库和表的分片策略,并生成路由路径;

  • sql改写-将逻辑SQL改写为在真实数据库中可以正确执行的SQL;

  • sql执行-使用多线程并发执行sql;

  • 结果归并-将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端;

显然数据库和表的分片是在sql路由阶段处理,所以我们以sql路由逻辑为入口分析下源码.

3.3.2 源码分析

ShardingStandardRoutingEngine类中的route方法为计算路由的入口,返回的结果是数据库和表的分片集合:

route方法中的核心逻辑在该类的route0方法中,其中routeDataSources方法负责database路由,routeTables方法负责table路由,实际路由计算在StandardShardingStrategy的doSharding方法中,我们继续深入.

在StandardShardingStrategy类中有两个成员属性,preciseShardingAlgorithm(精准分片算法),rangeShardingAlgorithm(范围分片算法),由于我们的sql都只指定分片键精准查询,使用的都是preciseShardingAlgorithm计算出的结果,PreciseShardingAlgorithm是个接口,那我们就可以实现这个接口来自定义分片算法.

同时在sharding-sphere官网上也找到了相应的标签支持:

所以我们只需要自己实现PreciseShardingAlgorithm接口并配置在标签内即可实现自定义分片策略.

3.4 改造步骤

3.4.1 库分片改造

目前应用配置了ds_0ds_511共512个数据源,我们只需配置ds_0ds_15共16个数据源,每个数据源配置的是单个实例上的第一个库.

对于分片规则,我们可以依然使用sharding:inline-strategy标签,只需对Groovy表达式进行重写,分片键为order_code,之前分片算法为(Math.abs(order_code.hashCode()) % 512)即用order_code列的哈希值对512取模得到0511,我们只需要将结果再整除32即可得到016,即表达式改写为(Math.abs(order_code.hashCode()) % 512).intdiv(32).

改造前分库规则配置:

改造后分库规则配置:

3.4.2 表分片改造

实现PreciseShardingAlgorithm接口,重写表分片算法,使计算结果返回"实际库名+表名"的形式;

例如:查询DB_31库上t_order表的user_id=35711的数据,数据库分片算法返回的数据源为"DB_0",表分片算法返回"DB_31.t_order";

自定义表分片算法:

在xml中定义sharding:standard-strategy标签,其属性precise-algorithm-ref配置为我们自定义的分表算法.

3.4.3 数据库连接池参数调整

改造前是一个库对应一个数据源连接池,改造后一个实例上的32个库共用一个数据源连接池,那么连接池的最大连接数,最小空闲连接数等参数需要相应的做调整.这个需要根据业务流量做合理的评估,当然最严谨的还是要以压测结果作为依据.

改造后客户端连接集群的形式如图:

优化前后数据库集群连接数对比:

四.小插曲

在改写库分片规则的Groovy表达式时,整除32直接在原有表达式上配置"/32"即Math.abs(order_code.hashCode()) % 512 / 32 ,在调试中发现执行sql会报"no database route info"错误信息,经过debug发现sharding-jdbc计算分片规则时会出现小数(例如:ds_14.6857),导致找不到数据源,这是因为Groovy没有提供专用的整数除法运算符,所以要用.intdiv()方法,最终表达式改写为(Math.abs(order_code.hashCode()) % 512).intdiv(32).

五.总结

本文介绍了分库分表的概念及优势,以及sharding-jdbc分库分表中间件,探究了sharding-jdbc的路由规则的执行流程.当然在系统设计之初,对于数据库的分库分表,到底需不需要做?是多分库好还是多分表好?并没有一个放之四海而皆准的法则,需结合系统的特点(例如qps,tps,单表数据量,磁盘规格,数据保留时间,业务增量,数据冷热方案等因素)来决策权衡,有利有弊才需决策,有取有舍才需权衡.

作者:京东物流 张仲良

来源:京东云开发者社区 自猿其说Tech

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/747193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AE/PR/OFX插件-Mocha Pro 2023 v10.0.2 Win 专业平面/三维跟踪摄像机反求插件

插件简介 Mocha Pro是用于VFX视觉特效和后期制作强大的平面跟踪工具&#xff0c;这里提供的是AE和PR的插件版。具有GPU加速的跟踪和对象去除功能&#xff0c;具有边缘捕捉功能的高级遮罩&#xff0c;稳定功能&#xff0c;镜头校准&#xff0c;3D摄像头求解器&#xff0c;立体3…

Win32 汇编在对话框上画线

参阅前文&#xff0c;首先要有一个基本的对话框&#xff1b; 把对话框资源文件里的控件定义都删除&#xff0c;得到的一个rc文件&#xff0c;test.rc&#xff1b; #include <resource.h>#define DLG_MAIN 1DLG_MAIN DIALOG 193, 180, 130, 150 STYLE DS_MODALFRAME | …

在win32 asm中了解intel浮点运算

看老罗的书中有如下一段&#xff0c; ...... fild dwRadius fild _dwDegree fldpi fmul ;角度*Pi fild dwPara180 fdivp st(1),st ;角度*Pi/180 fsin ;Sin(角度*Pi/180) fild …

038、TiDB特性_聚簇索引和非聚簇索引

聚簇表 表中的行数据才能出顺序与主键存储的顺序一致表的主键即为KV映射中Key的一部分通过主键访问行记录时&#xff0c;可以直接获取行记录 create table t( a biging primary key clustered ,b varchar(255)); # a列为主键列&#xff0c;聚簇列聚簇表&#xff08;且ID为主键…

TortoiseGit 入门指南05:推送和拉取

本节所讲内容均涉及到 远端版本库。 版本库 的概念在《TortoiseGit 入门指南02&#xff1a;创建和克隆仓库》中提到过&#xff0c;它是工作目录下面的一个名为 .git 的隐藏目录&#xff0c;我们每一次提交、每一个分支都会保存在版本库中。这个版本库就在我们电脑上的某个文件…

windows开机启动nginx(服务方式启动)

提示&#xff1a;本文章介绍如何借助Windows Service Wrapper小工具&#xff0c;将Nginx转换为Windows服务&#xff0c;在服务中心配置自启动&#xff0c;从而在开机时windows自行启动Nginx服务 Nginx是什么 官方链接&#xff1a;nginx下载 Nginx 是一个高性能的HTTP和反向代理…

Kotlin基础(五):类和接口

前言 本文主要讲解类和接口&#xff0c;主要包括类的声明、构造器、类成员、修饰符、类的继承、接口、抽象类。 Kotlin文章列表 Kotlin文章列表: 点击此处跳转查看 目录 1.1 类的声明 在 Kotlin 中&#xff0c;类的声明使用关键字 class。下面是一个简单的类声明的示例&…

感受C++模版的所带来的魅力

一、泛型编程思想 首先我们来看一下下面这三个函数&#xff0c;如果学习过了 C函数重载 和 C引用 的话&#xff0c;就可以知道下面这三个函数是可以共存的&#xff0c;而且传值会很方便 void Swap(int& left, int& right) {int temp left;left right;right temp; }…

三、Exsi安装虚拟机win10系统

Exsi安装虚拟机win10系统 1、新建虚拟机2、选择存储(直接下一步)3、自定义设置3.1 设置cpu3.2 设置内存3.3 设置硬盘3.4 设置网卡3.5 设置驱动3.6 设置BIOS 4、完成安装 1、新建虚拟机 2、选择存储(直接下一步) 3、自定义设置 3.1 设置cpu 3.2 设置内存 3.3 设置硬盘 3.4 设置…

python搭建文件服务

python -m http.server 访问8000端口&#xff0c;可分享或下载命令启动文件夹下文件。

美国访问学者的申请条件

作为一个富有学术活力和创新精神的国家&#xff0c;美国吸引着世界各地的学者前往交流和研究。对于希望成为美国访问学者的人来说&#xff0c;了解申请条件是至关重要的。下面知识人网小编将介绍一些常见的美国访问学者申请条件。 1. 学术背景与研究计划&#xff1a;作为申请者…

安全测试方法介绍(上)静态源代码审查

软件开发完成之后&#xff0c;我们需要进行安全验证&#xff0c;验证我们的软件是不是符合安全要求。软件安全测试主要有以下几个方面&#xff1a;确定软件的安全特性实现是否与预期设计一致的过程&#xff1b;有关验证软件安全等级和识别潜在安全缺陷的过程&#xff1b;查找软…

采集发布到WordPress网址(OneNav主题-WordPress主题)

WordPress系统的一导航主题&#xff08;OneNav主题&#xff09;是集网址、资源、资讯于一体的导航主题。 要将采集的数据批量自动发布到一导航主题&#xff08;OneNav主题&#xff09;的网址要怎么设置&#xff1f; 普通的文章采集器一般只能发布为wordpress文章类型&#xff…

echarts折线图横向渐变填充

这种情况&#xff0c;需要后端去计算&#xff0c;如何把不同分段的值赋予不同的颜色&#xff0c;然后组合成下面我们需要的格式就可以实现 drawLine1() {if (document.getElementById(s1) ! null) {var that thislet heightNum (this.porosityList.maxDepth-this.porosityLis…

网络安全—综合渗透实验

一、 实验名称 综合实验 二、 实验目的 【实验描述】 随着互联网的普及和快速发展&#xff0c;互联网产品多样化、迭代快的特点为一些企业赢得了机会&#xff0c;同样也给企业带来了众多安全问题。如网络安全、系统安全、web安全、数据安全等。 本实验模拟企业复杂网络、复…

开源代码分享(6)—考虑电动汽车可调度潜力的充电站两阶段市场投标策略(附matlab代码)

[1]詹祥澎,杨军,韩思宁等.考虑电动汽车可调度潜力的充电站两阶段市场投标策略[J].电力系统自动化,2021,45(10):86-96. 摘要&#xff1a;在电力市场环境下,充电站优化投标策略能降低电力成本&#xff0c;甚至通过售电获取收益。文中考 虑了电动汽车成为柔性储荷资源的潜力&#…

使用mongodump和mongorestore备份与恢复Mongodb数据

一、备份与恢复方案 mongodump是MongoDB官方提供的备份工具,它可以从MongoDB数据库读取数据,并生成BSON文件,mongodump适合用于备份和恢复数据量较小的MongoDB数据库, 不适用于大数据量备份。 默认情况下mongodump不获取local数据库里面的内容。mongodump仅备份数据库中的文档…

微信小程序原生button组件使用并修改样式

想使用原生button组件&#xff0c;但是想使用不同样式 <button type"default">登录</button> 注意type使用得type必须和修改时选择器样式&#xff08;button[typedefault]&#xff09;一样&#xff0c;才能对其修改样式&#xff0c;否则样式不会被修改…

ArcGIS提取路网节点

这是我学校周围的路网,数据就不分享给大家了,看教程知道有这个方法就好了 先打开编辑器,把他合并成只有一个字段的要素 然后打开高级编辑,打断相交线 保存退出,然后打开ArcCatalog 10.6, 建立网络数据集 这时候就成了带有节点的网络数据集,但这还不是我们需要的点文件,因为这…

如何创建智能合约游戏系统

区块技术的发展&#xff0c;智能合约成为了一个热门话题。智能合约是一种基于区块技术的自动化合约&#xff0c;它可以自动执行合同中规定的条款&#xff0c;从而实现去中心化的信任和价值传递。在游戏领域&#xff0c;智能合约可以让玩家在游戏中实现各种交易和交互&#xff0…