背景說明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)瞭解更多喲~