一套开源的分布式数据库中间件解决方案
有三个产品:Sharding-JDBC 和 Sharding-Proxy
定位为关系型数据库中间件,合理在分布式环境下使用关系型数据库操作
分库分表有两种方式:垂直切分和水平切分
垂直切分:垂直分表和垂直分库(表结构不同)
水平切分:水平分表和水平分库 (表结构相同)
垂直分表: 根据业务规则,或访问频次、是否大字段,将常用字段与不常用字段为为N个表
垂直分库: 根据业务规则,将不同模块的表分别放在不同数据库,库可以分布在不同服务器
水平分表: 根据表数据规则,将一个表得数据根据规则进行拆分成多个表(比如:分区,轮询,取模)
水平分库:根据表数据规则,将一个表的数据分到不同的库中,每个库只有这个表的部分数据
(1)在数据库设计时候考虑垂直分库和垂直分表
(2)随着数据库数据量增加,不要马上考虑做水平切分,首先考虑缓存处理,读写分离,使用索引等等方式,如果这些方式不能根本解决问题了,再考虑做水平分库和水平分表
(1)跨节点连接查询问题(分页、排序)
(2)多数据源管理问题
1)是轻量级的 java 框架,是增强版的 JDBC 驱动
2)主要目的是:简化对分库分表之后数据相关操作

<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.20version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.apache.shardingspheregroupId>
<artifactId>sharding-jdbc-spring-boot-starterartifactId>
<version>4.0.0-RC1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.0.5version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
记得引入spring-boot-starter-web依赖,否则项目启动成功后会自动关闭。
@SpringBootApplication
@MapperScan(basePackages = "com.atguigu.springcloud.mapper")
public class ShardingJDBCApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingJDBCApplication.class,args);
}
}
@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}
@Data
public class Course {
private String cstatus;
private String cname;
private Long cid;
private Long userId;
}
注意:测试类的包路径要与启动类一致,否则无法注入bean
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingjdbcdemoApplicationTests {
}

# shardingjdbc 分片策略
# 配置数据源,给数据源起名称
spring.shardingsphere.datasource.names=m1
# 允许覆盖重名bean
spring.main.allow-bean-definition-overriding=true
#配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
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:3306/course_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#指定 course 表分布情况,配置表在哪个数据库里面,表名称都是什么 m1.course_1 ,m1.course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}
# 指定 course 表里面主键 cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 指定分片策略 约定 cid 值偶数添加到 course_1 表,如果 cid 是奇数添加到 course_2表
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding_column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm_expression=course_$->{cid % 2 + 1}
# 打开 sql 输出日志
spring.shardingsphere.props.sql.show=true
注意:mysql 5.8 版本驱动类与之前不同。我使用的是5.7的mysql.Driver的实现类的全路径名是com.mysql.jdbc.Driver。
允许覆盖重名bean:dataSource这个bean已存在,允许覆盖。
spring.main.allow-bean-definition-overriding=true
cid % 2 + 1:为何+1?
+1的原因:如果下标从0开始就不用加,现在下标是从1开始,所以得加。本来下标0代表偶数,+1后下标1代表偶数
1)创建数据库 course_db
2)在数据库创建两张表 course_1 和 course_2
3)约定规则:如果添加课程 id 是偶数把数据添加 course_1,如果奇数添加到 course_2
//注入 mapper
@Resource
private CourseMapper courseMapper;
//添加课程的方法
@Test
public void addCourse() {
for (int i = 1; i <= 10; i++) {
Course course = new Course();
course.setCname("java" + i);
course.setUserId(100L);
course.setCstatus("Normal" + i);
courseMapper.insert(course);
}
}
//查询课程的方法
@Test
public void findCourse() {
QueryWrapper<Course> wrapper = new QueryWrapper<>();
wrapper.eq("cid", 763102447882207233L);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
2表cid全是奇数

# shardingjdbc 分片策略
# 配置数据源,给数据源起名称
spring.shardingsphere.datasource.names=m1,m2
# 允许覆盖重名bean
spring.main.allow-bean-definition-overriding=true
#配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
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:3306/edu_db_1?useUnicode=true&characterEncoding=utf-8&useSSL=false
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:3306/edu_db_2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root
#指定数据库分布情况,数据库里面表分布情况# m1 m2 course_1 course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
# 指定 course 表里面主键 cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 指定表分片策略 约定 cid 值偶数添加到 course_1 表,如果 cid 是奇数添加到course_2 表
# +1的原因:如果下标从0开始就不用加,现在下标是从1开始,所以得加。本来下标0代表偶数,+1后下标1代表偶数
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding_column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm_expression=course_$->{cid % 2 + 1}
# 指定数据库分片策略 约定 user_id 是偶数添加 m1,是奇数添加 m2
spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding_column=user_id
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm_expression=m$->{user_id % 2 + 1}
# 打开 sql 输出日志
spring.shardingsphere.props.sql.show=true

@Test
public void addCourseDb() {
Course course = new Course();
course.setCname("javademo1");
//分库根据 user_id
course.setUserId(111L);
course.setCstatus("Normal1");
courseMapper.insert(course);
}
//查询操作
@Test
public void findCourseDb() {
QueryWrapper<Course> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("user_id", 111L);
//设置 cid 值
wrapper.eq("cid", 763127952517890049L);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
cid与user_id都是奇数,2库的2表

# shardingjdbc 分片策略
# 配置数据源,给数据源起名称
spring.shardingsphere.datasource.names=m1,m2,m0
# 允许覆盖重名bean
spring.main.allow-bean-definition-overriding=true
#配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
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:3306/edu_db_1?useUnicode=true&characterEncoding=utf-8&useSSL=false
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:3306/edu_db_2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root
#配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
# 配置 user_db 数据库里面 t_user 专库专表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
# 指定 course 表里面主键 cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding_column=user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm_expression=t_user
# 打开 sql 输出日志
spring.shardingsphere.props.sql.show=true
@Test
public void addUserDb() {
User user = new User();
user.setUsername("lucy");
user.setUstatus("a");
userMapper.insert(user);
}

其实,我觉着垂直分库和分表不用刻意指定。因为垂直分库是把表分不到不同的库,垂直分表是把一个表拆分成多个表。这在设计之初是可以预见的。
分别在user_db、edu_db_2、edu_db_1中执行。
CREATE TABLE `t_udict` (
`dict_id` BIGINT(20) NOT NULL,
`uvalue` VARCHAR(64) DEFAULT NULL,
`ustatus` VARCHAR(32) DEFAULT NULL,
PRIMARY KEY (`dict_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
@Data
@TableName("t_udict")
public class Udict {
private Long dictid;
private String ustatus;
private String uvalue;
}
@Mapper
public interface UdictMapper extends BaseMapper<Udict> {
}
spring.shardingsphere.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column=dict_id
spring.shardingsphere.sharding.tables.t_udict.key-generator.type=SNOWFLAKE
@Test
public void addDict() {
Udict udict = new Udict();
udict.setUstatus("a");
udict.setUvalue("已启用");
udictMapper.insert(udict);
}
//删除操作
@Test
public void deleteDict() {
QueryWrapper<Udict> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("dict_id", 763535578640678913L);
udictMapper.delete(wrapper);
}
