TiDB在科捷物流神州金库核心系统的应用与实践
业务背景
北京科捷物流有限公司于2003年在北京正式成立,是ISO质量管理体系认证企业、国家AAAAA级物流企业、海关AEO高级认证企业,注册资金1亿元,是中国领先的大数据科技公司——神州控股的全资子公司。科捷物流融合B2B和B2C的客户需求,基于遍布全国的物流网络与自主知识产权的物流管理系统,为客户提供定制化的一站式供应链服务,在全国拥有231个仓储中心,总面积超100万平方米,年运送货值超5000亿元,日发送包裹超40万个,并在IT、通讯、精密仪器、汽车配件及电商物流领域处于行业领先地位。
神州金库平台经过十几年的更新迭代,支撑了科捷物流自营仓储体系、众多电商平台商家、第三方物流公司的核心业务,积累了庞大的数据量。为应对持续增长的业务规模,以及每年多次的电商大促活动,急需寻找更加高效高性能的数据存储方案。
现状与挑战
神州金库服务端采用微服务架构体系设计,不同的业务模块采用独立的集群部署模式,技术栈基于Java Spring框架构建,数据库目前主要使用 MySQL 主从集群,多台高性能物理机部署,通过 MyCat 做代理层进行读写请求转发。前端接入了多种不同的客户端形态,包括Web、APP、IoT设备、扫描枪、计重器、机器人、报表、第三方API等等。
随着数据量的持续快速增长,MySQL 的存储容量即将达到上限,SQL 响应时间开始变慢,业务受到影响。如果维持现有的技术架构,下一步势必要引入分表机制,同时扩展容量更大的集群,这其中数据迁移就是非常大的工程量,应用端还要引入额外的 sharding 中间件进行改造,后续数据库维护成本和难度成倍上升。
其次,大量的数据报表和分析需求凸显,仅仅依靠 MySQL 从库提供分析查询能力,效率已经达不到业务需求。某些场景下汇总数据的时效性要求非常高,直接影响到下一步的业务决策,引入传统的T+1离线分析方案无法满足。
除此之外,在应对电商大促场景下需要数据库提供足够的并发能力,响应比平时多出几十倍的流量高峰,同时数据库还可以保证稳定的性能。在平时业务量较小的时候,需要缩减配置控制成本,达到弹性易于扩展的目的。
基于以上需求,技术团队决定引入分布式数据库代替 MySQL 单机数据库,在充分考虑了应用和数据双方面迁移难度,以及一系列 POC 验证后,选择了使 TiDB 来替换 MySQL,并用神州金库的核心子系统 WMS 作为首期试点项目。
选择使用 TiDB 的主要因素有:
-
1、语法层面高度兼容 MySQL,应用端代码中没有使用 TiDB 不支持的特性, 最小程度减少应用改造成本,更换数据库连接串即可。
-
2、存储计算分离架构能够满足弹性扩展需求,针对不同时期的业务量动态调整节点达到所需的性能和容量,还可以把不同业务单元的 MySQL 库合并到一个 TiDB 集群中,自带高可用特性省去了 MySQL 从库的硬件成本,数据库维护起来简单高效。
-
3、一站式 HTAP 体验,同时满足交易型和分析性业务场景,且对应用端透明。
-
4、开源产品,技术社区活跃,产品迭代快,碰到问题容易解决。
TiDB 解决方案
测试
为赶在双11之前完成迁移任务,我们做前期做了充足的测试工作,包括应用兼容性测试和改造、多轮带实际业务的压力测试、模拟未来数十倍数据量的性能测试、稳定性测试、高可用测试、生产迁移演练等。在压测中选取了仓储业务中最核心的出库流程,一共包含6个场景,分别是创建出库单、调度、创建波次、单据复核、单据交接、交接确认。
其中稳定性测试过程中除了使用传统的长时间高压业务负载,还引入了 Chaos Mesh 混沌测试,对CPU、内存、网络等发生异常情况进行模拟,观察 TiDB 在测试期间的表现。从监控显示,压测期间资源使用率和数据库响应时间都非常稳定。
迁移
生产环境 TiDB 集群部署架构和数据迁移流程如下图所示:
在 TiDB 集群部署完成后,使用官方提供的数据迁移工具 TiDB Data Migration(DM)开始把全量和增量数据同步到 TiDB 中,然后找一个业务低峰期切断应用端到 MySQL 的流量,待 DM 把数据追平后使用校验工具 Sync-Diff 对上下游数据做一致性检查,校验完成开启 TiDB 到 MySQL 的回退链路,防止切换出现故障可以随时回滚到 MySQL。验证 TiDB Binlog 同步正常以后把应用端数据库连接切换到 TiDB 代理层的VIP,通过 HAProxy 转发请求到 TiDB 计算层。
收益
迁移之后经过一个月的观察和调整,各方面的性能指标都很稳定,P99 延时基本在100ms以下,服务器资源使用率普遍较低,各节点压力均衡。10月31日晚上9点左右,迎来了双11的第一轮业务高峰期,一直持续到11月3日,在这期间 P99 延时没有明显波动,但是集群 QPS 较平时上涨了5-8倍,最高峰值达到1万多。
在11月1日和11月11日两轮业务高峰期,TiDB 均表现得非常稳定,没有发生任何故障和性能问题。本次迁移的 WMS 3.0在双11期间的流量约占整个金库系统的10%,基于目前 TiDB 的优秀表现,我们有充足的信心把所有业务系统逐步迁移到 TiDB。
短期来看,TiDB 可能需要投入较高的硬件成本,但是随着数据规模增长,TiDB 的性价比会大幅提升。首先 TiDB 的数据压缩比非常高,三副本所需要的存储空间远低于三台 MySQL 主从节点,这意味着三台 TiKV 可以存储比 MySQL更多的数据。其次,要提高数据库整体并发能力只需要增加 TiDB Server 节点, 要扩展数据库容量只需要增加 TiKV 节点,从运维成本和硬件成本都要低于 MySQL。
问题
从单机数据库到分布式数据库,除了语法层面的兼容性之外,我们还需要关注相同的 SQL 表现行为是否一致。
例如在早期的测试中发现,当不显式指定排序字段时,MySQL 查询结果能得到固定的顺序,但是在 TiDB 中就会出现结果集顺序不稳定的情况,这主要是分布式特性带来的表现差异。TiDB 会把扫描数据的请求并行下发给多个 TiKV 节点,如果没有强制使用排序字段,受 TiKV 返回数据时间不一致的影响,最终的汇总结果必然没办法保证顺序,这就要求业务开发过程中要保持良好的 SQL 编写规范。
再就是使用 TiDB 普遍会遇到的热点问题,上线初期由于某张表的索引建立不当,导致某个索引读热点问题非常严重,高峰期能达到100多G/min的流量。
我们从三个方向进行了优化,首先找到热点所在的 Region 尝试做切分,会有短暂的效果,但是受 Region 调度影响读热点依旧存在。然后尝试了自动化 Load Base Split,发现效果也不好。最后回归 SQL 本身,仔细分析了业务查询逻辑和索引使用情况,重新调整索引后有了明显效果,但由于这是一个业务上小于当前时间的范围查询,某些 Region 的负载还是会高一些 ,再配合定期扫描 Region 流量超出阈值做切分的脚本,热点问题得到完美解决。
此外还碰到了 TiDB 产品本身的bug,我们生产环境使用了v5.3.2版本,在该版本下当 limit offset 值特别大的时候,如果此时碰上 IndexHashJoin 会导致 Session 处于假死状态,并且持续占用 TiDB 节点内存无法释放,同时也无法kill。早期因为这个问题出现过几次 TiDB 节点 OOM 的情况,只能不定期重启 TiDB Server 解决。经过仔细分析排查后定位到这是产品bug,可以通过 HashJoin 关联方式绕过,最后用 SQL Binding 的形式临时处理掉了。不过业务上这样的 SQL 比较多,目前依然存在这个问题,计划通过版本升级的方式(v5.4.3)彻底解决。
未来展望
整体来说,此次 WMS 3.0系统迁移非常顺利,各方面都能够满足预期,我们也期待未来把更多的业务系统接入到 TiDB 中,在更多场景中感受分布式数据库带来的魅力,助力业务的高速增长。
相关文章:

TiDB在科捷物流神州金库核心系统的应用与实践
业务背景 北京科捷物流有限公司于2003年在北京正式成立,是ISO质量管理体系认证企业、国家AAAAA级物流企业、海关AEO高级认证企业,注册资金1亿元,是中国领先的大数据科技公司——神州控股的全资子公司。科捷物流融合B2B和B2C的客户需求&#…...
React 18 更新 state 中的数组
参考文章 更新 state 中的数组 数组是另外一种可以存储在 state 中的 JavaScript 对象,它虽然是可变的,但是却应该被视为不可变。同对象一样,当想要更新存储于 state 中的数组时,需要创建一个新的数组(或者创建一份已…...

【css】css中使用变量var
CSS 变量可以有全局或局部作用域。 全局变量可以在整个文档中进行访问/使用,而局部变量只能在声明它的选择器内部使用。 如需创建具有全局作用域的变量,请在 :root 选择器中声明它。 :root 选择器匹配文档的根元素。 如需创建具有局部作用域的变量&am…...

判断自己网络所在的NAT类型
文章目录 各NAT类型介绍软件准备流程 各NAT类型介绍 NAT0: OpenInternet,没有经过NAT地址转换,公网IP NAT1: Full Cone NAT,动态家宽可以达到最优的状态,外网设备可以主动发信息给NAT1网络内的设备。 NAT2: Address-Restricted C…...

ClickHouse(十九):Clickhouse SQL DDL操作-1
进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:含各种IT体系技术,IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…...
小程序保留2位小数据,不四舍五入
方法1: parseInt toFixed /* * 保留2位小数,不四舍五入 * 5.992550 >5.99 , 2 > 2.00 * */ const toFixed2Decimal (value) > {return (parseInt(value*100)/100).toFixed(2) } console.log(587.67*100) console.log(toFixed2Decimal(587.67…...
【linux-nginx】nginx限流以及控制访问方法
一、限流 可以使用一些模块和指令来实现限流。以下是一些常用的方法: 使用 ngx_http_limit_req_module 模块:该模块可以限制每个客户端的请求速率。你可以在 Nginx 的配置文件中启用该模块,并使用 limit_req_zone 指令来定义限流规则。例如…...

菜单和内容滚动的联动原理及代码
之前写代码有个需求:左侧是一个菜单,右边是内容,点击左侧菜单右边内容滚动到对应位置,右边内容滚动到某位置时,左侧菜单也会选中对应的菜单项。UI如下:这是大多网站的移动端都会有的需求。 解决方案一&…...

Python爬虫:单线程、多线程、多进程
前言 在使用爬虫爬取数据的时候,当需要爬取的数据量比较大,且急需很快获取到数据的时候,可以考虑将单线程的爬虫写成多线程的爬虫。下面来学习一些它的基础知识和代码编写方法。 一、进程和线程 进程可以理解为是正在运行的程序的实例。进…...

超强的Everything,吊打系统自带文件搜索功能!
目录 一、软件简介 二、软件下载 三、软件说明 一、软件简介 Everything是一款由David OReilly开发的电脑搜索软件,它可以帮助用户快速找到电脑上的文件和文件夹。与其他搜索工具不同的是,Everything使用了一种非常快速和高效的搜索算法,…...
flink配置参数
flink-conf.yaml 基础配置 # jobManager 的IP地址jobmanager.rpc.address: localhost# JobManager 的端口号jobmanager.rpc.port: 6123# JobManager JVM heap 内存大小jobmanager.heap.size: 1024m# TaskManager JVM heap 内存大小taskmanager.heap.size: 1024m# 每个 TaskMan…...
学习Vue:安装Vue.js和设置开发环境
当您决定进入现代前端开发的世界,Vue.js 无疑是一个令人激动的选择。它以其简洁、灵活和高效的特点在开发者社区中备受赞誉。本文将为您详细介绍如何安装 Vue.js 并设置开发环境,让您能够迅速开始编写 Vue 应用程序。 步骤1:安装 Node.js 和 …...
代理技术在网络安全、爬虫和数据隐私中的多重应用
1. Socks5代理:灵活的数据中转 Socks5代理协议在网络通信中起着关键作用。与其他代理技术不同,Socks5代理不仅支持TCP连接,还能够处理UDP流量,使其在需要实时数据传输的场景中表现尤为出色。通过将请求和响应中转到代理服务器&am…...

Python 3 使用Hadoop 3之MapReduce总结
MapReduce 运行原理 MapReduce简介 MapReduce是一种分布式计算模型,由Google提出,主要用于搜索领域,解决海量数据的计算问题。 MapReduce分成两个部分:Map(映射)和Reduce(归纳)。…...

KU Leuven TU Berlin 推出“RobBERT”,一款荷兰索塔 BERT
荷兰语是大约24万人的第一语言,也是近5万人的第二语言,是继英语和德语之后第三大日耳曼语言。来自比利时鲁汶大学和柏林工业大学的一组研究人员最近推出了基于荷兰RoBERTa的语言模型RobBERT。 谷歌的BERT(来自Transformers的B idirectional …...
Postern中配置和使用Socks5代理指南
在Postern中配置和使用Socks5代理,可以为你的爬虫项目提供更灵活、更可靠的网络连接。本文将向你分享如何在Postern中配置和使用Socks5代理的方法,解决可能遇到的问题 配置和使用Socks5代理的步骤: 1.了解Socks代理:了解Socks5代…...
android 窗口级模糊实现方式
在Android上实现窗口级模糊效果有多种方法,下面列出了其中两种常用的实现方式: RenderScript模糊效果: 使用ScriptIntrinsicBlur类在RenderScript中实现模糊效果。创建一个RenderScript实例并将要模糊的图像传递给它。创建一个ScriptIntrinsi…...

面试热题(数组中的第K个最大元素)
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 输入: [3,2,1,5,6,4] 和 k 2 输出: 5提到数组中最大元素,我们往往想到就是先给数组…...
HTTP2协议介绍
前言 HTTP是现代互联网通信的基础协议之一,早在1991年,HTTP/0.9版本就诞生了,之后又陆续发布了HTTP/1.0和HTTP/1.1,为互联网应用提供了更高效和可靠的通信方式。 随着时间的推移,互联网的规模和复杂性不断扩大&#x…...
矩阵的转置
题目: 给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[[1,4,7],[2,5,8],[3,6,9]]class Solution(object):def transpose(self, matrix):"&q…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...

spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...