分库分表与自定义分表规则

总体介绍

  • Oinone的分库分表方案是基于Sharding-JDBC的整合方案,要先具备一些Sharding-JDBC的知识。[Sharding-JDBC]https://shardingsphere.apache.org/document/current/cn/overview/

  • 做分库分表前,大家要有一个明确注意的点就是分表字段(也叫均衡字段)的选择,它是非常重要的,与业务场景非常相关。在明确了分库分表字段以后,甚至在功能上都要做一些妥协。比如分库分表字段在查询管理中做为查询条件是必须带上的,不然效率只会更低。

  • 分表字段不允许更新,所以代码里更新策略设置类永不更新,并在设置了在页面修改的时候为readonly

配置分表策略

  1. 配置ShardingModel模型走分库分表的数据源pamirsSharding
  2. 为pamirsSharding配置数据源以及sharding规则
    a. pamirs.sharding.define用于oinone的数据库表创建用
    b. pamirs.sharding.rule用于分表规则配置
  3. 为pamirsSharding配置数据源以及sharding规则

    1)指定模型对应数据源

pamirs:
  framework:
    system:
      system-ds-key: base
      system-models:
        - base.WorkerNode
    data:
      default-ds-key: pamirs
      ds-map:
        base: base
      modelDsMap:
        "[demo.ShardingModel]": pamirsSharding  #配置模型对应的库

2)分库分表规则配置

pamirs: 
  sharding:
    define:
      data-sources:
        ds: pamirs
        pamirsSharding: pamirs #申明pamirsSharding库对应的pamirs数据源
      models:
        "[trigger.PamirsSchedule]":
          tables: 0..13
        "[demo.ShardingModel]":
          tables: 0..7
          table-separator: _
    rule:
      pamirsSharding: #配置pamirsSharding库的分库分表规则
        actual-ds:
          - pamirs  #申明pamirsSharding库对应的pamirs数据源
        sharding-rules:
          # Configure sharding rule ,以下配置跟sharding-jdbc配置一致
          - tables:
              demo_core_sharding_model: #demo_core_sharding_model表规则配置
                actualDataNodes: pamirs.demo_core_sharding_model_${0..7}
                tableStrategy:
                  standard:
                    shardingColumn: user_id
                    shardingAlgorithmName: table_inline
            shardingAlgorithms:
              table_inline:
                type: INLINE
                props:
                  algorithm-expression: demo_core_sharding_model_${(Long.valueOf(user_id) % 8)}
        props:
          sql.show: true

自定义规则

  • 默认规则即通用的分库分表策略,如按照数据量、哈希等方式进行分库分表;通常默认规则是可以的。
  • 但在一些复杂的业务场景下,使用默认规则可能无法满足需求,需要根据实际情况进行自定义。例如,某些业务可能有特定的数据分布模式或者查询特点,需要定制化的分库分表规则来优化数据访问性能或者满足业务需求。在这种情况下,使用自定义规则可以更好地适应业务的需求。

自定义分表规则示例

示例1:按月份分表(DATE_MONTH )

package pro.shushi.pamirs.demo.core.sharding;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;

import java.util.*;

/**
 * @author wangxian
 * @version 1.0
 * @description
 */
@Component
@Slf4j
public class DateMonthShardingAlgorithm implements StandardShardingAlgorithm<Date> {

    private Properties props;

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> preciseShardingValue) {
        Date date = preciseShardingValue.getValue();
        String suffix = "_" + (DateUtil.month(date) + 1);
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(suffix)) {
                return tableName;
            }
        }
        throw new IllegalArgumentException("未找到匹配的数据表");
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Date> rangeShardingValue) {
        List<String> list = new ArrayList<>();
        log.info(rangeShardingValue.toString());
        Range<Date> valueRange = rangeShardingValue.getValueRange();
        Date lowerDate = valueRange.lowerEndpoint();
        Date upperDate = valueRange.upperEndpoint();
        Integer begin = DateUtil.month(lowerDate) + 1;
        Integer end = DateUtil.month(upperDate) + 1;
        TreeSet<String> suffixList = ShardingUtils.getSuffixListForRange(begin, end);
        for (String tableName : availableTargetNames) {
            if (containTableName(suffixList, tableName)) {
                list.add(tableName);
            }
        }
        return list;
    }

    private boolean containTableName(Set<String> suffixList, String tableName) {
        boolean flag = false;
        for (String s : suffixList) {
            if (tableName.endsWith(s)) {
                flag = true;
                break;
            }
        }
        return flag;
    }

    @Override
    public void init() {

    }

    @Override
    public String getType() {
        return "DATE_MONTH";
    }

    @Override
    public Properties getProps() {
        return this.props;
    }

    @Override
    public void setProps(Properties properties) {
        this.props = props;
    }
}

示例2:按特定字段截取去取模分表

package pro.shushi.pamirs.demo.core.sharding;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;

import java.util.Collection;
import java.util.Properties;

/**
 * @author wangxian
 * @version 1.0
 * @description
 */
@Component
@Slf4j
public class AppUserCodeShardingAlgorithm implements StandardShardingAlgorithm<String> {

    private Properties props;

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> preciseShardingValue) {
        String appUserCode = preciseShardingValue.getValue();
        String suffix = "_" + Long.parseLong(appUserCode.substring(1)) % 21;
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(suffix)) {
                return tableName;
            }
        }
        throw new IllegalArgumentException("未找到匹配的数据表");
    }

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

    @Override
    public String getType() {
        return "APP_USER_CODE_TYPE";
    }

    @Override
    public Properties getProps() {
        return this.props;
    }

    @Override
    public void setProps(Properties properties) {
        this.props = props;
    }

    @Override
    public void init() {

    }
}

使用自定义分表策略

1)指定模型对应数据源

pamirs:
  framework:
    system:
      system-ds-key: base
      system-models:
        - base.WorkerNode
    data:
      default-ds-key: pamirs_biz
      ds-map:
        base: base
        demo_core: pamirs
      modelDsMap:
        "[demo.record.MsgRecode]": pamirsSharding

2)分库分表规则配置

pamirs:
  sharding:
    define:
      data-sources:
        ds: pamirs
        pamirsSharding: pamirs
      models:
        "[trigger.PamirsSchedule]":
          tables: 0..13
        "[demo.record.MsgRecode]":
          tables: 0..20
          table-separator: _
    rule:
      pamirsSharding:
        actual-ds:
          - pamirs
        sharding-rules:
          - tables:
              demo_core_record_msg_recode:
                actualDataNodes: pamirs.demo_core_record_msg_recode_${0..20}
                tableStrategy:
                  standard:
                    shardingColumn: app_user_code
                    shardingAlgorithmName: app_user_code_table_algorithm
            shardingAlgorithms:
              app_user_code_table_algorithm:
                type: APP_USER_CODE_TYPE
                props:
                  strategy: STANDARD
                  algorithmClassName:
                    pro.shushi.pamirs.demo.core.sharding.AppUserCodeShardingAlgorithm

配置自定义规则SPI

分库分表规则SPI

在resources/META-INF/services 配置 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm

pro.shushi.pamirs.demo.core.sharding.AppUserCodeShardingAlgorithm
pro.shushi.pamirs.demo.core.sharding.DateMonthShardingAlgorithm

Oinone社区 作者:望闲原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/7155.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
望闲的头像望闲数式管理员
上一篇 2024年5月9日 pm3:56
下一篇 2024年5月13日 pm7:06

相关推荐

  • 【PostgreSQL】后端部署使用PostgreSQL数据库

    PostgreSQL数据库配置 驱动配置 Maven配置(14.3版本可用) <postgresql.version>42.6.0</postgresql.version> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>${postgresql.version}</version> </dependency> 离线驱动下载 postgresql-42.2.18.jarpostgresql-42.6.0.jarpostgresql-42.7.3.jar JDBC连接配置 pamirs: datasource: base: type: com.alibaba.druid.pool.DruidDataSource driverClassName: org.postgresql.Driver url: jdbc:postgresql://127.0.0.1:5432/pamirs?currentSchema=base username: xxxxxx password: xxxxxx 连接url配置 暂无官方资料 url格式 jdbc:postgresql://${host}:${port}/${database}?currentSchema=${schema} 在jdbc连接配置时,${database}和${schema}必须完整配置,不可缺省。 其他连接参数如需配置,可自行查阅相关资料进行调优。 方言配置 pamirs方言配置 pamirs: dialect: ds: base: type: PostgreSQL version: 14 major-version: 14.3 pamirs: type: PostgreSQL version: 14 major-version: 14.3 数据库版本 type version majorVersion 14.x PostgreSQL 14 14.3 PS:由于方言开发环境为14.3版本,其他类似版本(14.x)原则上不会出现太大差异,如出现其他版本无法正常支持的,可在文档下方留言。 schedule方言配置 pamirs: event: enabled: true schedule: enabled: true dialect: type: PostgreSQL version: 14 major-version: 14.3 type version majorVersion PostgreSQL 14 14.3 PS:由于schedule的方言在多个版本中并无明显差异,目前仅提供一种方言配置。 其他配置 逻辑删除的值配置 pamirs: mapper: global: table-info: logic-delete-value: (EXTRACT(epoch FROM CURRENT_TIMESTAMP) * 1000000 + EXTRACT(MICROSECONDS FROM CURRENT_TIMESTAMP))::bigint PostgreSQL数据库用户初始化及授权 — init root user (user name can be modified by oneself) CREATE USER root WITH PASSWORD 'password'; — if using automatic database and schema creation, this is very important. ALTER USER root CREATEDB; SELECT * FROM pg_roles; — if using postgres database, this authorization is required. GRANT CREATE ON DATABASE postgres TO root;

    2023年11月1日
    1.2K00
  • 如何通过自定义支持excel导出的动态表头

    介绍 本文需要阅读过前置文档如何自定义Excel导出功能,动态表头的功能在前置文档的基础上做的进一步扩展,本文未提到的部分都参考这个前置文档。 在日常的业务开发中,我们在导出的场景会遇到需要设置动态表头的场景,比如统计商品在最近1个月的销量,固定表头列为商品的名称等基础信息,动态表头列为最近一个月的日期,在导出的时候设置每个日期的销量,本文将通过此业务场景提供示例代码。 1.自定义导出任务模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemDynamicExcelExportTask.MODEL_MODEL) @Model(displayName = "商品-Excel动态表头导出任务") public class DemoItemDynamicExcelExportTask extends ExcelExportTask { public static final String MODEL_MODEL = "demo.DemoItemDynamicExcelExportTask"; } 2.自定义导出任务处理数据的扩展点 package pro.shushi.pamirs.demo.core.excel.exportdemo.extPoint; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.FetchUtil; import pro.shushi.pamirs.core.common.cache.MemoryIterableSearchCache; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.file.api.config.FileConstant; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum; import pro.shushi.pamirs.file.api.entity.EasyExcelCellDefinition; import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.util.ExcelFixedHeadHelper; import pro.shushi.pamirs.file.api.util.ExcelHelper; import pro.shushi.pamirs.file.api.util.ExcelTemplateInit; import pro.shushi.pamirs.framework.common.entry.TreeNode; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.orm.ReadApi; import pro.shushi.pamirs.meta.api.core.orm.systems.relation.RelationReadApi; import pro.shushi.pamirs.meta.api.dto.config.ModelConfig; import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.TtypeEnum; import pro.shushi.pamirs.meta.util.FieldUtils; import java.util.*; @Component public class DemoItemDynamicExportExtPoint extends ExcelExportSameQueryPageTemplate<DemoItem> implements ExcelTemplateInit { public static final String TEMPLATE_NAME ="商品动态导出"; @Override public List<ExcelWorkbookDefinition> generator() { ExcelFixedHeadHelper excelFixedHeadHelper = ExcelHelper.fixedHeader(DemoItem.MODEL_MODEL,TEMPLATE_NAME) .createBlock(TEMPLATE_NAME, DemoItem.MODEL_MODEL) .setType(ExcelTemplateTypeEnum.EXPORT); return Collections.singletonList(excelFixedHeadHelper.build()); } public static void buildHeader(ExcelFixedHeadHelper excelFixedHeadHelper) { excelFixedHeadHelper.addColumn("name","名称") .addColumn("cateName","类目") .addColumn("searchFrom","搜索来源") .addColumn("description","描述") .addColumn("itemPrice","单价") .addColumn("inventoryQuantity","库存"); } @Override @ExtPoint.Implement(expression = "context.model == \"" + DemoItem.MODEL_MODEL+"\" && context.name == \"" +TEMPLATE_NAME+"\"" ) public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) { List<Object> result = super.fetchExportData(exportTask,context); Object block = result.get(0); if (block instanceof ArrayList) { ((List<Object>) block).forEach(o -> { if (o instanceof DemoItem) { DemoItem item = (DemoItem) o; // TODO 设置动态表头部分字段的值 item.get_d().put("2024-09-10", "1111"); item.get_d().put("2024-09-11", "2222"); }…

    2024年9月11日
    3.6K00
  • EIP开放接口使用MD5验签发起请求(v5.x)

    验签工具类 PS:该验签方法仅在pamirs-core的5.0.16版本以上可正常使用 public class EipSignUtils { public static final String SIGN_METHOD_MD5 = "md5"; private static final String SIGN_METHOD_HMAC = "hmac"; private static final String SECRET_KEY_ALGORITHM = "HmacMD5"; private static final String MESSAGE_DIGEST_MD5 = "MD5"; public static String signTopRequest(Map<String, String> params, String secret, String signMethod) throws IOException { // 第一步:检查参数是否已经排序 String[] keys = params.keySet().toArray(new String[0]); Arrays.sort(keys); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(); if (SIGN_METHOD_MD5.equals(signMethod)) { query.append(secret); } for (String key : keys) { String value = params.get(key); if (StringUtils.isNoneBlank(key, value)) { query.append(key).append(value); } } // 第三步:使用MD5/HMAC加密 byte[] bytes; if (SIGN_METHOD_HMAC.equals(signMethod)) { bytes = encryptHMAC(query.toString(), secret); } else { query.append(secret); bytes = encryptMD5(query.toString()); } // 第四步:把二进制转化为大写的十六进制(正确签名应该为32大写字符串,此方法需要时使用) return byte2hex(bytes); } private static byte[] encryptHMAC(String data, String secret) throws IOException { byte[] bytes; try { SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), SECRET_KEY_ALGORITHM); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); bytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); } catch (GeneralSecurityException e) { throw new IOException(e.toString(), e); } return bytes; } private static byte[] encryptMD5(String data) throws IOException { return encryptMD5(data.getBytes(StandardCharsets.UTF_8)); } private static byte[] encryptMD5(byte[] data) throws IOException { try { MessageDigest md = MessageDigest.getInstance(MESSAGE_DIGEST_MD5); return md.digest(data); } catch (NoSuchAlgorithmException e)…

    2024年6月29日
    1.5K00
  • 自定义数据权限拦截处理

    业务场景 公司给员工对哪些模块有访问权限,这个时候就需要在员工访问模块表的时候做数据过滤, 解决方案 我们可以通过平台提供的数据过滤占位符解决这个问题,新建一条数据行权限,过滤语句条件是占位符,再编写占位符的解析逻辑 1.初始化权限基础数据 package pro.shushi.pamirs.demo.core.init; import com.google.common.collect.Lists; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.shushi.pamirs.auth.api.constants.AuthConstants; import pro.shushi.pamirs.auth.api.enmu.AuthGroupTypeEnum; import pro.shushi.pamirs.auth.api.enmu.PermissionDataSourceEnum; import pro.shushi.pamirs.auth.api.enmu.PermissionTypeEnum; import pro.shushi.pamirs.auth.api.model.AuthGroup; import pro.shushi.pamirs.auth.api.model.AuthRole; import pro.shushi.pamirs.auth.api.model.ResourcePermission; import pro.shushi.pamirs.boot.base.model.UeModule; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.api.init.InstallDataInit; import pro.shushi.pamirs.boot.common.api.init.UpgradeDataInit; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.demo.core.placeholder.EmployeeModulePlaceholder; import pro.shushi.pamirs.framework.common.utils.ObjectUtils; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.domain.module.ModuleDefinition; import java.util.Collections; import java.util.List; @Slf4j @Component @Order(0) public class DemoModuleBizInit implements InstallDataInit, UpgradeDataInit { @Override public List<String> modules() { return Collections.singletonList(DemoModule.MODULE_MODULE); } @Override public int priority() { return 0; } @Override public boolean init(AppLifecycleCommand command, String version) { this.initAuth(); return true; } @Override public boolean upgrade(AppLifecycleCommand command, String version, String existVersion) { this.initAuth(); return true; } private void initAuth() { AuthGroup authGroup = new AuthGroup(); authGroup.setName("测试权限组") .setDisplayName("测试权限组") .setType(AuthGroupTypeEnum.RUNTIME) .setActive(true); authGroup.createOrUpdate(); AuthRole authRole = new AuthRole(); authRole.setCode("TEST_ROLE_1") .setName("测试角色") .setRoleTypeCode(AuthConstants.ROLE_SYSTEM_TYPE_CODE) .setPermissionDataSource(PermissionDataSourceEnum.CUSTOM) .setActive(true); authRole.createOrUpdate(); authRole.setGroups(Lists.newArrayList(authGroup)); authRole.fieldSave(AuthRole::getGroups); ResourcePermission authPermission = new ResourcePermission(); authPermission.setName("测试模块权限过滤") .setDomainExp(EmployeeModulePlaceholder.PLACEHOLDER) .setModel(ModuleDefinition.MODEL_MODEL) .setPermRead(true) .setPermRun(true) .setPermissionType(PermissionTypeEnum.ROW) .setPermissionDataSource(PermissionDataSourceEnum.CUSTOM) .setCanShow(true) .setActive(true); ResourcePermission authPermission2 = ObjectUtils.clone(authPermission); authPermission2.setName("测试ue模块权限过滤").setModel(UeModule.MODEL_MODEL); authGroup.setPermissions(Lists.newArrayList(authPermission, authPermission2)); authGroup.fieldSave(AuthGroup::getPermissions); } } 这里演示的module表比较特殊,需要同时设置ModuleDefinition和UeModule这2个模型做数据过滤 2.编写占位符拦截替换逻辑 package pro.shushi.pamirs.demo.core.placeholder; import org.springframework.stereotype.Component; import pro.shushi.pamirs.user.api.AbstractPlaceHolderParser; @Component public class EmployeeModulePlaceholder extends AbstractPlaceHolderParser { public static final String PLACEHOLDER = "${employeeModulePlaceholder}"; protected String value() { // TODO…

    2023年11月24日
    1.1K00

Leave a Reply

登录后才能评论