写点什么

MySQL-JDBC Loadbalance 深入解析

  • 2019-10-21
  • 本文字数:3074 字

    阅读完需:约 10 分钟

MySQL-JDBC Loadbalance深入解析

公司的整个电商系统搭建在华为云上,根据老总的估计,上线 3 个月之后日订单量会达到百万级别,保守估计 3 个月之后总订单个数预计会有 5 千万。MySQL 单表达到千万级别,就会出现明显的性能问题。


根据如此规模的数据,当时考虑了 2 套解决方案:


方案一


在业务上根据用户 ID 做拆分,将数据打散放在 5 台 32U128G 的华为云 RDS 上边


方案二


直接使用华为云的分布式数据库中间件 DDM


方案一的好处是,分片算法全部在业务上实现,整个方案都在自己的控制下。后续问题定位,方案修改都会好很多;坏处是,整个方案需要业务代码支撑,访问到做了拆分的数据都需要做特殊处理,代价还是比较大的,而且对开发人员的能力要求很高。后续运维的工作也比较大。


方案二的好处是,直接使用云服务后续不需要担心运维的事情,另外 DDM 从中间件层屏蔽了分库分表的具体实现,业务可以当做单库来操作,易用性以及对代码的要求、对开发人员的要求都会低了很多。缺点就是,使用了 DDM 之后,对华为云的粘性会大很多。


综合考虑了两个方案的优缺点,最终选择了方案二,主要是基于对华为云技术能力和后续蓬勃发展的信心。


对 DDM 做了一定的调研,的确是一个非常不错的分库分表服务。支持超大规模数据,10 备于单机数据库的超强性能,百万并发,读写分离,支持平滑扩容等等。。。优点数不胜数~


搭建到华为云之后,一直平稳运行,但是前阵子出了个奇怪的问题,在 DDM 技术专家的协助下,很快定位了出来,结果是 MySQL-JDBC 的一个 bug 导致。作为一个具有打破砂锅问到底、不破楼兰誓不还的程序员,决定对 MySQL 的相关参数做个详细的分析,免得从一个坑里边爬出来又进了另外一个。

Loadbalance 模式说明

为了提供高性能,百万并发,DDM 自身是以无状态的集群形式对外提供的。内部怎么做的我们不清楚,能看到的是,每个 DDM 提供了多个访问地址,每个库的访问 url 类似于:jdbc:mysql:loadbalance://192.168.0.35:5066,192.168.0.192:5066,192.168.0.175:5066,192.168.0.139:5066/orderdb?loadBalanceAutoCommitStatementThreshold=5


从访问的 url 看,内部应该是多台 DDM 节点的,实际上从我们测试的情况看,访问任何一台的效果都是一样的。猜测,内部的交互应该是类似如下图的:



跟 DDM 的技术专家求证,的确是如此的,心里有点小得意~~


我们的代码全部是 java 的代码,连接池用的是 druid,根据 DDM 的指导,将 url 配置好就能正常访问了。感觉关健的就在 loadbalance 这个,应该是告诉了驱动,通过负载均衡方式访问 DDM。在网上查了下,这种方式是直接在驱动层面做的负载均衡,相比通过负载均衡器的方式,少了一次网络转发,怪不得效率会这么高。不过,APP 到底是访问哪个 DDM,内部机制是什么样子的?这些在网上查了下,都是语焉不详,没办法只好从 MySQL JDBC 的源码入手了。


驱动的源码是托管在 github 上,我们当前用的是 DDM 推荐的 5.1.44 版本的:https://github.com/mysql/mysql-connector-j/tree/5.1.44


核心的就是几个 Loadbalance 开头的类:



代码比较多,其他的就不多说了,最关键的就是下边这块代码:


LoadBalancedConnectionProxy.java 类的 pickNewConnection()函数:


这个函数在创建连接对象、一个事务提交或者回滚都会调用,作用就是轮换下一个 DDM 节点。这块代码的逻辑就是,根据一定的负载均衡策略挑选一个节点的连接,做个基本的连接有效性探测,然后将当前连接的状态同步到新连接(见 Table 2 MultiHostConnectionProxy.syncSessionState())。同步完毕,就把当前使用的连接设置为新挑选的连接。如果所有的连接都不可用,就把当前状态设置为了 Closed 状态。看着快代码,感觉 MySQL 的有些代码也不严谨,比如如果在获取新连接的时候,如果抛了 SQLException 出来,这个异常就直接被吃掉了,不会抛出去,也不会有任何信息记录下来,这个对后续的问题定位还是很不方便的,不知道是出于什么考虑的。


Table 1 LoadBalancedConnectionProxy.pickNewConnection()   synchronized void pickNewConnection() throws SQLException {       if (this.isClosed && this.closedExplicitly) {           return;       }       if (this.currentConnection == null) { // startup           this.currentConnection = this.balancer.pickConnection(this, Collections.unmodifiableList(this.hostList),                   Collections.unmodifiableMap(this.liveConnections), this.responseTimes.clone(), this.retriesAllDown);           return;       }       if (this.currentConnection.isClosed()) {           invalidateCurrentConnection();       }       int pingTimeout = this.currentConnection.getLoadBalancePingTimeout();       boolean pingBeforeReturn = this.currentConnection.getLoadBalanceValidateConnectionOnSwapServer();       for (int hostsTried = 0, hostsToTry = this.hostList.size(); hostsTried < hostsToTry; hostsTried++) {           ConnectionImpl newConn = null;           try {               newConn = this.balancer.pickConnection(this, Collections.unmodifiableList(this.hostList), Collections.unmodifiableMap(this.liveConnections), this.responseTimes.clone(), this.retriesAllDown);               if (this.currentConnection != null) {                   if (pingBeforeReturn) {                       if (pingTimeout == 0) {                           newConn.ping();                       } else {                           newConn.pingInternal(true, pingTimeout);                       }                   }                   syncSessionState(this.currentConnection, newConn);
} this.currentConnection = newConn; return; } catch (SQLException e) { if (shouldExceptionTriggerConnectionSwitch(e) && newConn != null) { // connection error, close up shop on current connection invalidateConnection(newConn); } } } // no hosts available to swap connection to, close up. this.isClosed = true; this.closedReason = "Connection closed after inability to pick valid new connection during load-balance."; }


Table 2 MultiHostConnectionProxy.syncSessionState() static void syncSessionState(Connection source, Connection target, boolean readOnly) throws SQLException { if (target != null) { target.setReadOnly(readOnly); } if (source == null || target == null) { return; }target.setAutoCommit(source.getAutoCommit()); target.setCatalog(source.getCatalog()); target.setTransactionIsolation(source.getTransactionIsolation()); target.setSessionMaxRows(source.getSessionMaxRows()); }
复制代码


MySQL-JDBC Loadbalance 参数说明


明白了 MySQL-JDBC 的 Loadbalance 的相关机制,最重要的还是要对相关的参数有个详细的了解,并且设置有效的值,Loadbalance 相关一共有十几个参数,几个比较关键的如下表所示:




其他还有几个参数,一般用不到,也就不罗列出来了。


本文转载自公众号中间件小哥(ID:huawei_kevin)。


原文链接:


https://mp.weixin.qq.com/s/baxyONDEHTlPrSwszjo3Jw


2019-10-21 14:051471

评论

发布
暂无评论
发现更多内容

Go sync.Once:简约而不简单的并发利器

陈明勇

Go golang 高并发 三周年连更 sync.Once

UDP报头是通过结构体位段实现的吗

linux大本营

网络协议 udp UDP协议

dpdk中,如何建立portid/queue的配置和逻辑核心的关系

linux大本营

队列 DPDK DPDK开发

重载++运算符分别实现i++和++i

linux大本营

运算符 数据结构与算法

概述产品设计通用七原则

阿泽🧸

产品设计 三周年连更

系统稳定性建设之我见(64/100)

hackstoic

质量管理 系统稳定性

解析下rte_pktmbuf_pool_create参数含义

linux大本营

DPDK DPDK开发

dpdk l2fwd如何初始化每个逻辑核的port/queue的

linux大本营

队列 DPDK DPDK开发

6G 通信技术和 5G 通信技术的区别

汪子熙

通讯协议 通讯 三周年连更

如何建设IT运维流程与体系

穿过生命散发芬芳

运维体系 三周年连更

一文带你了解实战常用JavaScript API

程序员海军

JavaScript 三周年连更

minikube 初体验环境搭建

IT蜗壳-Tango

三周年连更

京韵、京城、京味:从一台服务器看数字北京

脑极体

算力

打工人逃不开「单人单岗」

Java 架构 程序人生 职场

写一个完整的SHOW TABLE STATUS 语句返回的所有表的状态信息对应的结构体

linux大本营

数据库 存储 结构体 C++

linux dbus代码举例

linux大本营

Linux C++

linux dbus客户端和服务器示例代码

linux大本营

c++ Linux dbus

2022-04-26:给定一个数组componets,长度为A, componets[i] = j,代表i类型的任务需要耗时j 给定一个二维数组orders,长度为M, orders[i][0]代表i

福大大架构师每日一题

golang 算法

第五期(2022-2023)传统行业云原生技术落地调研报告——金融篇

York

容器 DevOps 微服务 云原生 金融

扎最深的寨,打最持久的仗——一知智能AI商业化攻略访谈录

B Impact

基于Flutter实现Windows平台离线大模型对话应用实战

轻口味

flutter AI windows 跨平台 三周年连更

Golang new 和 make 函数

宇宙之一粟

Go make new 三周年连更

来字节跳动实习,有机会发Nature子刊

字节跳动技术范儿

openbmc 中如何使用D-bus

linux大本营

dbus openBMC

一键生成通用的微服务(gRPC)项目代码,让你的开发效率翻倍提升

vison

Go 微服务 gRPC 代码自动生成

数据存储与访问——文件存储读写

梦笔生花

application 三周年连更 SharedPreference

什么是Java 异常?如何处理异常?

Java架构历程

Java 三周年连更

一个有趣的图片加载效果

南城FE

CSS 前端 动画 图片

Django笔记十七之group by 分组用法总结

Hunter熊

Python django count 分组查询 sum

nvim 配置c++环境

linux大本营

vim C++

共话数字化新技术、新趋势 华为云开发者日东莞站成功举办

Geek_2d6073

MySQL-JDBC Loadbalance深入解析_文化 & 方法_苗永辉_InfoQ精选文章