發(fā)現(xiàn)夢想與現(xiàn)實的差距后搀军,堅持就越來越遠
前段時間在刷博客的時候抛猖,無意間刷到了一篇介紹數(shù)據(jù)庫分庫分表的框架,于是網(wǎng)上搜了一下啸罢,shardingjdbc 當當團隊網(wǎng)團隊開源的一個專做對于java開發(fā)在原生的jdbc層上封裝的一層輕量級中間件忍啤。我們看一下它的官網(wǎng)(http://shardingjdbc.io/index_zh.html)給的介紹:
對于shardingjdbc來說無非就是通過java代碼在原生的jdbc上封裝了一層腐宋,如果說自己去研發(fā)一個這樣的中間件也不是不可能。就是后期考慮到出現(xiàn)的bug檀轨、效率胸竞、性能等問題,沒有一個更好的前期規(guī)劃也很難完成参萄。定位上面這個輕量級的框架通過上面給的架構(gòu)圖來說卫枝,無非就是在jdbc層進行更好的封裝集成,從而形成一個加強版的JDBC連接數(shù)據(jù)庫的工具讹挎。沒有任何侵入性校赤,完全可以兼容任何的ORM框架,這一點確實是可以值得一用的筒溃。
下面我們來看看sharding-jdbc為我們提供的功能有哪些马篮?官網(wǎng)也有具體的介紹:
- 數(shù)據(jù)分片
數(shù)據(jù)分片主要以分庫+分表的操作、支持的sql語句查詢方式以及分布式主鍵的處理方案等- 讀寫分離
讀寫分離主要以一主多從怜奖、統(tǒng)一線程數(shù)據(jù)的一致性以及分庫分表時的讀寫分離操作等- 事物處理
主要針對TCC分布式事物處理解決- 分布式治理
主要一些熔斷措施浑测,以及配置中心的配置。
基本的說明就這些,后續(xù)將會沿著例子區(qū)找到源碼進行進一步的研究和探討迁央。接下來可以去github上將shardingjdbc-example(https://github.com/sharding-sphere/sharding-sphere-example)例子下載下來運行掷匠,如下圖,由于版本原因選擇branches版本下載:
本機運行例子需要選擇master版本的岖圈,dev是開發(fā)版本讹语,暫時maven代碼庫還沒有更新上去。
可以直接下載zip也可以直接使用:
git clone https://github.com/sharding-sphere/sharding-sphere-example.git
直接下載下來蜂科。然后將下載下來的項目導入到IDEA中如下圖:
注意:當前我使用的sharding-jdbc版本是2.0.3顽决。git可能會是最新版本。應該都可以用的運行的导匣,只是目錄有點不一樣了擎值。
我們來看看官網(wǎng)給的例子,首先在每個項目模塊下面對java實現(xiàn)的每個ORM框架進行了組個demo演示逐抑,無論我們使用哪種框架,只需要導入所需的jar包便可以完美的和ORM框架結(jié)合屹蚊。
首先我們先來看看在與原生的jdbc結(jié)合的時候sharding-jdbc是怎么實現(xiàn)分庫分表的策略的厕氨。找到
sharding-jdbc-raw-jdbc-example
該模塊下的
sharding-jdbc-raw-jdbc-java-example // 原生代碼與sharding-jdbc結(jié)合
/**僅讀寫分離操作*/
RawJdbcJavaMasterSlaveOnlyMain
/**讀寫分離+分庫分表操作*/
RawJdbcJavaShardingAndMasterSlaveMain
/**分庫分表操作*/
RawJdbcJavaShardingDatabaseAndTableMain
/**僅分庫操作*/
RawJdbcJavaShardingDatabaseOnlyMain
/**僅做分表操作*/
RawJdbcJavaShardingTableOnlyMain
注意:在運行demo前請先將基本的數(shù)據(jù)庫先初始化好,在每個模塊的根目錄下會有一個sql文件汹粤,是根據(jù)自己的業(yè)務來制定的建庫規(guī)則來創(chuàng)建對應的庫:
在創(chuàng)建好數(shù)據(jù)庫后將會有12個對應的數(shù)據(jù)庫命斧,待后續(xù)example會使用到:
下面將從分庫模塊學習使用入口:
/**僅分庫操作*/
RawJdbcJavaShardingDatabaseOnlyMain
使用到的庫:
- demo_ds_0
- demo_ds_1
運行該example結(jié)果如下:
1.動態(tài)創(chuàng)建表成功--------------
2.插入數(shù)據(jù)成功--------------
3.打印 Equals 查詢結(jié)果--------------
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 查詢結(jié)果--------------
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 查詢結(jié)果--------------
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
數(shù)據(jù)庫中的表如圖所示:
基本執(zhí)行步驟如下:
/**
* @author: ErnestFei
* @date: 2018/5/27 19:15
* @Description: 配置數(shù)據(jù)源
*/
private static DataSource getShardingDataSource() throws SQLException {
/**初始化sharding配置*/
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
/**添加創(chuàng)建主訂單表*/
shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
/**添加創(chuàng)建訂單項表*/
shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration());
/**根據(jù)主訂單表中的user_id字段定位該筆訂單入庫規(guī)則,這里使用user_id的奇偶數(shù)來定位插庫入口*/
shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "demo_ds_${user_id % 2}"));
/**創(chuàng)建好數(shù)據(jù)源并加入配置中得到數(shù)據(jù)源配置*/
return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new HashMap<String, Object>(), new Properties());
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:20
* @Description: 主訂單表規(guī)則的基本配置
*/
private static TableRuleConfiguration getOrderTableRuleConfiguration() {
TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
orderTableRuleConfig.setLogicTable("t_order");
/**設(shè)置主鍵*/
orderTableRuleConfig.setKeyGeneratorColumnName("order_id");
return orderTableRuleConfig;
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:22
* @Description: 訂單項目表的配置規(guī)則
*/
private static TableRuleConfiguration getOrderItemTableRuleConfiguration() {
TableRuleConfiguration orderItemTableRuleConfig = new TableRuleConfiguration();
orderItemTableRuleConfig.setLogicTable("t_order_item");
return orderItemTableRuleConfig;
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:22
* @Description: 創(chuàng)建兩個動態(tài)數(shù)據(jù)源 demo_ds_0 嘱兼、 demo_ds_1
*/
private static Map<String, DataSource> createDataSourceMap() {
Map<String, DataSource> 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;
}
執(zhí)行的具體操作類關(guān)鍵實現(xiàn)如下:
private final DataSource dataSource;
/**初始化數(shù)據(jù)源*/
public RawJdbcRepository(final DataSource dataSource) {
this.dataSource = dataSource;
}
public void demo() throws SQLException {
/**創(chuàng)建表*/
System.out.println("1.動態(tài)創(chuàng)建表--------------");
this.createTable();
/**插入數(shù)據(jù)*/
System.out.println("2.插入數(shù)據(jù)--------------");
this.insertData();
/**打印查詢*/
System.out.println("3.打印 Equals 查詢結(jié)果--------------");
printEqualsSelect();
System.out.println("4.打印使用 In 查詢結(jié)果--------------");
printInSelect();
System.out.println("4.打印使用 Hint 查詢結(jié)果--------------");
printHintSimpleSelect();
// dropTable();
}
/**
* @author: ErnestFei
* @date: 2018/5/27 19:24
* @Description: 動態(tài)創(chuàng)建表
*/
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: 插入數(shù)據(jù)
*/
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 查詢數(shù)據(jù)
*/
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 查詢數(shù)據(jù)
*/
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 查詢數(shù)據(jù)
*/
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: 打印并遍歷查詢結(jié)果
*/
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: 執(zhí)行連接數(shù)據(jù)源
*/
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: 插入數(shù)據(jù)并返回主鍵參數(shù)值
*/
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定位到制定規(guī)則的數(shù)據(jù)庫中,并在插入數(shù)據(jù)時進行動態(tài)分配數(shù)據(jù)源的數(shù)據(jù)芹壕,從而將數(shù)據(jù)均分落地到到兩個庫所對應的克隆的實際表中汇四,實現(xiàn)分庫操作。該example是通過直接取模分配數(shù)據(jù)源踢涌,當然我們也可以通過自己定的分片規(guī)則算法進行實現(xiàn)分片操作通孽。具體的實現(xiàn)流程說明下篇繼續(xù)tfyy。睁壁。背苦。