sharding-sphere学习(1)之-[raw-example]

发现梦想与现实的差距后,坚持就越来越远

前段时间在刷博客的时候,无意间刷到了一篇介绍数据库分库分表的框架,于是网上搜了一下,shardingjdbc 当当团队网团队开源的一个专做对于java开发在原生的jdbc层上封装的一层轻量级中间件。我们看一下它的官网(http://shardingjdbc.io/index_zh.html)给的介绍:

sharding-sphere

对于shardingjdbc来说无非就是通过java代码在原生的jdbc上封装了一层,如果说自己去研发一个这样的中间件也不是不可能。就是后期考虑到出现的bug、效率、性能等问题,没有一个更好的前期规划也很难完成。定位上面这个轻量级的框架通过上面给的架构图来说,无非就是在jdbc层进行更好的封装集成,从而形成一个加强版的JDBC连接数据库的工具。没有任何侵入性,完全可以兼容任何的ORM框架,这一点确实是可以值得一用的。


sharding-jdbc

下面我们来看看sharding-jdbc为我们提供的功能有哪些?官网也有具体的介绍:

  • 数据分片
    数据分片主要以分库+分表的操作、支持的sql语句查询方式以及分布式主键的处理方案等
  • 读写分离
    读写分离主要以一主多从、统一线程数据的一致性以及分库分表时的读写分离操作等
  • 事物处理
    主要针对TCC分布式事物处理解决
  • 分布式治理
    主要一些熔断措施,以及配置中心的配置。

基本的说明就这些,后续将会沿着例子区找到源码进行进一步的研究和探讨。接下来可以去github上将shardingjdbc-example(https://github.com/sharding-sphere/sharding-sphere-example)例子下载下来运行,如下图,由于版本原因选择branches版本下载:

分支

本机运行例子需要选择master版本的,dev是开发版本,暂时maven代码库还没有更新上去。
master

可以直接下载zip也可以直接使用:

git clone https://github.com/sharding-sphere/sharding-sphere-example.git

直接下载下来。然后将下载下来的项目导入到IDEA中如下图:

example运行例子

注意:当前我使用的sharding-jdbc版本是2.0.3。git可能会是最新版本。应该都可以用的运行的,只是目录有点不一样了。

我们来看看官网给的例子,首先在每个项目模块下面对java实现的每个ORM框架进行了组个demo演示,无论我们使用哪种框架,只需要导入所需的jar包便可以完美的和ORM框架结合。
首先我们先来看看在与原生的jdbc结合的时候sharding-jdbc是怎么实现分库分表的策略的。找到

sharding-jdbc-raw-jdbc-example
sharding-jdbc-raw-jdbc-example

该模块下的

sharding-jdbc-raw-jdbc-java-example // 原生代码与sharding-jdbc结合
/**仅读写分离操作*/
RawJdbcJavaMasterSlaveOnlyMain
/**读写分离+分库分表操作*/
RawJdbcJavaShardingAndMasterSlaveMain
/**分库分表操作*/
RawJdbcJavaShardingDatabaseAndTableMain
/**仅分库操作*/
RawJdbcJavaShardingDatabaseOnlyMain
/**仅做分表操作*/
RawJdbcJavaShardingTableOnlyMain

注意:在运行demo前请先将基本的数据库先初始化好,在每个模块的根目录下会有一个sql文件,是根据自己的业务来制定的建库规则来创建对应的库:


需要执行的建库sql

在创建好数据库后将会有12个对应的数据库,待后续example会使用到:


初始化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<String, Object>(), 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<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;
    }

执行的具体操作类关键实现如下:

    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。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容