Apache ShardingSphere分表的简单使用和常见问题

news2024/9/24 9:27:11

目录

简介

什么是 Apache ShardingSphere?

分库分表的背景

使用

pom

配置

1,application.properties配置文件

2,创建配置类

分表

验证分表

常见问题

自定义分表规则未生效


简介

官网:Apache ShardingSphere

版本:4.x

什么是 Apache ShardingSphere?

Apache ShardingSphere 是一款分布式的数据库生态系统,可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。

分库分表的背景

传统的将数据集中存储⾄单⼀数据节点的解决⽅案,在性能、可⽤性和运维成本这三⽅⾯已经难于满⾜互联⽹的海量数据场景。

随着业务数据量的增加,原来所有的数据都是在一个数据库上的,网络IO及文件IO都集中在一个数据库上的,因此CPU、内存、文件IO、网络IO都可能会成为系统瓶颈。

当业务系统的数据容量接近或超过单台服务器的容量、QPS/TPS接近或超过单个数据库实例的处理极限等,

在数据量超过阈值的情况下,索引深度的增加也将使得磁盘访问的 IO 次数增加,进而导致查询性能的下降;

⾼并发访问请求也使得集中式数据库成为系统的最⼤瓶颈。

在传统的关系型数据库⽆法满⾜互联⽹场景需要的情况下,将数据存储⾄原⽣⽀持分布式的 NoSQL 的尝试越来越多。但 NoSQL 并不能包治百病。

此时,往往是采用垂直和水平结合的数据拆分方法,把数据服务和数据存储分布到多台数据库服务器上。

使用

pom

            <!-- sharding-jdbc -->
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
                <version>4.1.1</version>
            </dependency>
            <!-- 数据源治理, 动态切换  -->
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-orchestration</artifactId>
                <version>4.1.1</version>
            </dependency>
            <!-- 分布式事务 -->
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-transaction-xa-core</artifactId>
                <version>4.1.1</version>
            </dependency>

配置

配置手册 :: ShardingSphere

1,application.properties配置文件

#垂直分表策略
# 配置真实数据源
spring.shardingsphere.datasource.names=m1

# 配置第 1 个数据源,数据源
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/coursedb?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root

# 指定表的分布情况 配置表在哪个数据库里,表名是什么。水平分表,分两个表:m1.course_1,m1.course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}

# 指定表的主键生成策略
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
#雪花算法的一个可选参数
spring.shardingsphere.sharding.tables.course.key-generator.props.worker.id=1

#使用自定义的主键生成策略
#spring.shardingsphere.sharding.tables.course.key-generator.type=MYKEY
#spring.shardingsphere.sharding.tables.course.key-generator.props.mykey-offset=88

#指定分片策略 约定cid值为偶数添加到course_1表。如果是奇数添加到course_2表。
# 选定计算的字段,分片健
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column= cid
# 根据计算的字段算出对应的表名。分片算法  course_$->{cid%2+1},2进courese1, 1进course2
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}

# 打开sql日志输出。
spring.shardingsphere.props.sql.show=true

spring.main.allow-bean-definition-overriding=true

2,创建配置类

本文采用这一种方式配置

MyDbConfig

package com.example.demo.config;

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

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.ComplexShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.shardingsphere.underlying.common.config.properties.ConfigurationPropertyKey;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;

/**
 * @author admin
 * @version 1.0
 * @since 2022/12/13 20:30
 */
@Configuration
@EnableTransactionManagement
public class MyDbConfig {

    /**
     * 主数据源, 默认注入
     */
    @Bean(name = "dataSource")
    @Primary
    public DataSource druidDataSource(Environment environment) {
        return createDruidDataSource(environment);
    }

    /**
     * 主数据源 分表
     */
    @Bean(name = "dataSourceSharding")
    public DataSource getShardingDataSource(@Qualifier("dataSource") DataSource dataSource, Environment environment) throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        // 分表规则
        TableRuleConfiguration userTableRuleConfiguration = new TableRuleConfiguration("user", "dataSource.user");
        ComplexShardingStrategyConfiguration userComplexShardingStrategyConfiguration = new ComplexShardingStrategyConfiguration("create_time",
            new UserComplexKeysShardingTableRule());
        userTableRuleConfiguration.setTableShardingStrategyConfig(userComplexShardingStrategyConfiguration);
        shardingRuleConfig.getTableRuleConfigs().add(userTableRuleConfiguration);

        //数据源
        Map<String, DataSource> result = new HashMap<>(1);
        result.put("dataSource", dataSource);
        Properties properties = new Properties();
        properties.put(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY.getKey(), 5);
        return ShardingDataSourceFactory.createDataSource(result, shardingRuleConfig, properties);
    }

    @Bean("mybatisMapWrapperFactory")
    public MybatisMapWrapperFactory createMybatisMapWrapperFactory() {
        return new MybatisMapWrapperFactory();
    }

    @Bean(name = "sqlSessionFactory")
    public MybatisSqlSessionFactoryBean createMybatisSqlSessionFactoryBean(@Qualifier("dataSourceSharding") DataSource dataSource,
                                                                           MybatisMapWrapperFactory mybatisMapWrapperFactory) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTransactionFactory(new SpringManagedTransactionFactory());
        // 扫描指定目录的xml
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*Mapper.xml"));
        Properties prop = new Properties();
        //转驼峰
        prop.setProperty("mapUnderscoreToCamelCase", "true");
        //允许使用自动生成主键
        prop.setProperty("useGeneratedKeys", "true");
        prop.setProperty("logPrefix", "dao.");
        bean.setConfigurationProperties(prop);
        // 扫描包
        bean.setTypeAliasesPackage("com.example.demo.dao");
        bean.setObjectWrapperFactory(mybatisMapWrapperFactory);
        return bean;
    }

    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean("transactionManager")
    public DataSourceTransactionManager createTransactionManager(@Qualifier("dataSourceSharding") DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    /**
     * 创建数据源
     * @param env env
     * @return DruidDataSource
     */
    private static DruidDataSource createDruidDataSource(Environment env) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.datasource.druid.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.druid.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.druid.password"));
        dataSource.setInitialSize(env.getProperty("spring.datasource.druid.initial-size", Integer.class));
        dataSource.setMaxActive(env.getProperty("spring.datasource.druid.max-active", Integer.class));
        dataSource.setMinIdle(env.getProperty("spring.datasource.druid.min-idle", Integer.class));
        dataSource.setMaxWait(env.getProperty("spring.datasource.druid.maxWait", Integer.class));
        dataSource.setValidationQuery(env.getProperty("spring.datasource.druid.validationQuery"));
        return dataSource;
    }
}

application.properties

spring.datasource.druid.initial-size = 10
spring.datasource.druid.min-idle = 10
spring.datasource.druid.max-active = 20
spring.datasource.druid.maxWait = 6000
spring.datasourceslave.druid.initial-size = 10
spring.datasourceslave.druid.min-idle = 10
spring.datasourceslave.druid.max-active = 20
spring.datasourceslave.druid.maxWait = 6000
spring.datasource.druid.validationQuery = SELECT 1 FROM DUAL

spring.datasource.druid.url = jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.druid.username = root
spring.datasource.druid.password = 123

分表

自定义分表规则,这里用创建时间截取年分表

UserComplexKeysShardingTableRule

package com.example.demo.config;

import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.util.CollectionUtils;

import com.google.common.collect.Range;

/**
 * 多列分片规则定义
 * @author admin
 * @version 1.0
 * @since 2023/01/05 15:15
 */
public class UserComplexKeysShardingTableRule implements ComplexKeysShardingAlgorithm<String> {

    private static final String TABLE_COLUMN_TIME = "create_time";

    private static final String TABLE_PREFIX      = "user_";

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> shardingValue) {
        return getShardingValue(shardingValue);
    }

    /**
     * 获取分片键对应的值
     *
     * @param shardingValue shardingValue
     * @return Collection<String>
     */
    private Collection<String> getShardingValue(ComplexKeysShardingValue<String> shardingValue) {
        Collection<String> times = new HashSet<>();

        Map<String, Collection<String>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
        Map<String, Range<String>> columnNameAndRangeValuesMap = shardingValue.getColumnNameAndRangeValuesMap();

        if (columnNameAndShardingValuesMap.containsKey(TABLE_COLUMN_TIME)) {
            Collection<String> collection = columnNameAndShardingValuesMap.get(TABLE_COLUMN_TIME);
            Object next = collection.iterator().next();
            times.add(next.toString().substring(2, 4));
        }
        if (columnNameAndRangeValuesMap.containsKey(TABLE_COLUMN_TIME)) {
            Range<String> range = columnNameAndRangeValuesMap.get(TABLE_COLUMN_TIME);
            Collection<String> values = getTimeList(range);
            if (values.isEmpty()) {
                throw new UnsupportedOperationException("分片规则键不能为空");
            } else {
                times.addAll(values);
            }
        }
        if (CollectionUtils.isEmpty(times)) {
            throw new UnsupportedOperationException("分片规则键不能为空");
        }
        Set<String> tableNames = new HashSet<>();
        for (String billTime : times) {
            tableNames.add(getTableName(billTime));
        }
        return tableNames;
    }

    /**
     * 获取时间
     *
     * @param valueRange valueRange
     * @return Collection<String>
     */
    private Collection<String> getTimeList(Range<String> valueRange) {
        Set<String> prefixes = new HashSet<>();
        if (valueRange.isEmpty()) {
            throw new UnsupportedOperationException("分片规则键不能为空");
        } else {
            int start = Integer.parseInt(valueRange.lowerEndpoint().substring(2, 4));
            int end = Integer.parseInt(valueRange.upperEndpoint().substring(2, 4));
            int max = Integer.parseInt(dateToString(new Date(), "yy"));
            // 只能查2018年开始到当前年份的数据
            start = Math.max(start, 18);
            start = Math.min(start, max);
            end = Math.max(end, 18);
            end = Math.min(end, max);
            while (start <= end) {
                prefixes.add(String.valueOf(start));
                start++;
            }
            return prefixes;
        }
    }

    /**
     * 转格式
     * @param date date
     * @param format format
     * @return String
     */
    private String dateToString(Date date, String format) {
        SimpleDateFormat formater = new SimpleDateFormat(format);
        return formater.format(date);
    }

    /**
     * 根据学校编码分表
     *
     * @param simpleYear 年份的第3、4位
     * @return String
     */
    private static String getTableName(String simpleYear) {
        if (simpleYear.length() != 2) {
            throw new UnsupportedOperationException("分片规则键不能为空");
        }
        System.out.println("getTableName=" + TABLE_PREFIX + simpleYear);
        return TABLE_PREFIX + simpleYear;
    }

}

getColumnNameAndShardingValuesMap() // 分片键= shardingValue.getColumnNameAndRangeValuesMap();// 分片键IN和范围查询

验证分表

表是这样的

CREATE TABLE `user_21` (
  `id` int(10) NOT NULL,
  `name` varchar(32) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

实体类和service这里就不赘述了,这里贴下查询TestController

    @RequestMapping("select")
    public String select() {
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(User::getCreateTime, new Date());
        userService.list(queryWrapper);
        return "ok";
    }

结果:日志打印

当前时间是二三年,这里的报错是因为没有创建对应表,创建user_23后则正常

常见问题

自定义分表规则未生效

场景: 分表规则字段范围查询且有拼接或格式处理

解决: 去掉sql上的字段格式化处理, 在服务层对传入参数进行处理

失效示例代码

and pay_time between DATE_FORMAT(#{param.billDate,jdbcType=VARCHAR}, '%Y-%m-%d 00:00:00')
                AND DATE_FORMAT(#{param.billDate,jdbcType=VARCHAR}, '%Y-%m-%d 23:59:59')

and pay_time between CONCAT(#{param.billDate,jdbcType=VARCHAR}, ' 00:00:00') AND
                CONCAT(#{param.billDate,jdbcType=VARCHAR}, ' 23:59:59')

AND DATE_FORMAT(pay_time, '%Y-%m-%d') = #{param.billDate,jdbcType=VARCHAR}

成功示例代码

and pay_time between #{param.payTimeStart,jdbcType=VARCHAR} AND #{param.payTimeEnd,jdbcType=VARCHAR}

持续更新ing!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ArcGIS基础实验操作100例--实验42创建渔网Fishnet

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验42 创建渔网Fishnet 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&a…

深度学习推理框架调研总结

深度学习推理框架1.移动端深度学习推理框架调研1.1 小米的MACE&#xff08;2017&#xff09;1.2 阿里的MNN1.3 腾讯的TNN1.4 ARM的tengine1.5 百度的paddle-mobie1.6 Facebook的Caffe2&#xff08;*&#xff09;1.7 Google的TensorFlow Lite &#xff08;*&#xff09;1.8 Appl…

南京晓庄操作系统期末复习【背诵部分】

操作系统期末复习背诵部分第一章1.操作系统的特征&#xff1a;2.操作系统的最基本特征是什么&#xff0c;为什么3.操作系统的目标4.操作系统的主要功能5.计算机资源6.多道程序设计时应解决什么问题7.三种操作系统及特点第二章1.进程的定义2.进程的特征3.进程的三种基本状态4.进…

Spark04: Transformation与Action开发

一、创建RDD的三种方式 RDD是Spark编程的核心&#xff0c;在进行Spark编程时&#xff0c;首要任务是创建一个初始的RDD这样就相当于设置了Spark应用程序的输入源数据然后在创建了初始的RDD之后&#xff0c;才可以通过Spark 提供的一些高阶函数&#xff0c;对这个RDD进行操作&a…

python学生管理系统(pyqt5 含界面)

学生管理系统项目流程 项目模块 账号登陆 人脸识别 增添学生信息 删除学生信息 改动学生信息 查询学生信息 项目主体框架 app.py为主代码&#xff0c;负责用于界面打开展示。img文件夹负责放置项目qrc的图像page文件夹为单独页面的类plugin文件夹为功能模块的类ui文件夹…

机器人中的数值优化|【一】数值优化基础

数值优化基础 凸集 Convex Sets 凸集的定义 令X是线性空间。如果对于X的子集S中的所有x和y&#xff0c;并且在区间 [0,1]中的所有t&#xff0c;点 (1−t)xty(1-t)x ty(1−t)xty也属于S&#xff0c;则S称为凸集。 不失一般性&#xff0c;对于所有的凸集&#xff0c;其线性组…

Zookeeper详解(二)——API 事件监听

Java API znode是zooKeeper集合的核心组件&#xff0c;zookeeper API提供了一小组方法使用zookeeper集合来操纵znode的所有细节。 客户端应该遵循以下步骤&#xff0c;与zookeeper服务器进行清晰和干净的交互。 连接到zookeeper服务器。zookeeper服务器为客户端分配会话ID。…

uniapp 之 接入小程序客服

目录 前言 小程序客服 代码只需要一步 配置也需要一步​​​​​​​ 前言 小程序客服 因老大 看到别人家有在线客服这个功能&#xff0c;就让我也做一个&#xff0c;这个功能很简单 效果图1 代码只需要一步 <button type"default" open-type"con…

MATLAB | 绘图复刻(六) | 分组环形热图

有粉丝问我Ecology Letters, (2021) 24: 1018–1028 Soil carbon persistence governed by plant input and mineral protection at regional and global scales 这篇文章中的Figure 2咋画&#xff0c;原图长这样&#xff1a; 复刻效果&#xff1a; 完整步骤 0 数据定义 按…

node.js创建网站实例1

1.node.js安装 我的电脑环境&#xff1a;win10 网址&#xff1a;https://nodejs.org/en/ 我下载了18.12.1版本 一路next默认安装&#xff0c;安装完成后&#xff0c;运行cmd&#xff0c;查看版本号 会同时安装npm&#xff0c;也可以同时查看版本号 2.创建第一个网站实例hell…

内卷对于2022是一种无奈,也是一种修行

其实我们谁也不知道2023年对于我们普通的开发人员来说会有什么样的试炼&#xff0c;因为2022年身边有太多的人&#xff0c;为了工作&#xff0c;为了生活&#xff0c;为了家庭&#xff0c;为了理想&#xff0c;不得不选择走向别人看似很卷的那条路。 对于我们周围的人来说&…

【Vim】基本操作及命令集详解

概述 Vim 是从 vi 发展出来的一个文本编辑器。vi 内置在Linux系统中&#xff0c;是vim的简化版编辑器&#xff0c;vim则需要进行安装使用。Vim代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;可以实现高效率移动和高效的输入&#xff0c;在程序员中被广泛使用。…

CPT203-Software Engineering(3)

文章目录9. Software Design9.1 Architecture Design9.1.1 Architectural patterns9.2 Component-level Design9.2.1 Component9.2.2 Views of component9.2.3 Component-level design process9.3 User Interface Design9.3.1 Interface Design Process9.3.2 Interface Design …

蓝桥杯Python练习题16-最大最小公倍数

资源限制   内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述   已知一个正整数N&#xff0c;问从1~N中任选出三个数&#xff0c;他们的最小公倍数最大可以为多少。 输入格式   输入一…

三维数学(一)

视频教程&#xff1a;https://www.bilibili.com/video/BV12s411g7gU?p155 向量 一个数字列表&#xff0c;表示各个维度上的有向位移&#xff1b;同时也是一个有大小有方向的物理量&#xff0c;大小及向量的模长&#xff0c;而方向即空间中向量的指向&#xff0c;可以表示物体…

TikTok Shop 越南站点收入已达Lazada 的 80%

让我们一起来看看今日都有哪些新鲜事吧&#xff01;01 TikTok Shop 越南站点收入已达Lazada 的 80% 据越南电商平台数据分析软件Metric.vn 统计&#xff0c;Shopee、Lazada、Tiki 和 Sendo 仍然主导着越南电子商务市场&#xff0c;1-11 月&#xff0c;共销售了 13 亿件产品。其…

简化开发小技巧-Mybatis-Plus的使用和常用操作

目录 简介 快速使用 pom 代码 mapper service 使用 常用操作 简单或操作查询 多条件或查询 更新字段为null 方法一&#xff0c;如果要更新的字段是String类型&#xff0c; 方法二&#xff0c; 使用mybatis-plus的字段注入。 方法三&#xff0c;使用UpdateWrapper…

基于R的Bilibili视频数据建模及分析——预处理篇

基于R的Bilibili视频数据建模及分析——预处理篇 文章目录基于R的Bilibili视频数据建模及分析——预处理篇0、写在前面1、项目介绍1.1 项目背景1.2 数据来源1.3 数据集展示2、数据预处理2.1 删除空数据2.2 增加id字段2.3 处理数值字段3、参考资料0、写在前面 实验环境 Python版…

Stable Diffusion背后原理(Latent Diffusion Models)

前言 2023年第一篇博客&#xff0c;大家新年好呀~ 这次来关注一下Stable Diffusion背后的原理&#xff0c;即 High-Resolution Image Synthesis with Latent Diffusion Models 这篇论文。 之前关注的那些工作只能工作到 256256256 \times 256256256 像素(resize成这个后才输…

设计模式简介

一、设计模式简介 编写软件过程中&#xff0c;程序员面临着来自耦合性&#xff0c;内聚性 以及可维护性&#xff0c;可扩展性&#xff0c;重用性&#xff0c;灵活性等多方面的挑战&#xff0c;设计模式是为了让程序&#xff08;软件&#xff09;&#xff0c;具有更好的&#xf…