分库别表主要有两种解决方案:垂直拆分、水平拆分

垂直拆分

操作数据库中某张表,把这张表中一部分字段数据存到一张新表里面,再把这张表另一 部分字段数据存到另外一张表里面 例如:将商品基本信息放在一张表,商品详情放在一张表

水平拆分

将一个数据库,拆分为两个相同结构的数据库,存储数据

存储方式

方式一:按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表进行修改

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐