zoukankan      html  css  js  c++  java
  • 【分库分表】sharding-jdbc实践—分库分表入门

    一、准备工作

    1、准备三个数据库:db0、db1、db2

    2、每个数据库新建两个订单表:t_order_0、t_order_1

    DROP TABLE IF EXISTS `t_order_x`;
    CREATE TABLE `t_order_x` (
      `id` bigint NOT NULL AUTO_INCREMENT,
      `user_id` bigint NOT NULL,
      `order_id` bigint NOT NULL,
      `order_no` varchar(30) NOT NULL,
      `isactive` tinyint NOT NULL DEFAULT '1',
      `inserttime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      `updatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    二、分库分表配置

     数据源的配置可以使用任何链接池,本例用druid为例。

    1、引言依赖包:

    引用最新的maven包

    <sharding-jdbc.version>2.0.1</sharding-jdbc.version>


    <dependency>
    <groupId>io.shardingjdbc</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>${sharding-jdbc.version}</version>
    </dependency>

    2、配置DataSource:

        @Bean(name = "shardingDataSource", destroyMethod = "close")
        @Qualifier("shardingDataSource")
        public DataSource getShardingDataSource() {
            // 配置真实数据源
            Map<String, DataSource> dataSourceMap = new HashMap<>(3);
    
            // 配置第一个数据源
            DruidDataSource dataSource1 = createDefaultDruidDataSource();
            dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource1.setUrl("jdbc:mysql://localhost:3306/db0");
            dataSource1.setUsername("root");
            dataSource1.setPassword("root");
            dataSourceMap.put("db0", dataSource1);
    
            // 配置第二个数据源
            DruidDataSource dataSource2 = createDefaultDruidDataSource();
            dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource2.setUrl("jdbc:mysql://localhost:3306/db1");
            dataSource2.setUsername("root");
            dataSource2.setPassword("root");
            dataSource2.setName("db1-0001");
            dataSourceMap.put("db1", dataSource2);
    
            // 配置第三个数据源
            DruidDataSource dataSource3 = createDefaultDruidDataSource();
            dataSource3.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource3.setUrl("jdbc:mysql://localhost:3306/db2");
            dataSource3.setUsername("root");
            dataSource3.setPassword("root");
            dataSourceMap.put("db2", dataSource3);
    
    
            // 配置Order表规则
            TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
            orderTableRuleConfig.setLogicTable("t_order");
            orderTableRuleConfig.setActualDataNodes("db${0..2}.t_order_${0..1}");
            //orderTableRuleConfig.setActualDataNodes("db0.t_order_0,db0.t_order_1,db1.t_order_0,db1.t_order_1,db2.t_order_0,db2.t_order_1");
    
            // 配置分库策略(Groovy表达式配置db规则)
            orderTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "db${user_id % 3}"));
    
            // 配置分表策略(Groovy表达式配置表路由规则)
            orderTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_${order_id % 2}"));
    
            // 配置分片规则
            ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
            shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
    
            // 配置order_items表规则...
    
            // 获取数据源对象
            DataSource dataSource = null;
            try {
                dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), new Properties());
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return dataSource;
        }

    可以使用Druid监控db。

    三、示例验证

    1、新增数据

    @Slf4j
    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Autowired
        private OrderMapper orderMapper;
    
        @RequestMapping("/add")
        public void addOrder() {
            OrderEntity entity10 = new OrderEntity();
            entity10.setOrderId(10000L);
            entity10.setOrderNo("No1000000");
            entity10.setUserId(102333001L);
            orderMapper.insertSelective(entity10);
    OrderEntity entity11
    = new OrderEntity(); entity11.setOrderId(10001L); entity11.setOrderNo("No1000000"); entity11.setUserId(102333000L); orderMapper.insertSelective(entity11); } }

    依据配置的分片规则

    • DB路由规则:user_id % 3:

         102333001 % 3 = 1

    102333000 % 3 = 0

    • 表路由规则:order_id % 2:

    10000 % 2 = 0

    10001 % 2 = 1

    userid=102333001,orderId=10000的数据落地到db1.t_order_0
    userid=102333000,orderId=10001的数据落地到db0.t_order_1

    2、未指定分片规则字段的查询

        /**广播遍历所有的库和表*/
        @RequestMapping("get")
        public void getOrder() {
            List<Integer> ids = new ArrayList<>();
            ids.add(4);
            List<OrderEntity> orderEntities = orderMapper.selectByPrimaryIds(ids);
    
            log.info(JSON.toJSONString(orderEntities));
        }

    由druid监控sql得知,查询被广播到db0、db1、db2的各个表里,如下监控所示:

    3、不能执行批量插入操作

    不支持对不同分片规则的字段值进行批量插入操作,类似sql:insert into t_order values(x,x,x,x),(x,x,x,x),(x,x,x,x)

     4、谨慎修改分片规则字段

    如果修改了分片规则的字段,比如本例的user_id或order_id,因为路由规则会造成数据存在,却查不到数据的情况。

        @RequestMapping("/upd")
        public void update() {
            OrderEntity orderWhere = new OrderEntity();
            orderWhere.setOrderId(10001L);
            orderWhere.setUserId(102333001L);
            orderWhere.setId(4L);
    
            OrderEntity orderSet = new OrderEntity();
            orderSet.setOrderId(10002L);
            orderSet.setOrderNo("修改订单号");
    
            orderMapper.updateByPredicate(orderSet, orderWhere);
    
            /**查不到,orderId更改会引起路由查询失败*/
            OrderEntity predicate = new OrderEntity();
            predicate.setOrderId(10002L);
            OrderEntity entity = orderMapper.selectSingleByPredicate(predicate);
            log.info("after update orderEntity:"+JSON.toJSONString(entity));
        }

    四、sharding建表

    目前配置并验证了3个库,每库2个order表的场景:

    如果分库分表数量比较多,仅仅创建表就是一件很繁琐的事情。sharding查询数据不指定分片规则字段时,会自动路由到各个库的各个表里查询,不知道大家有没有想到:如果配置要创建表的路由规则,用sharding来执行一条创建sql的语句,会不会就自动路由到各个库去执行了,也就代替人工去各个库建表了呢?下面来验证一下这个想法,以创建t_order_items表为例:

    1、配置t_order_items的规则

    在上面配置t_order规则下面补充t_order_items的规则配置:

            // 省略配置order_item表规则...
            TableRuleConfiguration orderItemTableRuleConfig = new TableRuleConfiguration();
            orderItemTableRuleConfig.setLogicTable("t_order_items");
            orderItemTableRuleConfig.setActualDataNodes("db${0..2}.t_order_items_${0..1}");// 配置分库策略
            orderItemTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "db${order_id % 3}"));
    
            // 配置分表策略
            orderItemTableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_items_${order_id % 2}")); shardingRuleConfig.getTableRuleConfigs().add(orderItemTableRuleConfig);

    2、t_order_items建表sql语句

        <update id="createTItemsIfNotExistsTable">
            CREATE TABLE IF NOT EXISTS `t_order_items` (
              `id` bigint NOT NULL AUTO_INCREMENT,
              `order_id` bigint NOT NULL,
              `unique_no` varchar(32) NOT NULL,
              `quantity` int NOT NULL DEFAULT '1',
              `is_active` tinyint NOT NULL DEFAULT 1,
              `inserttime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
              `updatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
              PRIMARY KEY (`id`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
        </update>

    3、OrderItemsMapper方法

    Integer createTItemsIfNotExistsTable();

    4、执行方法

    orderItemsMapper.createTItemsIfNotExistsTable();

    查看db0、db1、db2:

    验证了我们上面的想法,建表成功了。 

    附录

    如果没有配置t_order_items规则,执行建表sql会报错:

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error updating database. Cause: io.shardingjdbc.core.exception.ShardingJdbcException: Cannot find table rule and default data source with logic table: 't_order_items'
    ### The error may involve defaultParameterMap
    ### The error occurred while setting parameters
    ### SQL: CREATE TABLE IF NOT EXISTS `t_order_items` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_id` bigint NOT NULL, `unique_no` varchar(32) NOT NULL, `quantity` int NOT NULL DEFAULT '1', `is_active` tinyint NOT NULL DEFAULT 1, `inserttime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    ### Cause: io.shardingjdbc.core.exception.ShardingJdbcException: Cannot find table rule and default data source with logic table: 't_order_items'

  • 相关阅读:
    cmake
    docker
    rust
    linux
    FPGA
    visual studio
    win+R
    word文档的导出(用freemarker模板导出)(桃)
    iconfont的引入方法
    jquery 日期插件
  • 原文地址:https://www.cnblogs.com/mr-yang-localhost/p/8280500.html
Copyright © 2011-2022 走看看