简介
关于shardingsphere的基本相关概念的学习和使用shardingjdbc进行水平分表的例子
shardingsphere
是一套开源的分布式关系型数据库中间件解决方案,由sharding-jdbc,sharding-proxy,sharding-sidecar三个项目组成。
定位为关系型数据库中间件,合理在分布式环境下使用关系型数据库操作。
分库分表
随着时间和业务的发展,表中的数据会越来越多,这时,对数据库表进行crud操作的时候,会有性能问题。
解决方案:
- 从硬件上
- 分库分表
分库分表,主要分为:垂直分库,水平分库,垂直分表,水平分表
垂直分表
将一张表按照字段分成多个表,每个表存储其中的一部分字段。
一般按照字段的访问频率高低来区分
- 将热门字段,冷门字段分别存到不同的表中
- 把text,blob等大字段拆分出来放到附表中
- 经常组合查询的列放到一张表中
好处:提高热门数据的操作效率上,避免io的争抢。
垂直分库
按照业务将表进行分类,存储到不同的数据库中,每个库可以放到不同的服务器上。
好处:
- 降低业务层面的耦合,使得业务更清晰
- 方便对数据进行分级管理,维护,监控和扩展等
- 高并发场景下,可以提升io,数据库连接数,降低单机硬件资源的瓶颈
水平水库
把一个表的数据按照一定的规则拆分到不同的数据库中,每个库可以放到不同的服务器上。拆分出来的多张表结构是一样的。
好处:
- 解决了单库大数据,高并发的性能瓶颈
- 提高了系统的稳定性和可用性。一个库出问题,还有部分可用。
- io冲突减少,锁定减少
水平分表
如果水平分库太多,运维成本会增加,就可以水平分表。
水平分表:把一个表的数据按一定规则拆到多个表中。
好处:
- 解决单一表数据量过大而导致的性能问题
- 避免io争抢并减少锁表的概率
最佳实践
系统设计阶段就根据业务耦合松紧来确定垂直分库,垂直分表方案,在数据量不是很大的时候,首先考虑缓存,读写分离,索引等方案。当数据量很大而且持续增长的时候,考虑水平分库水平分表方案。
分库分表问题
- 跨节点连接查询问题(分页,排序)
- 多数据源管理问题
shardingjdbc
开源的分布式数据库中间件,是轻量级的Java框架,在jdbc层提供额外的服务,使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的jdbc驱动,完全兼容jdbc和各种orm框架。
核心功能
- 数据分片
- 读写分离
shardingjdbc做的不是分库分表,而是让我们更简单地去操作分库分表里的数据。
通过shardingjdbc可以透明的访问已经分库分表,读写分离的多个数据源,而不用关心数据源的数量以及数据如何分布。
程序员不用知道多少张表,多少个数据源,通过shardingjdbc就像操作单个数据源单个表一样去操作。
性能
对于单库单表来说,shardingjdbc的性能比jdbc低一点点,不超7%。
对于多库多表来说,shardingjdbc的性能比jdbc高2倍左右。
水平分表例子
在一个数据库中,有两张同样结构的表,插入数据的时候,偶数插入course_1表,奇数插入course_2表。
创建数据库和表
创建数据库
创建表一course_1
CREATE TABLE `course_1` (`cid` bigint NOT NULL,`cname` varchar(50) NOT NULL,`user_id` bigint NOT NULL,`cstatus` varchar(10) NOT NULL,PRIMARY KEY (`cid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
创建表二course_2
CREATE TABLE `course_2` (`cid` bigint NOT NULL,`cname` varchar(50) NOT NULL,`user_id` bigint NOT NULL,`cstatus` varchar(10) NOT NULL,PRIMARY KEY (`cid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
创建springboot项目
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atguigu</groupId><artifactId>shardingjdbcdemo</artifactId><version>0.0.1-SNAPSHOT</version><name>shardingjdbcdemo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><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></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
实体类course
package com.christ.entity;
import lombok.Data;
@Data
public class course {private Long cid;private String cname;private Long userId;private String cstatus;
}
mapper接口CourseMapper
package com.christ.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.christ.entity.course;
import org.springframework.stereotype.Repository;
@Repository
public interface CourseMapper extends BaseMapper<course> {
}
启动类DemoApplication
package com.christ;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.christ.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
配置文件application.properties
# 配置数据源,给数据源起名称
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/course_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456#指定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
解析:
course 是实际的数据库表名。
m1.course_$->{1…2} 是该表在数据库中的实际数据节点,也就是物理分片。
这种配置是用来描述数据库分片的策略,即将逻辑表 course 在物理上分散存储在不同的数据库节点中,每个节点对应一个具体的物理表。
所以,course 表不是虚拟表,而是一个真实存在的逻辑表,只是它的数据在物理上被分片存储在多个数据库节点中。
测试类
package com.christ;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.christ.entity.course;
import com.christ.mapper.CourseMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;@RunWith(SpringRunner.class)
@SpringBootTest
class DemoApplicationTests {
@Resource
private CourseMapper courseMapper;@Testpublic void add() {course c = new course();c.setCname("christ");c.setUserId(1L);c.setCstatus("good");courseMapper.insert(c);}@Test
public void select(){QueryWrapper<course> queryWrapper = new QueryWrapper<>();queryWrapper.eq("cid",1024200364422856705L);course c = courseMapper.selectOne(queryWrapper);System.out.println(c);}
}