Java DB loadBalance 設計
Table of Contents
1 JDBC
簡單介紹下JDBC的定義,以下(摘自百度百科):java
JDBC(Java Data Base Connectivity,java數據庫鏈接)是一種用於執行SQL語句的Java API,能夠爲多種關係數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,據此能夠構建更高級的工具和接口,使數據庫開發人員可以編寫數據庫應用程序,同時,JDBC也是個商標名。1 平時咱們在實際開發中通常都是直接使用鏈接池來作DB相關操做的,不多會直接使用JDBC進行編程。可是鏈接池底層鏈接DB的時候也是使用的JDBC,好比c3p0。2mysql
下面經過代碼介紹下經過JDBC操做DB的過程。以下所示:sql
//1. 註冊DriverManager,DriverManager中的靜態List registeredDrivers 保存了全部的Driver引用 Class.forName("com.mysql.jdbc.Driver"); //2.拼接鏈接信息,協議/子協議/數據源標識,鏈接信息 String url = "jdbc:mysql://localhost:3306/csc?user=root&password=xxxx"; //3. DriverManager 負責從註冊的驅動中挑選合適的鏈接 Connection connection = DriverManager.getConnection(url); //4. 創建陳述式語句 (Statement,PrepareStatement(安全性[防止sql注入]和性能),CallableStatement(存儲過程)) //4.1 Statement Statement statement = connection.createStatement(); //execute query ResultSet resultSet = statement.executeQuery("SELECT * FROM test ORDER by ID limit 10"); //4.2 PrepareStatement,在一個提交中設置了多個陳述式語句。 connection.setAutoCommit(false); PreparedStatement preparedStatementInsert = connection.prepareStatement("INSERT into test(name,sex) VALUES (?,?)"); preparedStatementInsert.setString(1, "csophys"); preparedStatementInsert.setInt(2, 0); preparedStatementInsert.execute(); Statement getLastIdStatement = connection.createStatement(); ResultSet set = getLastIdStatement.executeQuery("SELECT LAST_INSERT_ID()"); connection.commit(); //4.3 statement 的batch的功能以及CallableStatement平時用的很少 //5. 處理返回結果ResultSet while (resultSet.next()) { System.out.println(resultSet.getString(2)); System.out.println(resultSet.getString("id")); } while(set.next()){ System.out.println(set.getString(1)); System.out.println(set.getString("LAST_INSERT_ID()")); }
經過上面的代碼,比較清楚的可以看到經過JDBC進行DB操做的幾個步驟。以下圖所示:數據庫
其中 創建陳述式語句 Statement時能夠有三類,,Statement,PrepareStatement,CallableStatement。CallableStatement通常用於存儲過程, 而Statement和PrepareStatement通常用PrepareStatement比較多,PrepareStatement 能夠預編譯SQL預計,而後經過sql參數傳遞執行sql語句, 而Statement執行的時候是完整的執行一個sql,不會預編譯,因此須要屢次執行一個sql的時候。PrepareStatement比Statement的效果要好,並且PrepareStatement還能夠預防SQL注入。3編程
2 DATASOURCE
Datasource 的功能和DriverManager 比較相似,都是向外輸出Connection,只是DataSource通常不直接和DB交互,而是會從鏈接池中獲取DB鏈接
安全
不要混淆DataSource,DriverManager還有鏈接池的概念。個人理解是,DriverManager封裝了各個DB廠商數據庫驅動的差別,能直接和DB操做而且向
外提供數據庫鏈接。而鏈接池保存了多個DB鏈接,減小新建DB鏈接所須要的時間開銷。Datasource是更高層次的封裝,向外提供Connection,底層通常會
使用鏈接池技術。 bash
好比採用Spring和Mybatis進行集成開發的時候,會須要配置一個DataSource。通常從採用比較有名的c3p0等。以下: 架構
<!--c3p0數據源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/csc" /> <property name="user" value="root" /> <property name="password" value="xxxx" /> </bean>
爲了強化記憶,咱們能夠本身來實現一個簡單的DataSource,來給Mybatis使用。以下:ide
<!--MyDatasource--> <bean id="myDataSource" class="base.jdbc.MyDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/csc" /> <property name="user" value="root" /> <property name="password" value="xxxx" /> </bean>
MyDataSource中來接收DB的鏈接信息,而且直接經過DriverManager來獲取DB鏈接。MyDataSource只要集成javax.sql.DataSource,
而且實現裏面的getConnection方法就能夠了。以下所示: 工具
public class MyDataSource implements DataSource { private String driverClass; private String jdbcUrl; private String user; private String password; public Connection getConnection() throws SQLException { try { Class.forName(driverClass); } catch (ClassNotFoundException e) { e.printStackTrace(); } return DriverManager.getConnection(jdbcUrl+"?user="+user+"&password="+password); } ....
而後用本身的DataSource替換c3p0後用單元測試能夠正常工做。
3 讀寫分離及分庫分表設計
首先讀寫分離確定會有多個數據源,因此須要先獲取到主從庫的配置信息好比,w,r1,r2.w爲主庫,r1,r2爲從庫,而且創建了3個數據源wds,rds1,rds2。 爲了方便DBA修改,主從庫的配置信息能夠配置在遠程,而後程序動態獲取。
接下來能夠對於多個數據源作一個封裝,根據SQL類型、事務是否自動提交來決定具體走哪個數據源。封裝的類圖以下所示(取名參考大衆點評的架構組件zebra):
GroupPrepareStatement執行的時候,會根據SQL的類型以及配置類型會選擇GroupConnection中的Datasource引用,而後獲取真實的connection後進行數據庫操做。
而後分庫分表設計的原理也和上面相似,只是主庫通常會有多個,會根據某個維度進行水平切分。執行的時候須要根據sql中的維度的值來肯定具體須要選擇的DataSource。