1. 程式人生 > >sharding-jdbc分庫分表規則(1)-單表查詢

sharding-jdbc分庫分表規則(1)-單表查詢

前言

當資料量到達一定數量級的時候,一般都會考慮分庫分表。sharding-jdbc是一個開源的客戶端分庫分表基礎類庫,以一個jar包的形式提供,基於原生的JDBC驅動進行增強,基本能夠無縫整合舊程式碼,非常的便捷。本小節以一個最簡單的單表查詢淺析概要流程。

建庫建表

ds_jdbc_0 t_order_0 , t_order_1
ds_jdbc_1 t_order_0 , t_order_1



訂單表邏輯語名:
CREATE TABLE IF NOT EXISTS t_order (order_id INT NOT NULL, user_id

INT NOT NULL, status VARCHAR(50), PRIMARY KEY (order_id))

配置

為簡單起見,使用基本的jdbc進行操作,最精簡的程式碼如下:

public final class SingleSelect {

    public static void main(final String[] args) throws SQLException {
        DataSource dataSource = getOrderShardingDataSource();
        printSingleSelect(dataSource);
    }

    private
static ShardingDataSource getOrderShardingDataSource() { DataSourceRule dataSourceRule = new DataSourceRule(createDataSourceMap()); TableRule orderTableRule = TableRule.builder("t_order").actualTables(Arrays.asList("t_order_0", "t_order_1")).dataSourceRule(dataSourceRule).build(); ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(orderTableRule)) .databaseShardingStrategy(new
DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm())) .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())).build(); return new ShardingDataSource(shardingRule); } private static void printSingleSelect(final DataSource dataSource) throws SQLException { String sql = "SELECT * FROM t_order where user_id=? and order_id=?"; try ( Connection conn = dataSource.getConnection(); PreparedStatement preparedStatement = conn.prepareStatement(sql)) { preparedStatement.setInt(1, 10); preparedStatement.setInt(2, 1001); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { System.out.println(rs.getInt(1)); System.out.println(rs.getInt(2)); System.out.println(rs.getInt(3)); } } } } private static DataSource createDataSource(final String dataSourceName) { BasicDataSource result = new BasicDataSource(); result.setDriverClassName(com.mysql.jdbc.Driver.class.getName()); result.setUrl(String.format("jdbc:mysql://127.0.0.1:3306/%s", dataSourceName)); result.setUsername("root"); result.setPassword("123456"); return result; } private static Map<String, DataSource> createDataSourceMap() { Map<String, DataSource> result = new HashMap<>(2); result.put("ds_jdbc_0", createDataSource("ds_jdbc_0")); result.put("ds_jdbc_1", createDataSource("ds_jdbc_1")); return result; } }

分庫分表最主要有幾個配置:
1. 有多少個數據源
2. 每張表的邏輯表名和所有物理表名
3. 用什麼列進行分庫以及分庫演算法
4. 用什麼列進行分表以及分表演算法

本示例定義了兩個資料來源: ds_jdbc_0 和 ds_jdbc_1,定義了邏輯表:t_order,以及物理表:t_order_0 和 t_order_0。採用 user_id列進行分庫,order_id列進行分表。一切準備就緒,我們的目標是執行如下的語句:

SELECT * FROM t_order where user_id=10 and order_id=1001

我們想實現如下的目標:
分庫:
user_id % 2 = 0 的資料儲存到 ds_jdbc_0 ,為1的資料儲存到 ds_jdbc_1
分表:
order_id % 2 = 0 的資料儲存到 t_order_0 ,為1的資料儲存到 t_order_1

這屬於業務的範疇,我們必須清楚告知sharding-jdbc我們的意圖,所以要提供分庫分表策略類,
看看分庫策略類:

public final class ModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer> {

    @Override
    public String doEqualSharding(final Collection<String> dataSourceNames, final ShardingValue<Integer> shardingValue) {
        for (String each : dataSourceNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> dataSourceNames, final ShardingValue<Integer> shardingValue) {
        return null;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> dataSourceNames, final ShardingValue<Integer> shardingValue) {
        return null;
    }
}

由於我們使用了 = 號條件進行查詢,所以只實現了 doEqualSharding 這個方法。程式碼非常簡單,引數dataSourceNames的值為[ds_jdbc_0 , ds_jdbc_1],而shardingValue在執行的時候可以獲取到 user_id=10這個值,在doEqualSharding 中,我們自己根據user_id的值返回路由的庫的名稱。

接下來看看分表策略類:

public final class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Integer> {

    @Override
    public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<Integer> shardingValue) {
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> tableNames, final ShardingValue<Integer> shardingValue) {
        return null;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> tableNames, final ShardingValue<Integer> shardingValue) {
        return null;
    }
}

一樣實現了 doEqualSharding這個方法,因為我們的條件中有 order_id=1001,在執行回撥時,tableNames的值為[t_order_0 ,t_order_1 ],我們可以決定如何路由到真實的表名。

流程淺析

先來看個基本的流程圖:

這裡寫圖片描述

我們再來看看我們的目標sql語句:

SELECT * FROM t_order where user_id=10 and order_id=1001
  1. 通過sql解析發現有一張邏輯表名稱:t_order

    發現兩個條件: t_order.userId = 10 和 t_order.order_id = 1001

  2. 通過 t_order 找到對應的表配置規則 TableRule,這裡定義了兩個物理表: t_order_0和t_order_1

  3. 根據 TableRule找出目標資料來源集合

    通過TableRule找到DatabaseShardingStrategy,得到分庫列:user_id
    通過t_order 和 user_id聯合為key從條件中查詢,找到了t_order.userId = 10,
    再結合引數值(10),得到分片值
    ShardingValue(logicTableName=t_order, columnName=user_id, value=10),
    呼叫自定義分庫策略類(傳輸[ds_jdbc_0 , ds_jdbc_1],ShardingValue),得到最終的資料來源名稱集合[ds_jdbc_0]

  4. 根據TableRule和資料來源 [ds_jdbc_0] 找到物理表

    通過TableShardingStrategy找到表的分片列: order_id
    通過t_order 和 order_id聯合為key從條件中查詢,找到了order_id=1001,再結合引數值(1001),得到分片值
    ShardingValue(logicTableName=t_order, columnName=order_id, value=1001),
    呼叫自定義分表策略類(傳輸[t_order_0,t_order_1],ShardingValue),得到[ds_jdbc_0]下的最終物理表集合[t_order_1]

  5. 根據資料來源和物理表,得到 DataNode的集合

    根據得到的[ds_jdbc_0] 和 [t_order_1],構建 DataNode集合,每一個DataNode表示 xx庫.xx表,此示例下得到一個DataNode實體: [ds_jdbc_0].[t_order_1]

  6. 根據 DataNode生成TableUnits集合

    TableUnit由 邏輯表,物理庫,物理表 三個欄位組成,
    此示例為: t_order 、ds_jdbc_0 、t_order_1

  7. SQL重寫

    構建重寫引擎SQLRewriteEngine,根據TableUnits生成對應最終的sql語句執行單元(替換成最終表名),得到執行單元集合(ExecutionUnits),一個執行單元表示在哪個庫,執行什麼sql語句

  8. ExecutionUnits轉換為PreparedStatement,最後又轉為PreparedStatementUnit

  9. 執行緒池併發執行PreparedStatementUnit,最後再合併結果返回

多庫單表又如何

通過上面的分析,我們已經知道了單庫單表的基本查詢邏輯,現在把sql簡單調整為:

SELECT * FROM t_order where order_id=1001

這次,我們發現搜尋條件並沒有分庫鍵,這時候,引擎並不會呼叫分庫策略類,直接認定目標庫為[ds_jdbc_0,ds_jdbc_1],而分表的邏輯是不變的,既然目標庫有兩個,後面生成的DataNode,TableUnits,PreparedStatementUnit 將是以前數量的兩倍,所以這回,引擎最終將會發起多個sql語句的併發執行,併合並最終的結果再返回。

總結

以上基於一個最簡單的查詢拆解了基本的流程,當然,sql解析的細節還是很複雜的,但不是本文關注的重點,本文主要關注一個簡單的sql語句,在sharding-jdbc下是如何達到分庫分表的目的的,後續再分析更多的sql語句的執行。

參考

相關推薦

sharding-jdbc分庫規則(1)-查詢

前言 當資料量到達一定數量級的時候,一般都會考慮分庫分表。sharding-jdbc是一個開源的客戶端分庫分表基礎類庫,以一個jar包的形式提供,基於原生的JDBC驅動進行增強,基本能夠無縫整合舊程式碼,非常的便捷。本小節以一個最簡單的單表查詢淺析概要流程。

SpringBoot使用Sharding-JDBC分庫

ber password view select ext dep 簡單配置 匹配 nap 本文介紹SpringBoot使用當當Sharding-JDBC進行分庫分表。 1.有關Sharding-JDBC 有關Sharding-JDBC介紹這裏就不在多說,之前Shardi

學習sharding-jdbc 分庫擴充套件框架

前一段時間研究阿里的分庫框架cobar-client,cobar-client是基於ibatis的SqlMapClientTemplate進行了一層薄薄的封裝,分裝成CobarSqlMapClientTemplate,在使用者在CRUD的時候可以透明的進行操作,算是現

Sharding-JDBC分庫使用例項

Sharding-JDBC是噹噹開源的用於分庫分表的基礎類庫。定位輕量級java框架,可以通過客戶端直接連線資料庫,只需要在增加額外的資料來源配置就可以輕鬆實現完整的分庫分表功能。 Sharding-JDBC是一個開源的適用於微服務的分散式資料訪問基礎類

sharding-jdbc 分庫的 4種分片策略,還蠻簡單的

上文[《快速入門分庫分表中介軟體 Sharding-JDBC (必修課)》](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247488500&idx=1&sn=108bf704a54b0a9638e84698deb3ce4c&chksm=9b8

利用Sharding-Jdbc實現

利用Sharding-Jdbc實現分表 由 匿名 (未驗證) 提交於 2018-07-25 15:50:57 132 次瀏覽 閒來無事,喜歡研究一些自己未接觸過的技術~ 看到了噹噹開源的Sharding-JDBC元件,它可以在幾乎不修改程式碼的情況下完成

Springboot使用sharding進行分庫

pom.xml檔案加入sharing引用和druid引用 <dependency>             <groupId>com.dangdang</groupId>             <artifactId&

Mysql(四)-1:查詢

name 格式 none between 排列 字段 group 員工 tle 一 單表查詢的語法 SELECT 字段1,字段2... FROM 表名 WHERE 條件 GROUP

mysql四-1:查詢

單表查詢一、單表查詢的語法SELECT 字段1,字段2... FROM 表名 WHERE 條件 GROUP BY field HAVING 篩選 ORDER BY field

jdbc筆記(一) 使用Statement對的CRUD操作

jdbc連線mysql並執行簡單的CRUD的步驟: 1.註冊驅動(需要丟擲/捕獲異常) Class.forName("com.mysql.jdbc.Driver"); 2.建立連線需要丟擲/捕獲異常) connection = DriverManager.getConnection("

jdbc筆記(二) 使用PreparedStatement對的CRUD操作

  首先宣告,本文只給出程式碼,並不是做教程用,如有不便之處,還請各位見諒。   PreparedStatement相較於Statement,概括來說,共有三個優勢:   1. 程式碼的可讀性和易維護性:PreparedStatement不需要像Statement那樣拼接sql語句,而是用?代替,再對其進

Django - ORM資料庫操作 - 結構、操作

目錄 一、ORM (物件關係對映 Object Relational Mapping) 二、表結構的建立和修改  1、表結構的建立 1-1 setting內建立資料庫連線 1-2 models.py 內建立模型 1-3 migrations 內 __init__.p

Day055--MySQL--外來鍵的變種,的關係,查詢,多查詢, 內連線,左右連線,全外連線

表和表的關係 ---- 外來鍵的變種 * 一對多或多對一 多對多 一對一 如何找出兩張表之間的關係 分析步驟: #1、先站在左表的角度去找 是否左表的多條記錄可以對應右表的一條記錄,如果是,則證明左表的一個欄位foreign key 右表一個欄位(通常是id) #2、再站在右表的角度去找 是否右表

數據庫分庫中間件 Sharding-JDBC 源碼分析 —— SQL 解析(六)之刪除SQL

java 後端 架構 數據庫 中間件關註微信公眾號:【芋道源碼】有福利:RocketMQ / MyCAT / Sharding-JDBC 所有源碼分析文章列表RocketMQ / MyCAT / Sharding-JDBC 中文註釋源碼 GitHub 地址您對於源碼的疑問每條留言都將得到認真回復。甚至不知道如

數據庫分庫中間件 Sharding-JDBC 源碼分析 —— 布式主鍵

java 後端 架構 數據庫 中間件關註**微信公眾號:【芋道源碼】**有福利:RocketMQ / MyCAT / Sharding-JDBC 所有源碼分析文章列表RocketMQ / MyCAT / Sharding-JDBC 中文註釋源碼 GitHub 地址您對於源碼的疑問每條留言都將得到認真回復。甚至

分庫sharding-jdbc + spring boot對大進行分庫

一、前言       最近小編跳槽了,剛好入職了一家移動網際網路公司。非常的幸運。來新公司後的第一個專案就是對通知服務進行優化改進,其中,一個業務就是當用戶登入的時候,就會登入訪問通知表,根據使用者id載入通知資訊。由於通知量已經上億了,在查詢的時候是非常慢的。

Spring Boot 結合 Sharding-Jdbc分庫示例

對於一些大系統,資料庫資料量很大,業務量特別大的時候,而我們的資料庫及表的對於大資料量的時候,處理的效能就不容樂觀,這個時候我們就需要對我們的資料和表做分庫分表處理了。一般分庫分表都會採用資料庫中介軟體,像Mycat這種中介軟體,它幫我們做資料來源,路由對映控制。而今天介紹的Sharding

分庫框架cobar,cobar-client,tddl,sharding-JDBC

前一段時間研究阿里的分庫框架cobar-client,cobar-client是基於ibatis的SqlMapClientTemplate進行了一層薄薄的封裝,分裝成CobarSqlMapClientTemplate,在使用者在CRUD的時候可以透明的進行操作,算是現在大多公司分庫的一個成熟解決方

sharding-jdbc結合mybatis實現分庫功能

源自:https://www.cnblogs.com/zwt1990/p/6762135.html最近忙於專案已經好久幾天沒寫部落格了,前2篇文章我給大家介紹了搭建基礎springMvc+mybatis的maven工程,這個簡單框架已經可以對付一般的小型專案。但是我們實際專案

噹噹分庫中介軟體-sharding-jdbc

使用指南 閱讀本指南前,請先閱讀快速起步。本文件使用更復雜的場景進一步介紹Sharding-JDBC的分庫分表能力。 資料庫模式 本文件中提供了兩個資料來源db0和db1,每個資料來源之中包含了兩組表t_order_0和t_order_1,t_order_item_