SpringBoot + ShardingSphere实现读写分离,分库分表

分布式项目在通过一定的服务拆分,项目运行一段时间以后,随着用户和使用资源的不断增大,数据库的性能也会面临瓶颈。单表数据量过大,超高并发的读写引发的锁问题,导致系统运行加载越来越慢,不时出现系统假死的现象,数据库的优化已将近极限,几乎没有空间,这时就需要考虑读写分离,分库分表(这里主要是水平的分库分表)的策略,来减少对同一数据库的访问的压力。

一.操作的工程目录

二.读写分离

application.yml

​
spring:
    shardingsphere:
        datasource:
            names: master,slave
            #数据源配置
            master:
                driver-class-name: com.mysql.jdbc.Driver
                type: com.alibaba.druid.pool.DruidDataSource
                url: jdbc:mysql://localhost:3306/master0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            slave:
                driver-class-name: com.mysql.jdbc.Driver
                type: com.alibaba.druid.pool.DruidDataSource
                url: jdbc:mysql://localhost:3306/slave0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456

        #读写分离配置(主库写,从库读)
        masterslave:
            load-balance-algorithm-type: round_robin
            name: ms
            #写库
            master-data-source-name: master
            #读库
            slave-data-source-names: slave
    props:
        sql:
            show: true
    main:
        allow-bean-definition-overriding: true

2.1 代码生成的测试

​
package com.aliyun.readWrite.test.controller;


import com.aliyun.readWrite.test.entity.VideoRoom;
import com.aliyun.readWrite.test.service.IVideoRoomService;
import com.aliyun.util.Result;
import com.aliyun.util.StatusCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
* <p>
    * 房间表 前端控制器
    * </p>
*
* @author haige
* @since 2021-11-24
*/

@Slf4j
@RestController
@RequestMapping("/videoRoom")
public class VideoRoomController {

    @Autowired
    public IVideoRoomService videoRoomService;

    @PostMapping("/save")
    public Result save(@RequestBody VideoRoom videoRoom){
        videoRoomService.save(videoRoom);
        return new Result(StatusCode.SUCCESS,"保存成功");
    }

    @GetMapping("/get/{id}")
    public Result get(@PathVariable("id") String id){
        VideoRoom videoRoom = videoRoomService.getById(id);
        return new Result(StatusCode.SUCCESS,"查询成功",videoRoom);
    }

}

​

实现了接口调用时master库写,slave库读的效果

三.分库分表

application.yml

spring:
    shardingsphere:
        props:
            sql:
                show: true
        main:
            allow-bean-definition-overriding: true
        datasource:
            names: master0,master1
            #数据源配置
            master0:
                driver-class-name: com.mysql.jdbc.Driver
                type: com.alibaba.druid.pool.DruidDataSource
                url: jdbc:mysql://localhost:3306/master0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            master1:
                driver-class-name: com.mysql.jdbc.Driver
                type: com.alibaba.druid.pool.DruidDataSource
                url: jdbc:mysql://localhost:3306/master1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456

        #分库分表配置
        sharding:
            #分库
            default-database-strategy:
                inline:
                    sharding-column:  age
                    algorithm-expression: master$->{age % 2}      #根据年龄分库
            #分表
            tables:
                tab_user:
                    actual-data-nodes: master$->{0..1}.tab_user$->{0..1}
                    table-strategy:
                        inline:
                            sharding-column: id
                            algorithm-expression: tab_user$->{id % 2}

3.1 分库分表数据源及策略配置的注入类

package com.aliyun.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
@ComponentScan(basePackageClasses = DataSourceConfig.class)
@MapperScan(basePackages = "com.aliyun.shard.mapper")
public class DataSourceConfig {

    /**
     * SqlSessionFactory注入
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setFailFast(true);
        sessionFactory.setMapperLocations(resolver.getResources("classpath:/mapper/system/*Mapper.xml"));
        return sessionFactory.getObject();
    }

    @Bean
    public DataSource dataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getBindingTableGroups().add("tab_user");
//        shardingRuleConfig.getBroadcastTables().add("t_config");
        //TODO 根据年龄分库 一共分为2个库
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("age", "master${age % 2}"));
        //TODO 根据ID分表  一共分为2张表
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("id", new PreciseModuloShardingTableAlgorithm()));
        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new Properties());
    }

    private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() {
        KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "id");
        return result;
    }

    TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration result = new TableRuleConfiguration("tab_user", "master${0..1}.tab_user${0..1}");
        result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());
        return result;
    }


    Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> result = new HashMap<>();
        result.put("master0", DataSourceUtil.createDataSource("master0"));
        result.put("master1", DataSourceUtil.createDataSource("master1"));
        return result;
    }
}
package com.aliyun.config;

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.util.Collection;

/**
 * 表分片规则
 */
public final class PreciseModuloShardingTableAlgorithm implements PreciseShardingAlgorithm<Long> {

    @Override
    public String doSharding(final Collection<String> tableNames, final PreciseShardingValue<Long> shardingValue) {
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }
}
package com.aliyun.config;

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;

/**
 * @Description: 数据源配置
 *
 * @author xub
 * @date 2019/10/16 下午7:47
 */
public final class DataSourceUtil {

    private static final String HOST = "localhost";

    private static final int PORT = 3306;

    private static final String USER_NAME = "root";

    private static final String PASSWORD = "123456";

    public static DataSource createDataSource(final String dataSourceName) {
        DruidDataSource result = new DruidDataSource();
        result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
        result.setUrl(String.format("jdbc:mysql://%s:%s/%s?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8", HOST, PORT, dataSourceName));
        result.setUsername(USER_NAME);
        result.setPassword(PASSWORD);
        return result;
    }
}

3.2 代码生成的测试

package com.aliyun.shard.controller;

import com.aliyun.shard.entity.TabUser;
import com.aliyun.shard.service.ITabUserService;
import com.aliyun.util.Result;
import com.aliyun.util.StatusCode;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.List;

/**
* <p>
    *  前端控制器
    * </p>
*
* @author haige
* @since 2021-11-26
*/

@Slf4j
@RestController
@RequestMapping("/tabUser")
public class TabUserController {

    @Autowired
    public ITabUserService tabUserService;

    /**
     * 模拟插入数据
     */
    List<TabUser> userList = Lists.newArrayList();

    /**
     * 初始化插入数据
     */
    @PostConstruct
    private void getData() {
        userList.add(new TabUser(1L,"小小", "女", 3));
        userList.add(new TabUser(2L,"爸爸", "男", 30));
        userList.add(new TabUser(3L,"妈妈", "女", 28));
        userList.add(new TabUser(4L,"爷爷", "男", 64));
        userList.add(new TabUser(5L,"奶奶", "女", 62));
    }

    @PostMapping("batchSaveUser")
    public Result batchSaveUser() {
        return new Result(StatusCode.SUCCESS,tabUserService.insertForEach(userList));
    }

    @GetMapping("/findAll")
    public Result findAll(){
        List<TabUser> tabUserList = tabUserService.findAll();
        return new Result(StatusCode.SUCCESS,"查询成功",tabUserList);
    }

}

实现了5条数据分别添加到了tab_user0,tab_user1,tab_user2三张表,在查询findAll数据时又能返回三张表的所有数据。

四.工程源码

感兴趣的猿友可以了解--> 下载源码

好的,我可以为您解答关于SpringBootShardingSphere实现读写分离的问题。 首先,您需要在pom.xml文件中添加ShardingSphere相关依赖: ```xml <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-core</artifactId> <version>${sharding-sphere.version}</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>${sharding-sphere.version}</version> </dependency> ``` 其中${sharding-sphere.version}为ShardingSphere的版本号。 接下来,您需要在application.yml或application.properties中配置ShardingSphere的数据源和规则,例如: ```yaml spring: shardingsphere: datasource: names: ds_master, ds_slave_0, ds_slave_1 ds_master: url: jdbc:mysql://localhost:3306/mydb_master?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root ds_slave_0: url: jdbc:mysql://localhost:3306/mydb_slave_0?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root ds_slave_1: url: jdbc:mysql://localhost:3306/mydb_slave_1?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 username: root password: root sharding: default-data-source-name: ds_master master-slave-rules: ds_0: master-data-source-name: ds_master slave-data-source-names: ds_slave_0, ds_slave_1 load-balance-algorithm-type: round_robin ``` 以上配置中,我们配置了3个数据源:ds_master, ds_slave_0和ds_slave_1,其中ds_master为主库,ds_slave_0和ds_slave_1为从库。然后我们使用了ShardingSphere提供的master-slave规则将ds_master和ds_slave_0、ds_slave_1进行了关联,并使用了轮询算法进行负载均衡,从而实现读写分离。 最后,您需要在SpringBoot主类上添加@EnableSharding注解,以启用ShardingSphere的功能。 这就是使用SpringBootShardingSphere实现读写分离的基本步骤。希望对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇潇雨歇_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值