前面已经介绍过,水平分表是在同一个数据内,把同一个表的数据按一定规则拆到多个表中。在 sharding-jdbc 快速入门里,我们已经对水平分表进行实现,这里不再重复介绍。
前面已经介绍过,水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。接下来看一下如何使用 Sharding-JDBC 实现水平分库,接下来继续对 sharding-jdbc 快速入门 中的例子进行完善。
将原有的 order_db 库拆分为 order_db_1、order_db_2
由于数据库拆分了两个,这里需要配置两个数据源。
分库需要配置分库的策略,和分表策略的意义类似,通过分库策略实现数据操作针对分库的数据库进行操作。
server.port = 9095
server.servlet.context-path = /sharding-jdbc
spring.application.name = sharding-jdbc
spring.http.encoding.charset = utf-8
spring.http.encoding.enabled = true
spring.http.encoding.force = true
# 针对bean被重复定义,重复则覆盖
spring.main.allow-bean-definition-overriding = true
# 和数据库字段进行印射,最终成为驼峰命名
mybatis.configuration.map-underscore-to-canel-case = true
## Sharding-jdbc 分片规则配置
# 定义多个数据源
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.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://127.0.0.1:3306/order_db_1?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 123456
spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://127.0.0.1:3306/order_db_2?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 123456
# 分库策略,以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,某则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}
# 指定t_order表的数据分布情况配置数据节点
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..2}
# 指定t_order表的主键生成策略为SNOWFLAKE(雪花算法,实现全局主键自增)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE
# 指定t_order表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show = true
分库策略定义方式如下:
# 分库策略,如何将一个逻辑表印射到多个数据源
spring.shardingsphere.sharding.tables.<逻辑表名称>.database-strategy.<分片策略>.<分片策略属性名> = #分片策略属性值
# 分表策略,如何将一个逻辑表映射为多个实际表
spring.shardingsphere.sharding.tables.<逻辑表名称>.table-strategy.<分片策略>.<分片策略属性名> = #分片策略属性值
不管分库还是分表,策略基本一样。有以下集中分片策略:
本例中使用的是 inline 分片策略,其他请查阅官方文档:https://shardingsphere.apache.org/document/current/cn/features/sharding/concept/sharding/
/*
SQLyog Ultimate v12.08 (32 bit)
MySQL - 8.0.11 : Database - order_db
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`order_db_1` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `order_db_1`;
/*Table structure for table `t_order_1` */
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) NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表1';
/*Data for the table `t_order_1` */
/*Table structure for table `t_order_2` */
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) NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表2';
/*Data for the table `t_order_2` */
CREATE DATABASE /*!32312 IF NOT EXISTS*/`order_db_2` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `order_db_2`;
/*Table structure for table `t_order_1` */
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) NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表1';
/*Data for the table `t_order_1` */
/*Table structure for table `t_order_2` */
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) NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表2';
/*Data for the table `t_order_2` */
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
因为这是在 Sharding-JDBC 快速入门中的项目进行测试,如果没看,请移步到 Sharding-JDBC 快速入门。
从结果上可以看出,因为插入的是 user_id = 1L,因此 Sharding-JDBC 通过水平分库策略,定位到 m2(t_order_2)数据库,然后再通过水平分表策略,定位到具体的表中去;如果插入的是 user_id = 2L,则会定位到 m1 中。
@Test
public void testSelect() {
List orderIds = new ArrayList<>();
orderIds.add(393345721874513920l);
orderIds.add(393783366735888385L);
List
查询结果
从结果上,生成了 4 条 SQL ,然而和我们预期有点不一样。为什么?不应该是定位到某一个数据库中吗?
其实不然,因为我们分库策略中是以 user_id 作为分片键,而我们查询时并没有传入 user_id,因此 Sharding-JDBC 不知道具体数据是在哪个库中,所以会使用广播路由进行全库查询。要想指定库查询,则需要传入 user_id.
新增 mapper 接口
/**
* 根据id查询订单
* @param orderIds id集合
* @return
*/
@Select("")
List
/**
* 通过订单id和用户id查询
*/
@Test
public void testSelectByOrderIdsAndUserId() {
List orderIds = new ArrayList<>();
orderIds.add(393345721874513920l);
orderIds.add(393783366735888385L);
List