shardingsphere的学习(一):shardingsphere的基本概念和水平分表例子

简介

关于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;
    @Test
    public 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);
    }
}
查看数据库

在这里插入图片描述

水平分库例子

有两个数据库edu_db_1,edu_db_2,两个数据库中都分表有course_1和course_2表。插入数据时,当userid为偶数时添加到edu_db_1中,当userid为奇数时添加到edu_db_2中;当cid为偶数时,添加到course_1表,当cid为奇数时,添加到course_2表。
在原来的springboot项目上修改

创建数据库和表

在这里插入图片描述
sql语句:

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;
写配置文件
# shardingjdbc分片策略
# 配置数据源,给数据源起名称,
# 水平分库,配置两个数据源
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=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://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=1234556

#指定数据库分布情况,数据库里面表分布情况
# 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.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
测试类
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;
    //水平分库
    @Test
    public void add1() {
        course c = new course();
        c.setCname("mike");
        c.setUserId(3L);
        c.setCstatus("good");
        courseMapper.insert(c);
    }
    @Test
    public void select(){
        QueryWrapper<course> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("cid",1024380349527883777l);
        course c = courseMapper.selectOne(queryWrapper);
        System.out.println(c);
    }
}

add的结果
在这里插入图片描述
在这里插入图片描述

垂直分库

专库专用,插入,查询用户信息的时候会操作user_db数据库中的t_user表。

添加数据库

创建user_db数据库,里面创建t_user表
在这里插入图片描述

CREATE TABLE `t_user` (
  `user_id` bigint NOT NULL,
  `username` varchar(100) NOT NULL,
  `ustatus` varchar(50) NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
实体类user
package com.christ.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName(value = "t_user")  
public class user {
    private Long userId;
    private String username;
    private String ustatus;
}
mapper接口UserMapper
package com.christ.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.christ.entity.user;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends BaseMapper<user> {
}

配置文件application.properties
# 配置数据源,给数据源起名称,
spring.shardingsphere.datasource.names=m1,m2,m0

# 一个实体类对应两张表,覆盖
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=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://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456

#配置第三个数据源具体内容,包含连接池,驱动,地址,用户名和密码
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=123456

# 配置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

# 指定表分片策略  约定cid值偶数添加到course_1表,如果cid是奇数添加到course_2表
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


#指定数据库分布情况,数据库里面表分布情况
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.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
测试类DemoApplicationTests
package com.christ;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.christ.entity.user;
import com.christ.mapper.CourseMapper;
import com.christ.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
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;
@Resource
    private UserMapper userMapper;
//垂直分库
    @Test
public void add(){
    user u = new user();
    u.setUsername("john");
    u.setUstatus("good");
    userMapper.insert(u);
}
@Test
    public void find(){
        QueryWrapper<user> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id",1024403036157509633l);
    user user1 = userMapper.selectOne(queryWrapper);
    System.out.println(user1);
}
}
运行结果

在这里插入图片描述
在这里插入图片描述

公共表

用于存储一些变化很少,查询时候需要经常关联的数据的表,类似于数据字典,需要在每个数据库中都创建出相同结构公共表。

公共表例子
添加表

在edu_1,edu_2,user_db三个数据库中都创建t_udict表。

CREATE TABLE `t_udict` (
  `dictid` bigint NOT NULL,
  `ustatus` varchar(100) NOT NULL,
  `uvalue` varchar(100) NOT NULL,
  PRIMARY KEY (`dictid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
修改配置文件

在配置文件application.properties中,添加上:

# 配置公共表
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
编写实体类
@Data
@TableName(value = "t_udict")
public class udict {
    private Long dictid;
    private String ustatus;
    private String uvalue;
}
编写mapper接口
@Repository
public interface UdictMapper extends BaseMapper<udict> {
}
测试
package com.christ;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.christ.entity.udict;
import com.christ.entity.user;
import com.christ.mapper.CourseMapper;
import com.christ.mapper.UdictMapper;
import com.christ.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
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;
@Resource
    private UserMapper userMapper;


@Resource
private UdictMapper udictMapper;
//公共表
    @Test
    public void add(){
        udict u = new udict();
        u.setUstatus("1");
        u.setUvalue("good");
        udictMapper.insert(u);
    }
    @Test
public void delete(){
QueryWrapper<udict> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("dictid",1024461354494328833l);
udictMapper.delete(queryWrapper);
}
}

运行结果:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值