背景说明java
公司的整个电商系统搭建在华为云上,根据老总的估计,上线3个月以后日订单量会达到百万级别,保守估计3个月以后总订单个数预计会有5千万。MySQL单表达到千万级别,就会出现明显的性能问题。根据如此规模的数据,当时考虑了2套解决方案:mysql
方案一:在业务上根据用户ID作拆分,将数据打散放在5台32U128G的华为云RDS上边git
方案二:直接使用华为云的分布式数据库中间件DDM程序员
方案一的好处是,分片算法所有在业务上实现,整个方案都在本身的控制下。后续问题定位,方案修改都会好不少;坏处是,整个方案须要业务代码支撑,访问到作了拆分的数据都须要作特殊处理,代价仍是比较大的,并且对开发人员的能力要求很高。后续运维的工做也比较大。github
方案二的好处是,直接使用云服务后续不须要担忧运维的事情,另外DDM从中间件层屏蔽了分库分表的具体实现,业务能够当作单库来操做,易用性以及对代码的要求、对开发人员的要求都会低了不少。缺点就是,使用了DDM以后,对华为云的粘性会大不少。算法
综合考虑了两个方案的优缺点,最终选择了方案二,主要是基于对华为云技术能力和后续蓬勃发展的信心。sql
对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相关一共有十几个参数,几个比较关键的以下表所示:
其余还有几个参数,通常用不到,也就不罗列出来了。你们感兴趣的话能够关注公众号:中间件小哥(zhongjianjianxiaoge)了解更多哟~