发现梦想与现实的差距后,坚持就越来越远
前段时间在刷博客的时候,无意间刷到了一篇介绍数据库分库分表的框架,于是网上搜了一下,shardingjdbc 当当团队网团队开源的一个专做对于java开发在原生的jdbc层上封装的一层轻量级中间件。我们看一下它的官网(http://shardingjdbc.io/index_zh.html)给的介绍:
对于shardingjdbc来说无非就是通过java代码在原生的jdbc上封装了一层,如果说自己去研发一个这样的中间件也不是不可能。就是后期考虑到出现的bug、效率、性能等问题,没有一个更好的前期规划也很难完成。定位上面这个轻量级的框架通过上面给的架构图来说,无非就是在jdbc层进行更好的封装集成,从而形成一个加强版的JDBC连接数据库的工具。没有任何侵入性,完全可以兼容任何的ORM框架,这一点确实是可以值得一用的。
下面我们来看看sharding-jdbc为我们提供的功能有哪些?官网也有具体的介绍:
- 数据分片
数据分片主要以分库+分表的操作、支持的sql语句查询方式以及分布式主键的处理方案等- 读写分离
读写分离主要以一主多从、统一线程数据的一致性以及分库分表时的读写分离操作等- 事物处理
主要针对TCC分布式事物处理解决- 分布式治理
主要一些熔断措施,以及配置中心的配置。
基本的说明就这些,后续将会沿着例子区找到源码进行进一步的研究和探讨。接下来可以去github上将shardingjdbc-example(https://github.com/sharding-sphere/sharding-sphere-example)例子下载下来运行,如下图,由于版本原因选择branches版本下载:
本机运行例子需要选择master版本的,dev是开发版本,暂时maven代码库还没有更新上去。
可以直接下载zip也可以直接使用:
git clone https://github.com/sharding-sphere/sharding-sphere-example.git
直接下载下来。然后将下载下来的项目导入到IDEA中如下图:
注意:当前我使用的sharding-jdbc版本是2.0.3。git可能会是最新版本。应该都可以用的运行的,只是目录有点不一样了。
我们来看看官网给的例子,首先在每个项目模块下面对java实现的每个ORM框架进行了组个demo演示,无论我们使用哪种框架,只需要导入所需的jar包便可以完美的和ORM框架结合。
首先我们先来看看在与原生的jdbc结合的时候sharding-jdbc是怎么实现分库分表的策略的。找到
sharding-jdbc-raw-jdbc-example
该模块下的
sharding-jdbc-raw-jdbc-java-example // 原生代码与sharding-jdbc结合
/**仅读写分离操作*/
RawJdbcJavaMasterSlaveOnlyMain
/**读写分离+分库分表操作*/
RawJdbcJavaShardingAndMasterSlaveMain
/**分库分表操作*/
RawJdbcJavaShardingDatabaseAndTableMain
/**仅分库操作*/
RawJdbcJavaShardingDatabaseOnlyMain
/**仅做分表操作*/
RawJdbcJavaShardingTableOnlyMain
注意:在运行demo前请先将基本的数据库先初始化好,在每个模块的根目录下会有一个sql文件,是根据自己的业务来制定的建库规则来创建对应的库:
在创建好数据库后将会有12个对应的数据库,待后续example会使用到:
下面将从分库模块学习使用入口:
/**仅分库操作*/
RawJdbcJavaShardingDatabaseOnlyMain
使用到的库:
- demo_ds_0
- demo_ds_1
运行该example结果如下:
1.动态创建表成功--------------
2.插入数据成功--------------
3.打印 Equals 查询结果--------------
order_item_id:1, order_id:207575701002387456, user_id:10
order_item_id:2, order_id:207575701472149504, user_id:10
order_item_id:3, order_id:207575701568618496, user_id:10
order_item_id:4, order_id:207575701669281792, user_id:10
order_item_id:5, order_id:207575701803499520, user_id:10
order_item_id:6, order_id:207575701950300160, user_id:10
order_item_id:7, order_id:207575702038380544, user_id:10
order_item_id:8, order_id:207575702118072320, user_id:10
order_item_id:9, order_id:207575702256484352, user_id:10
4.打印使用 In 查询结果--------------
order_item_id:1, order_id:207575701405040640, user_id:11
order_item_id:2, order_id:207575701518286848, user_id:11
order_item_id:3, order_id:207575701623144448, user_id:11
order_item_id:4, order_id:207575701723807744, user_id:11
order_item_id:5, order_id:207575701899968512, user_id:11
order_item_id:6, order_id:207575701988048896, user_id:11
order_item_id:7, order_id:207575702080323584, user_id:11
order_item_id:8, order_id:207575702218735616, user_id:11
order_item_id:9, order_id:207575702290038784, user_id:11
order_item_id:1, order_id:207575701002387456, user_id:10
order_item_id:2, order_id:207575701472149504, user_id:10
order_item_id:3, order_id:207575701568618496, user_id:10
order_item_id:4, order_id:207575701669281792, user_id:10
order_item_id:5, order_id:207575701803499520, user_id:10
order_item_id:6, order_id:207575701950300160, user_id:10
order_item_id:7, order_id:207575702038380544, user_id:10
order_item_id:8, order_id:207575702118072320, user_id:10
order_item_id:9, order_id:207575702256484352, user_id:10
4.打印使用 Hint 查询结果--------------
order_item_id:1, order_id:207575701405040640, user_id:11
order_item_id:2, order_id:207575701518286848, user_id:11
order_item_id:3, order_id:207575701623144448, user_id:11
order_item_id:4, order_id:207575701723807744, user_id:11
order_item_id:5, order_id:207575701899968512, user_id:11
order_item_id:6, order_id:207575701988048896, user_id:11
order_item_id:7, order_id:207575702080323584, user_id:11
order_item_id:8, order_id:207575702218735616, user_id:11
order_item_id:9, order_id:207575702290038784, user_id:11
数据库中的表如图所示:
基本执行步骤如下:
/**
* @author: ErnestFei
* @date: 2018/5/27 19:15
* @Description: 配置数据源
*/
private static DataSource getShardingDataSource() throws SQLException {
/**初始化sharding配置*/
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
/**添加创建主订单表*/
shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
/**添加创建订单项表*/
shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
/**根据主订单表中的user_id字段定位该笔订单入库规则,这里使用user_id的奇偶数来定位插库入口*/
shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "demo_ds_${user_id % 2}"));
/**创建好数据源并加入配置中得到数据源配置*/
return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new HashMap(), new Properties());
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:20
* @Description: 主订单表规则的基本配置
*/
private static TableRuleConfiguration getOrderTableRuleConfiguration() {
TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
orderTableRuleConfig.setLogicTable("t_order");
/**设置主键*/
orderTableRuleConfig.setKeyGeneratorColumnName("order_id");
return orderTableRuleConfig;
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:22
* @Description: 订单项目表的配置规则
*/
private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
TableRuleConfiguration orderItemTableRuleConfig = new TableRuleConfiguration();
orderItemTableRuleConfig.setLogicTable("t_order_item");
return orderItemTableRuleConfig;
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:22
* @Description: 创建两个动态数据源 demo_ds_0 、 demo_ds_1
*/
private static Map createDataSourceMap() {
Map result = new HashMap<>(2, 1);
result.put("demo_ds_0", DataSourceUtil.createDataSource("demo_ds_0"));
result.put("demo_ds_1", DataSourceUtil.createDataSource("demo_ds_1"));
return result;
}
执行的具体操作类关键实现如下:
private final DataSource dataSource;
/**初始化数据源*/
public RawJdbcRepository(final DataSource dataSource) {
this.dataSource = dataSource;
}
public void demo() throws SQLException {
/**创建表*/
System.out.println("1.动态创建表--------------");
this.createTable();
/**插入数据*/
System.out.println("2.插入数据--------------");
this.insertData();
/**打印查询*/
System.out.println("3.打印 Equals 查询结果--------------");
printEqualsSelect();
System.out.println("4.打印使用 In 查询结果--------------");
printInSelect();
System.out.println("4.打印使用 Hint 查询结果--------------");
printHintSimpleSelect();
// dropTable();
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:24
* @Description: 动态创建表
*/
public void createTable() throws SQLException {
execute(dataSource, "CREATE TABLE IF NOT EXISTS t_order (order_id BIGINT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id))");
execute(dataSource, "CREATE TABLE IF NOT EXISTS t_order_item (order_item_id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, user_id INT NOT NULL, PRIMARY KEY (order_item_id))");
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:25
* @Description: 删除表
*/
public void dropTable() throws SQLException {
execute(dataSource, "DROP TABLE t_order_item");
execute(dataSource, "DROP TABLE t_order");
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:25
* @Description: 插入数据
*/
public void insertData() throws SQLException {
for (int i = 1; i < 10; i++) {
long orderId = this.executeAndGetGeneratedKey(dataSource, "INSERT INTO t_order (user_id, status) VALUES (10, 'INIT')");
this.execute(dataSource, String.format("INSERT INTO t_order_item (order_id, user_id) VALUES (%d, 10)", orderId));
orderId = this.executeAndGetGeneratedKey(dataSource, "INSERT INTO t_order (user_id, status) VALUES (11, 'INIT')");
this.execute(dataSource, String.format("INSERT INTO t_order_item (order_id, user_id) VALUES (%d, 11)", orderId));
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:25
* @Description: 使用 Equals 查询数据
*/
public void printEqualsSelect() throws SQLException {
String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=?";
try (
Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setInt(1, 10);
printSimpleSelect(preparedStatement);
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:26
* @Description: 使用 In 查询数据
*/
public void printInSelect() throws SQLException {
String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id IN (?, ?)";
try (
Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setInt(1, 10);
preparedStatement.setInt(2, 11);
printSimpleSelect(preparedStatement);
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:27
* @Description: 使用 Hint 查询数据
*/
public void printHintSimpleSelect() throws SQLException {
String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id";
try (
HintManager hintManager = HintManager.getInstance();
Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
hintManager.addDatabaseShardingValue("t_order", "user_id", 11);
printSimpleSelect(preparedStatement);
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:27
* @Description: 打印并遍历查询结果
*/
private void printSimpleSelect(final PreparedStatement preparedStatement) throws SQLException {
try (ResultSet rs = preparedStatement.executeQuery()) {
while (rs.next()) {
System.out.print("order_item_id:" + rs.getLong(1) + ", ");
System.out.print("order_id:" + rs.getLong(2) + ", ");
System.out.print("user_id:" + rs.getInt(3));
System.out.println();
}
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:27
* @Description: 执行连接数据源
*/
private void execute(final DataSource dataSource, final String sql) throws SQLException {
try (
Connection conn = dataSource.getConnection();
Statement statement = conn.createStatement()) {
statement.execute(sql);
}
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:28
* @Description: 插入数据并返回主键参数值
*/
private long executeAndGetGeneratedKey(final DataSource dataSource, final String sql) throws SQLException {
long result = -1;
try (
Connection conn = dataSource.getConnection();
Statement statement = conn.createStatement()) {
statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet resultSet = statement.getGeneratedKeys();
if (resultSet.next()) {
result = resultSet.getLong(1);
}
}
return result;
}
至此可看出在分库策略上sharding-jdbc做的流程,可将通过user_id定位到制定规则的数据库中,并在插入数据时进行动态分配数据源的数据,从而将数据均分落地到到两个库所对应的克隆的实际表中,实现分库操作。该example是通过直接取模分配数据源,当然我们也可以通过自己定的分片规则算法进行实现分片操作。具体的实现流程说明下篇继续tfyy。。。