Sharding-JDBC 入门水平分库,水平分表(一)

Sharding-JDBC是当当网研发的开源分布式数据库中间件,从 3.0 开始Sharding-JDBC被包含在 Sharding-Sphere 中,之后该项目进入进入Apache孵化器,4.0版本之后的版本为Apache版本。 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding- Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和 数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景

分库分表概念

垂直分表:可以把一个宽表的字段按访问频次、是否是大字段的原则拆分为多个表,这样既能使业务清晰,还能提 升部分性能。拆分后,尽量从业务角度避免联查,否则性能方面将得不偿失。
垂直分库:可以把多个表按业务耦合松紧归类,分别存放在不同的库,这些库可以分布在不同服务器,从而使访问 压力被多服务器负载,大大提升性能,同时能提高整体架构的业务清晰度,不同的业务库可根据自身情况定制优化 方案。但是它需要解决跨库带来的所有复杂问题。
水平分库:可以把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在 不同服务器,从而使访问压力被多服务器负载,大大提升性能。它不仅需要解决跨库带来的所有复杂问题,还要解 决数据路由的问题(数据路由问题后边介绍)。
水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,这 样做能小幅提升性能,它仅仅作为水平分库的一个补充优化。 一般来说,在系统设计阶段就应该根据业务耦合松紧来确定垂直分库,垂直分表方案,在数据量及访问压力不是特 别大的情况,首先考虑缓存、读写分离、索引技术等方案。若数据量极大,且持续增长,再考虑水平分库水平分表 方案。

入门程序(采用水平分表加水平分库)

1.创建数据库

创建订单 order_db_1,order_db_2数据库

CREATE DATABASE `order_db_1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
CREATE DATABASE `order_db_2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

在 order_db_1和 order_db_2 库中创建t_order_1,t_order_2两张表

DROP TABLE IF EXISTS `t_order_1`; 
CREATE TABLE `t_order_1` ( 
`order_id` bigint(20) NOT NULL COMMENT '订单id', 
`price` decimal(10, 2) NOT NULL COMMENT '订单价格', 
`user_id` bigint(20) NOT NULL COMMENT '下单用户id', 
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态', 
PRIMARY KEY (`order_id`) USING BTREE 
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 
DROP TABLE IF EXISTS `t_order_2`; 
CREATE TABLE `t_order_2` ( 
`order_id` bigint(20) NOT NULL COMMENT '订单id', 
`price` decimal(10, 2) NOT NULL COMMENT '订单价格', 
`user_id` bigint(20) NOT NULL COMMENT '下单用户id', `
status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态', PRIMARY KEY (`order_id`) USING BTREE 
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2.引入sharding-jdbc 和 springboot整合 maven依赖
<dependency> 
	<groupId>org.apache.shardingsphere</groupId> 
	<artifactId>sharding‐jdbc‐spring‐boot‐starter</artifactId> 
	<version>4.0.0‐RC1</version> 
</dependency>
3.编写分片规则配置
server.port=8080
spring.application.name=sharding-jdbc-demo
#覆盖 bean
spring.main.allow-bean-definition-overriding=true
#mybatis 驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true

#以下是分片规则
#配置分几个库
spring.shardingsphere.datasource.names=m1,m2

#数据库的连接参数
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver‐class‐name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3307/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root

spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver‐class‐name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3307/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root

#分库策略 以user_id为分片键,分片策略为user_id % 2 + 1,user_id为偶数操作m1数据源,否则操作m2。
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.algorithmExpression=m$->{user_id % 2 + 1}

#指定t_order 表的数据分布情况
spring.shardingsphere.sharding.tables.t_order.actualDataNodes=m$->{1..2}.t_order_$->{1..2}

# 指定t_order表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.keyGenerator.column=order_id
spring.shardingsphere.sharding.tables.t_order.keyGenerator.type=SNOWFLAKE

#分表策略 以 order_id 为分片键,分片策略为order_id % 2 + 1,order_id为偶数操作t_order_1表,否则操作t_order_2表
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithmExpression=t_order_$->{order_id % 2 +1}

# 打开sql输出日志
spring.shardingsphere.props.sql.show=true

logging.level.root = info
logging.level.org.springframework.web=info
logging.level.com.itheima.dbsharding=debug
logging.level.druid.sql = debug
1.首先定义数据源 m1,m2 (这个名称可以随便定义 但是这里定义什么,下面的配置就需要使用什么)
2.配置各个数据源的连接配置参数
3.配置水平分库策略.
4.指定t_order 表的数据分部情况,实际分部情况为 m1.t_order_1,m1.t_order_2,m2.t_order_1,m2.t_order_2,使用的Groovy的表达式语法.
5.使用程序控制主键生成,这里采用雪花生成器算法
6.配置水平分表策略
4.代码
@Component
@Mapper
public interface OrderDao {

    @Insert("insert into t_order(price,user_id,status) values(#{price},#{userId},#{status})")
    int insertOrder(@Param("price")BigDecimal price,@Param("userId")Long userId,@Param("status")String status);

    @Select({"<script>" +
            "select " +
            " * " +
            " from t_order t" +
            " where t.order_id in " +
            "<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>" +
            " #{id} " +
            "</foreach>"+
            "</script>"})
    List<Map> selectOrderByIds(@Param("orderIds")List<Long> orderIds);

    @Select({"<script>"+ 
            " select"+ 
            " * "+ 
            " from t_order t "+ 
            "where t.order_id in"+ 
            "<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>"+ 
            "#{id}"+ 
            "</foreach>"+ 
            " and t.user_id = #{userId} "+ 
            "</script>" })
    List<Map> selectOrderbyUserAndIds(@Param("userId") Long userId,@Param("orderIds")List<Long> orderIds);

}
@Test
    void testInsertOrder() {
        for (int i = 0; i < 10; i++) {
            orderDao.insertOrder(new BigDecimal((i + 1) * 5), 1L, "WAIT_PAY");
        }
        for (int i = 0; i < 10; i++) {
            orderDao.insertOrder(new BigDecimal((i + 1) * 10), 2L, "WAIT_PAY");
        }
    }

    @Test
    void testSelectOrderbyIds() {
        List<Long> ids = new ArrayList<>();
        ids.add(487266182827606016L);
        ids.add(487266182856966145L);
        List<Map> maps = orderDao.selectOrderByIds(ids);
        System.out.println(maps);
    }

    //根据指定 userId测试分库以及 order_id分表
    @Test
    void testSelectOrderbyUserAndIds() {
        List<Long> orderIds = new ArrayList<>();
        orderIds.add(373422416644276224L);
        orderIds.add(373422415830581248L);
        //查询条件中包括分库的键user_id
        Long user_id = 1L;
        List<Map> orders = orderDao.selectOrderbyUserAndIds(user_id, orderIds);
        JSONArray jsonOrders = new JSONArray(orders);
        System.out.println(jsonOrders);
    }

Sharding-JDBC支持以下几种分片策略

不管理分库还是分表,策略基本一样。 
	standard:标准分片策略,对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的 分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和 RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。 RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置 RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
    complex:符合分片策略,对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和 BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复 杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发 者实现,提供最大的灵活度。 
 	inline:行表达式分片策略,对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和 IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java 代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为 t_user_0 到 t_user_7 。 
 	hint:Hint分片策略,对应HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。对于分片字段 非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工 登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。 none:不分片策略,对应NoneShardingStrategy。不分片的策略。 

上面程序中采用的是 inline:行表达式分片策略,适用于比较简单的分片算法,复杂的分片算法可以使用 standard分片策略来实现

分库策略

1.分库策略配置修改为
inline 策略配置:

spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.algorithmExpression=m$->{user_id % 2 + 1}

standard 策略配置:(需要自定义实现类实现分片算法)

spring.shardingsphere.sharding.tables.t_order.databaseStrategy.standard.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.standard.preciseAlgorithmClassName=com.study.sharding.config.DatabaseOrderShardingAlgofithm

2.standard分库策略算法实现类

@Configuration
public class DatabaseOrderShardingAlgofithm implements PreciseShardingAlgorithm {
    /**
     *
     * 根据 userid进行分库
     * @Author
     * @Description //TODO
     * @Date
     * @Param
     * @return
     **/
    @Override
    public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
    	//可以进行复杂的分片算法
        Long userId = (Long)preciseShardingValue.getValue();
        String l = userId % 2 + 1 + "";
        System.out.println("得到的 userId 为:"+userId);
        System.out.println("分表算法后数据库序号为:"+l);
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()){
            String next = (String) iterator.next();
            if(next.endsWith(l)){
                System.out.println("返回的实际数据库名:"+next);
                return next;
            }
        }
        throw new IllegalArgumentException();
    }
}

分表策略

1.分表策略配置修改为
inline 分表策略配置:

spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithmExpression=t_order_$->{order_id % 2 +1}

standard 分表策略配置:(需要自定义实现类实现分表算法)

spring.shardingsphere.sharding.tables.t_order.tableStrategy.standard.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.standard.preciseAlgorithmClassName=com.study.sharding.config.TablesOrderShardingAlgofithm

2. standard分表策略算法实现类

@Configuration
public class TablesOrderShardingAlgofithm implements PreciseShardingAlgorithm {

    @Override
    public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
        Long id = (Long) preciseShardingValue.getValue();
        System.out.println("得到生成的 id为:" + id);
        long i = id % 2 + 1;
        System.out.println("经过自定义分表算法后:" + i);
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            String echo = (String) iterator.next();
            if (echo.endsWith(i+"")) {
                System.out.println("返回的实际表名为:" + echo);
                return echo;
            }
        }
        throw new IllegalArgumentException();
    }
}

若对其他分片策略细节若感兴趣,请查阅官方文档: sharding 官方文档

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值