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。