shardingjdbc分库分表入门实践

shardingjdbc介绍
定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。它使⽤客⼾端直连数据库,以 jar 包形式提供服务,⽆需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适⽤于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使⽤ JDBC。
  • ⽀持任何第三⽅的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
  • ⽀持任意实现 JDBC 规范的数据库,⽬前⽀持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。
    ——官方文档《shardingsphere_docs_cn.pdf》

一、项目说明

通过整合springboot+mybatisplus+shardingjdbc,包含订单、商品和用户三个实体的增删改查操作的分库分表demo工程
项目结构

image.png

二、准备

三个数据库,实现水平分库,三个表结构一致的user表,实现水平分表。
sql脚本,在resources/sql/ddl.txt中,按照脚本可以创建需要的数据库和表。


image.png

三、依赖

引入shardingjdbc依赖

<dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-namespace</artifactId>
            <version>3.1.0</version>
        </dependency>

四、配置

shardingjdbc可以做到对代码零侵入性,在springboot整合mybatis-plus的基础上,做好分库分表配置即可。
以下重点对配置进行介绍
使用shardingjdbc进行分库分表,那么首先我们需要告诉它我们有哪几个数据源,然后按照什么规则进行分库和分表。

1. 项目配置

# 同一个主机,两个库,演示分库分表
# 数据源 db0,db1
sharding.jdbc.datasource.names=db0,db1
# 第一个数据库
sharding.jdbc.datasource.db0.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.db0.driver-class-name=com.mysql.cj.jdbc.Driver
sharding.jdbc.datasource.db0.jdbc-url=jdbc:mysql://192.168.99.105:3306/ecommerce1?useUnicode=true&characterEncoding=utf-8
sharding.jdbc.datasource.db0.username=root
sharding.jdbc.datasource.db0.password=masterpwd

# 第二个数据库
sharding.jdbc.datasource.db1.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
sharding.jdbc.datasource.db1.jdbc-url=jdbc:mysql://192.168.99.105:3306/ecommerce2?useUnicode=true&characterEncoding=utf-8
sharding.jdbc.datasource.db1.username=root
sharding.jdbc.datasource.db1.password=masterpwd


# user表和item表 分库不分表
#user表
# 分库策略
sharding.jdbc.config.sharding.tables.t_user.database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_user.database-strategy.inline.algorithm-expression=db$->{id%2}
sharding.jdbc.config.sharding.tables.t_user.actual-data-nodes=db$->{0..1}.t_user
# 配置主键生成策略
sharding.jdbc.config.sharding.tables.t_user.key-generator.column=id
sharding.jdbc.config.sharding.tables.t_user.key-generator.type=SNOWFLAKE

#item表
# 分库策略
sharding.jdbc.config.sharding.tables.t_item.database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_item.database-strategy.inline.algorithm-expression=db$->{id%2}
sharding.jdbc.config.sharding.tables.t_item.actual-data-nodes=db$->{0..1}.t_item
# 配置主键生成策略
sharding.jdbc.config.sharding.tables.t_item.key-generator.column=id
sharding.jdbc.config.sharding.tables.t_item.key-generator.type=SNOWFLAKE

#order表
# 分库策略
sharding.jdbc.config.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id
sharding.jdbc.config.sharding.tables.t_order.database-strategy.inline.algorithm-expression=db$->{user_id%2}
#分表策略
sharding.jdbc.config.sharding.tables.t_order.actual-data-nodes=db$->{0..1}.t_order_$->{0..15}
sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_$->{id%16}
# 配置主键生成策略
sharding.jdbc.config.sharding.tables.t_order.key-generator.column=id
sharding.jdbc.config.sharding.tables.t_order.key-generator.type=SNOWFLAKE


# 打印执行的数据库以及语句
sharding.jdbc.config.props.sql.show=true
spring.main.allow-bean-definition-overriding=true
# 绑定表,若不配置,两表关联查询会使用笛卡尔积的方式查询
sharding.binding-tables[0]=t_user,t_order

2. 配置说明

2.1 概念说明
  • 数据节点
    数据库+表构成数据所在的存储位置。
  • 逻辑表名
    代码中用到的表名,可以理解为各个分表的抽象。t_order
  • 实际表名
    数据库里具体的表名,t_order_0,t_order_1....

下面这条配置中,逻辑表名是t_order,数据节点是db->{0..1}.t_order_->{0..15}。
代码中如果使用select * from t_order进行查询,shardingjdbc会按照分片键和分片算法找到具体的表民,比如db0数据库的t_order_1表。

sharding.jdbc.config.sharding.tables.t_order.actual-data-nodes=db$->{0..1}.t_order_$->{0..15}
2.2 如何配置多数据源

告诉了shardingjdbc,我们将把表分几个数据库,本工程分2个数据库。
sharding.jdbc.datasource.names 为数据库申明几个别名
可以看到在配置同步数据源时,和jdbc数据源的配置非常相似,所不同的是多了个shardingjdbc前缀,中间加了个数据库别名


image.png
2.3 如何分库分表

本工程采用inline行表达式分片算法进行分库分表,使用简单的groovy表达式,就可以告诉shardingjdbc我们按照什么规则进行分库,通俗说就是当我们要插入一条数据,shardingjdbc根据我们配置的键和规则,将这条数据插入到指定库和表中。
如下图,第一部分的分库配置,指定了采用inline分片策略,分片算法表达式是db$->{0..1}.t_order_$->{0..15},对t_order表的user_id进行取模,根据结果将sql路由到db0或db1。
同理,第二部分的分表配置,分表策略,指定了采用inline分片策略,分片算法表达式是t_order_$->{id%16},对t_order表的user_id进行取模,根据结果将sql路由到t_order_0、t_order_1...t_order_15。
你会发现上下两段配置极其相似,差别就是一个是database-strategy,table-strategy。可见shardingjdbc的设计是多么统一。

image.png

# 分库策略
sharding.jdbc.config.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id
sharding.jdbc.config.sharding.tables.t_order.database-strategy.inline.algorithm-expression=db$->{user_id%2}
#分表策略
sharding.jdbc.config.sharding.tables.t_order.actual-data-nodes=db$->{0..1}.t_order_$->{0..15}
sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_$->{id%16}

2.4 如何设置主键生成方式

配置好逻辑表名和主键名

sharding.jdbc.config.sharding.tables.t_order.key-generator.column=id
sharding.jdbc.config.sharding.tables.t_order.key-generator.type=SNOWFLAKE
2.5 分库不分表

t_item和t_user表就是分库不分表
以t_item表为例,相对于t_order分库分表,我们只需要配置分库,然后注意数据节点配置中的groovy表达式写法,因为不分表,所以每个表的表名都是一样的,即t_item。

image.png

五、演示

5.1 演示写

  • 代码
    自动生成的主键1370385793309491202,按照分库分表配置的分片算法(db$->{user_id%2},t_order_$->{id%16}),对2取模为0,所以选择了db0库,对16取模为2,所以走了t_order_2表
 @Test
    public void saveOrder(){
        Order order = new Order(null,"orderNo",1370295535473528834L,1370301749800476674L,12.0,1,System.currentTimeMillis(), System.currentTimeMillis());
        boolean success = orderService.save(order);
        System.out.println(success);
    }
  • 执行结果
2021-03-12 22:46:48.703  INFO 4455 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: INSERT INTO t_order_2  ( id,
order_no,
user_id,
item_id,
order_price,
status,
create_time,
update_time )  VALUES  ( ?,
?,
?,
?,
?,
?,
?,
? ) ::: [[1370385793309491202, orderNo, 1370295535473528834, 1370301749800476674, 12.0, 1, 1615560408154, 1615560408154]]
true

5.2 演示读

  • 代码
 @Test
    public void get(){
        //查询条件只有order的主键,走两个库,然后根据分片策略查t_order_2表
        Order order = orderService.getById(1370304180772954114L);
        System.out.println(order);

        //查询条件有user_id,和id,先根据user_id选择db0,再根据id选择t_order_2表
        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",1370304180772954114L);
        queryWrapper.eq("user_id",1370295535473528834L);
        Order order2 = orderService.getOne(queryWrapper);
        System.out.println(order2);
    }
  • 执行结果
    t_order表是按照user_id进行分库的,当没有设置user_id条件,两个库都查,然后按照配置的分片算法(t_order_$->{id%16}),主键1370304180772954114对16取模为2,走了t_order_2。
    当指定了user_id,根据分库的分片算法(db$->{user_id%2}), 1370295535473528834对2取模,选择了db0库,然后进一步选择了t_order_2表
2021-03-12 22:49:26.170  INFO 4566 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: SELECT id,order_no,user_id,item_id,order_price,status,create_time,update_time FROM t_order_2 WHERE id=?  ::: [[1370304180772954114]]
2021-03-12 22:49:26.170  INFO 4566 --- [           main] ShardingSphere-SQL                       : Actual SQL: db1 ::: SELECT id,order_no,user_id,item_id,order_price,status,create_time,update_time FROM t_order_2 WHERE id=?  ::: [[1370304180772954114]]
Order(id=1370304180772954114, orderNo=orderNo, userId=1370295535473528834, itemId=1370301749800476674, orderPrice=12.0, status=1, createTime=1615540950252, updateTime=1615540950252)
2021-03-12 22:49:28.259  INFO 4566 --- [           main] ShardingSphere-SQL                       : Rule Type: sharding
2021-03-12 22:49:28.259  INFO 4566 --- [           main] ShardingSphere-SQL                       : Logic SQL: SELECT  id,order_no,user_id,item_id,order_price,status,create_time,update_time  FROM t_order 
 
 WHERE (id = ? AND user_id = ?)
2021-03-12 22:49:28.259  INFO 4566 --- [           main] ShardingSphere-SQL                       : SQLStatement: SelectStatement(super=DQLStatement(super=io.shardingsphere.core.parsing.parser.sql.dql.select.SelectStatement@205159dc), containStar=false, firstSelectItemStartPosition=8, selectListLastPosition=79, groupByLastPosition=0, items=[CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=order_no, alias=Optional.absent()), CommonSelectItem(expression=user_id, alias=Optional.absent()), CommonSelectItem(expression=item_id, alias=Optional.absent()), CommonSelectItem(expression=order_price, alias=Optional.absent()), CommonSelectItem(expression=status, alias=Optional.absent()), CommonSelectItem(expression=create_time, alias=Optional.absent()), CommonSelectItem(expression=update_time, alias=Optional.absent())], groupByItems=[], orderByItems=[], limit=null, subQueryStatement=null, subQueryStatements=[], subQueryConditions=[])
2021-03-12 22:49:28.259  INFO 4566 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: SELECT  id,order_no,user_id,item_id,order_price,status,create_time,update_time  FROM t_order_2 
 
 WHERE (id = ? AND user_id = ?) ::: [[1370304180772954114, 1370295535473528834]]
Order(id=1370304180772954114, orderNo=orderNo, userId=1370295535473528834, itemId=1370301749800476674, orderPrice=12.0, status=1, createTime=1615540950252, updateTime=1615540950252)

六、代码地址

https://github.com/xushengjun/JAVA-01/tree/main/Week_08/day1/homework2/ecommerce-shardingjdbc

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

推荐阅读更多精彩内容