分库别表主要有两种解决方案:垂直拆分、水平拆分
垂直拆分
操作数据库中某张表,把这张表中一部分字段数据存到一张新表里面,再把这张表另一 部分字段数据存到另外一张表里面 例如:将商品基本信息放在一张表,商品详情放在一张表
水平拆分
将一个数据库,拆分为两个相同结构的数据库,存储数据
存储方式
方式一:按id取模方式实现,缺点:不好扩容
方式二:按id分片实现,缺点:因为id连续,热点数据可能会集中到一个数据库
其他思路:大范围按时间分,小范围取模分
分库分表可能出现的问题:
事务一致性问题:单表操作事务执行失败可以回滚,分表之后可能会插入到多条数据库
跨节点关联查询:当查询固定范围的数据也在多个数据库中
主键避重问题:数据库表主键冲突
所以在开发中:数据增加不要立马做水平拆分,首先考虑缓存处理,读写分离,使用索引等
ShardingSphere
开源分布式数据库中间件解决方案,主要有Sharding-JDBC和Sharding-Proxy
客户端分库分表:以jar包形式放在java应用里,通过Sharding-JDBC去找对应数据,主要作用数据分片,读写分离
服务端分库分表:利用Sharding-Proxy伪装成数据库,分库分表由Sharding-Proxy实现
Sharding-JDBC实现水平分表
创建数据库,按水平分表方式
在数据库创建两张表 course_1 和 course_2
约定规则:如果添加课程 id 是偶数把数据添加 course_1,如果奇数添加到 course_2
创建springboot项目,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
实体类
@Data
public class Course {
private Long cid;
private String cname;
private Long userId;
private String cstatus;
}
mapper层
@Repository
public interface CourseMapper extends BaseMapper<Course> {
}
启动类加注解
@MapperScan("com.lzq.mapper")
配置 Sharding-JDBC 分片策略
在配置文件中配置
# shardingjdbc 分片策略
# 配置数据源,给数据源起名称
spring.shardingsphere.datasource.names=m1
# 一个实体类对应两张表,覆盖
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.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/sharding?serverTimezone=GMT%2B8
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
测试
@SpringBootTest
class ShardingJdbcApplicationTests {
@Autowired
private CourseMapper courseMapper;
@Test
void add() {
Course course = new Course();
course.setCname("java");
course.setUserId(10l);
course.setCstatus("normal");
courseMapper.insert(course);
}
@Test
public void find(){
QueryWrapper<Course> wrapper = new QueryWrapper<>();
wrapper.eq("cid",714887289653690369l);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
}
springboot版本不同可能会报错,由于一个实体类对应两个数据库,在配置文件添加
spring.main.allow-bean-definition-overriding=true
水平分库
创建两个数据库,每个数据库里创建两个表分别为course_1 和 course_2
约定分片规则:userid为偶数加入第一个数据库,cid为偶数放入course_1表
修改之前代码:配置分片规则,在配置文件中加入
# 配置数据源,给数据源起名称,
# 水平分库,配置两个数据源
spring.shardingsphere.datasource.names=m1,m2
# 一个实体类对应两张表,覆盖
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.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
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.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
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 表
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.default-database-strategy.inline.sharding.column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm.expression=m$->{user_id % 2 + 1}
#写法二 对具体表有固定规则
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}
测试同上
垂直分库:
专库专用,查询用户信息时去用户数据库查用户表
创建数据库user_db,创建用户信息表t_user
创建实体类与mapper
@Data
@TableName(value = "t_user") //指定对应表
public class User {
private Long userId;
private String username;
private String ustatus;
}
@Repository
public interface UserMapper extends BaseMapper<User> {
}
配置文件添加
# 配置数据源,给数据源起名称
spring.shardingsphere.datasource.names=m1,m2,m0
.....
#配置第三个数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8
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.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column=dictid
spring.shardingsphere.sharding.tables.t_udict.key-generator.type=SNOWFLAKE
当有修改操作时,会向多个数据库中t_udict表进行修改