Spring Boot集成ShardingSphere实现按月数据分片及创建自定义分片算法 | Spring Cloud 44

一、前言

在前面我们通过以下章节对数据分片有了基础的了解:

Spring Boot集成ShardingSphere实现数据分片(一) | Spring Cloud 40

Spring Boot集成ShardingSphere实现数据分片(二) | Spring Cloud 41

Spring Boot集成ShardingSphere实现数据分片(三) | Spring Cloud 42

书接上回,本章进行对以下部分进行讲解演示:

  • 基于时间范围的分片算法(INTERVAL)实现按月数据分片
  • 创建自定义ShardingSphere分片算法及实现按月数据分片

下面我们开始正文内容。

二、时间范围分片算法

2.1 简介

  • 类型:INTERVAL

  • 可配置属性:

属性名称数据类型说明默认值
datetime-patternString分片键的时间戳格式,必须遵循 Java DateTimeFormatter 的格式。例如:yyyy-MM-dd HH:mm:ssyyyy-MM-ddHH:mm:ss 等。但不支持与 java.time.chrono.JapaneseDate 相关的 Gy-MM
datetime-lowerString时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致
datetime-upper (可选项)String时间分片上界值,格式与 datetime-pattern 定义的时间戳格式一致当前时间
sharding-suffix-patternString分片数据源或真实表的后缀格式,必须遵循 Java DateTimeFormatter 的格式,必须和 datetime-interval-unit 保持一致。例如:yyyyMM对应MONTHS
datetime-interval-amount (可选项)int分片键时间间隔,超过该时间间隔将进入下一分片1
datetime-interval-unit (可选项)String分片键时间间隔单位,必须遵循 Java ChronoUnit 的枚举值。例如:MONTHSDAYS
  • 所属类:org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm

  • 注意事项:

    此算法主动忽视了 datetime-pattern 的时区信息。 这意味着当 datetime-lower, datetime-upper 和传入的分片键含有时区信息时,不会因为时区不一致而发生时区转换。 当传入的分片键为 java.time.Instant 时存在特例处理,其会携带上系统的时区信息后转化为 datetime-pattern 的字符串格式,再进行下一步分片。

ShardingSphere内置提供了多种分片算法,请见官网:
https://shardingsphere.apache.org/document/5.2.1/cn/user-manual/common-config/builtin-algorithm/sharding/

2.2 使用示例

2.2.1 项目总体结构

在这里插入图片描述

2.2.2 Maven依赖

shading-sphere/shading-tables/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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shading-sphere</artifactId>
        <groupId>com.gm</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shading-tables</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>


        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>

    </dependencies>

</project>
  • shardingsphere-jdbc-core-spring-boot-starter使用版本5.2.1

  • JDBCORM 框架选用mybatis-plus

2.2.3 逻辑表 t_share_month

DROP TABLE IF EXISTS `t_share_month_202304`;
CREATE TABLE `t_share_month_202304`  (
  `id` bigint NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `create_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `t_share_month_202305`;
CREATE TABLE `t_share_month_202305`  (
  `id` bigint NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `create_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `t_share_month_202306`;
CREATE TABLE `t_share_month_202306`  (
  `id` bigint NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `create_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

2.2.4 配置文件

shading-sphere/shading-tables/src/main/resources/application.yml

spring:
  application:
    name: @artifactId@
  shardingsphere:
    # 数据源配置
    datasource:
      names: ds1
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.0.35:3306/db1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
        username: root
        password: '1qaz@WSX'

    # 定义规则
    rules:
      sharding:
        # 数据分片规则配置
        tables:
          # 指定某个表的分片配置,逻辑表名
          t_share_month:
            actual-data-nodes: ds1.t_share_month_202$->{304..306}
            #actual-data-nodes: ds1.t_share_month_${2022..2030}${(1..12).collect{t ->t.toString().padLeft(2,'0')}}
            table-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: t_share_month_table_inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
        # 分片算法配置
        sharding-algorithms:
          # 分片算法名称
          t_share_month_table_inline:
            type: INTERVAL
            props:
              datetime-pattern: yyyy-MM-dd HH:mm:ss
              datetime-lower: 2023-04-01 00:00:00
              datetime-interval-unit: MONTHS
              sharding-suffix-pattern: yyyyMM
        # 分布式序列算法配置(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段)
        keyGenerators:
          # 分布式序列算法名称
          snowflake:
            # 分布式序列算法类型
            type: SNOWFLAKE

    props:
      sql-show: true #显示sql

配置简要说明:

  • 逻辑表t_share_month按照create_time分片键进行每月数据分片

注意事项:

  • 当部分月份的物理表未实际创建时,会造成分布式序列不生效,此时如物理表主键采用分布式序列会提示:Cause: java.sql.SQLException: Field 'id' doesn't have a default value

三、自定义分片算法

ShardingSphere支持两种方式来扩展自定义算法:SPIClassBasedCLASS_BASE实际上是已经实现了的SPI

目录结构及依赖情况,请见2.2.12.2.2章节。

ShardingSphere内置提供了多种分片算法,请见官网:
https://shardingsphere.apache.org/document/5.2.1/cn/user-manual/common-config/builtin-algorithm/sharding/

3.1 创建自定义分片算法

新建一个类MonthStrategyShardingAlgorithm并实现StandardShardingAlgorithm接口。

com/gm/shading/tables/component/MonthStrategyShardingAlgorithm.java

import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
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 java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
public class MonthStrategyShardingAlgorithm implements StandardShardingAlgorithm<LocalDateTime> {

    /** 配置值需要储存 */
    private Properties props;

    private static final DateTimeFormatter yyyyMM = DateTimeFormatter.ofPattern("yyyyMM");

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<LocalDateTime> preciseShardingValue) {
        LocalDateTime dateTime = preciseShardingValue.getValue();
        String tableSuffix = dateTime.format(yyyyMM);
        String logicTableName = preciseShardingValue.getLogicTableName();
        String table = logicTableName.concat("_").concat(tableSuffix);
        System.out.println("MonthStrategyShardingAlgorithm.doSharding table name: {}" + table);
        return collection.stream().filter(s -> s.equals(table)).findFirst().orElseThrow(() -> new RuntimeException("逻辑分表不存在"));
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<LocalDateTime> rangeShardingValue) {
        // 逻辑表名
        String logicTableName = rangeShardingValue.getLogicTableName();

        // between and 的起始值
        Range<LocalDateTime> valueRange = rangeShardingValue.getValueRange();

        Set<String> queryRangeTables = extracted(logicTableName, valueRange.lowerEndpoint(), valueRange.upperEndpoint());
        ArrayList<String> tables = new ArrayList<>(collection);
        tables.retainAll(queryRangeTables);
        System.out.println("MonthStrategyShardingAlgorithm.doSharding tables collection name: {}" + tables);
        return tables;
    }


    /**
     * 根据范围计算表明
     *
     * @param logicTableName 逻辑表明
     * @param lowerEndpoint  范围起点
     * @param upperEndpoint  范围终端
     * @return 物理表名集合
     */
    private Set<String> extracted(String logicTableName, LocalDateTime lowerEndpoint, LocalDateTime upperEndpoint) {
        Set<String> rangeTable = new HashSet<>();
        while (lowerEndpoint.isBefore(upperEndpoint)) {
            String str = getTableNameByDate(lowerEndpoint, logicTableName);
            rangeTable.add(str);
            lowerEndpoint = lowerEndpoint.plusMonths(1);
        }
        // 获取物理表明
        String tableName = getTableNameByDate(upperEndpoint, logicTableName);
        rangeTable.add(tableName);
        return rangeTable;
    }

    /**
     * 根据日期获取表明
     *
     * @param dateTime       日期
     * @param logicTableName 逻辑表名
     * @return 物理表名
     */
    private String getTableNameByDate(LocalDateTime dateTime, String logicTableName) {
        String tableSuffix = dateTime.format(yyyyMM);
        return logicTableName.concat("_").concat(tableSuffix);
    }

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

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

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

}

3.2 基于ClassBased使用自定义分片算法

完整配置文件如下:

server:
  port: 8844

spring:
  application:
    name: @artifactId@
  shardingsphere:
    # 数据源配置
    datasource:
      names: ds1
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.0.35:3306/db1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
        username: root
        password: '1qaz@WSX'

    # 定义规则
    rules:
      sharding:
        # 数据分片规则配置
        tables:
          # 指定某个表的分片配置,逻辑表名
          t_share_month:
            actual-data-nodes: ds1.t_share_month_202$->{304..306}
            #actual-data-nodes: ds1.t_share_month_${2022..2030}${(1..12).collect{t ->t.toString().padLeft(2,'0')}}
            table-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: t_share_month_table_inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
        # 分片算法配置
        sharding-algorithms:
          # 分片算法名称
          t_share_month_table_inline:
            type: CLASS_BASED
            props:
              strategy: standard
              # 自定义标准分配算法
              algorithmClassName: com.gm.shading.tables.component.MonthStrategyShardingAlgorithm

        # 分布式序列算法配置(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段)
        keyGenerators:
          # 分布式序列算法名称
          snowflake:
            # 分布式序列算法类型
            type: SNOWFLAKE

    props:
      sql-show: true #显示sql

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

自定义算法核心配置:

          # 分片算法名称
          t_share_month_table_inline:
            type: CLASS_BASED
            props:
              strategy: standard
              # 自定义标准分配算法
              algorithmClassName: com.gm.shading.tables.component.MonthStrategyShardingAlgorithm

3.3 基于SPI使用自定义分片算法

3.3.1 SPI配置

META-INF/services/org.apache.shardingsphere.sharding.spi.ShardingAlgorithm 文件中添加自定义分片算法全类名:

com.gm.shading.tables.component.MonthStrategyShardingAlgorithm

在这里插入图片描述

3.3.2 配置文件

完整配置文件如下:

server:
  port: 8844

spring:
  application:
    name: @artifactId@
  shardingsphere:
    # 数据源配置
    datasource:
      names: ds1
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.0.35:3306/db1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
        username: root
        password: '1qaz@WSX'

    # 定义规则
    rules:
      sharding:
        # 数据分片规则配置
        tables:
          # 指定某个表的分片配置,逻辑表名
          t_share_month:
            actual-data-nodes: ds1.t_share_month_202$->{304..306}
            #actual-data-nodes: ds1.t_share_month_${2022..2030}${(1..12).collect{t ->t.toString().padLeft(2,'0')}}
            table-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: t_share_month_table_inline
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
        # 分片算法配置
        sharding-algorithms:
          # 分片算法名称
          t_share_month_table_inline:
            type: CREATE_TIME

        # 分布式序列算法配置(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段)
        keyGenerators:
          # 分布式序列算法名称
          snowflake:
            # 分布式序列算法类型
            type: SNOWFLAKE

    props:
      sql-show: true #显示sql

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

自定义算法核心配置:

        # 分片算法配置
        sharding-algorithms:
          # 分片算法名称
          t_share_month_table_inline:
            type: CREATE_TIME

注意事项:

  • 配置文件中分片算法的type属性,必须与自定义分片算法中的getType()方法的值相对应。

3.4 源码

上述示例完整源码请见:https://gitee.com/gm19900510/springboot-cloud-example.git

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

权限提升:信息收集 .(Linux系统)

权限提升&#xff1a;信息收集. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通过 Webshel…

1.1 基于B/S 结构的 Web 应用

文章目录 1.1 基于B/S 结构的 Web 应用1.2 JDK安装与配置1.3 服务器Tomcat下载与安装1.4 Eclipse安装与使用1.4.1 Eclipse 下载及创建Dynamic Web Project1.4.2 Eclipse 中的编码问题1.4.3 将Tomcat和Eclipse相关联1.4.4 Eclipse 自动部署项目到 Tomcat 的 webapps 目录 1.5 My…

【AWS入门】AWS Lamda

目录 创建一个Lamda函数用Lamda函数控制启停EC2实例创建一台EC2实例创建角色创建lamda函数 使用Amazon EventBridge计划启停实例创建EventBridge 用户往S3存储桶上传图片文件&#xff0c;触发Lambda函数&#xff0c;将图片压缩并上传至另一个存储桶创建两个存储桶通过Cloudform…

【SpringMVC】| SpringMVC执行流程原理 | 常用注解 剥析

MVC目录 一. &#x1f981; MVC模型二. &#x1f981; SpringMVC1. SpringMVC执行流程&#xff08;重点&#xff09;Ⅰ. SpringMVC四大组件Ⅱ. 执行流程 2. RequestMapping3. RequestParam4. ReuqestHeader & CookieValue5. RESTful风格支持Ⅰ. 传统 vs restfulⅡ. PathVar…

【网络技术】什么是CNI

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 Never look back unless you are planning to go that way. 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用…

【应急响应】日志自动提取分析项目ELKLogkitLogonTracerAnolog等

日志自动提取-七牛Logkit&观星应急工具 1、七牛Logkit&#xff1a;(Windows&Linux&Mac等) https://github.com/qiniu/logkit/ 支持的数据源&#xff08;各类日志&#xff0c;各个系统&#xff0c;各个应用等&#xff09; File: 读取文件中的日志数据&#xff0c;包…

面了一个4年经验的测试工程师,自动化都不会也要15k,我也是醉了····

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…

数说热点 | 跟着《长月烬明》起飞,今年各地文旅主打的就是一个听劝

近日&#xff0c;随着热播剧《长月烬明》的爆火&#xff0c;蚌埠、宣城、敦煌等多个与剧情梦幻联动的宝藏城市被带飞&#xff0c;各地热心网友也纷纷催促自家文旅局赶紧“蹭飞”&#xff0c;《长月烬明》以一己之力打造了影视文旅融合的新样板。 仙偶剧特效天花板&#xff0c;…

《互联网安全产品漏洞管理规定》

《网络产品安全漏洞管理规定》由工业和信息化部、国家互联网信息办公室、公安部联合印发&#xff0c;自2021年9月1日起施行。 该《规定》明确&#xff0c;任何组织或者个人不得利用网络产品安全漏洞从事危害网络安全的活动&#xff0c;不得非法收集、出售、发布网络产品安全漏洞…

Redis高频面试题,使用场景

一、缓存 1、什么是缓存穿透 ? 怎么解决 ? 缓存穿透 查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库。 解决 方案一&#xff1a;缓存空数据&#xff0c;查询返回的数据为空&#xff0c;仍把这个空结果进行…

【JavaEE】认识线程

目录 1、什么是线程 2、为什么引入线程 2.1、线程的优缺点 3、CPU的工作原理 4、线程和进程的关系 4.1、线程和进程的入口函数 4.2、线程独享的资源 1、什么是线程 一个进程中可以有一个或者多个线程&#xff0c;每个线程都是一个独立的执行流。多个线程之间&#xff0c;也…

3.rabbitMQ之发布确认高级和整合springboot(重要)找了很多博客整理出来的

1.极端情况下 rabbitMQ需要重启,导致消息投递失败(生产者发消息全部丢失)(交换机或者队列出问题) 生产者需要把数据放到缓存,用定时任务重新发送 解决方法: 0.必须配置文件写 spring.rabbitmq.publisher-confirm-typecorrelatedspring.rabbitmq.publisher-returnstruecorrelati…

Word Embedding

One-hot-encoding 缺点 1.向量维度和向量个数很大&#xff0c;假设有1w个token的话&#xff0c;向量个数和维度就都是1w 2. 语义相近的词的向量并不相似 Word Embedding 核心思想&#xff1a;可以通过上下文理解单词的语义 predection-based方法 使用前一个单词预测下一个…

【机器学习】信息量、香农熵、信息增益

这节可以搭配 【机器学习】Logistic回归&#xff08;重新整理&#xff09;信息量&#xff08;信息&#xff09;信息量公式的推理过程 香农熵信息增益 【机器学习】Logistic回归&#xff08;重新整理&#xff09; B站视频&#xff1a;“交叉熵”如何做损失函数&#xff1f;打包…

Linux一学就会——编写自己的shell

编写自己的shell 进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行…

视觉震撼的数据可视化示例

众所周知&#xff0c;数据可以非常强大——当你真正理解它告诉你什么时。 数据和信息可视化(数据可视化或信息可视化)是对大量复杂的定量、定性数据、信息进行设计和创建易于沟通、易于理解的图形或视觉表示的实践&#xff0c;在静态、动态或交互式视觉项目的帮助下&#xff0…

存储网络架构——DAS、NAS、SAN、分布式组网架构

目录 DAS直连式存储 NAS网络附加存储 SAN存储 存储区域网络 分布式存储组网 DAS直连式存储 DAS遇到的挑战 NAS网络附加存储 向主机提供文件服务&#xff1b;文件系统由存储设备维护&#xff0c;用户访问文件系统&#xff0c;不直接访问底层存储 拥有所有主机上文件与底层存储空…

JS案例分析-某国际音x-tt-params参数分析

今天我们要分析的网站是&#xff1a;https://www.tiktok.com/selenagomez?langen&#xff0c;参数名字叫x-tt-params。 先来抓个包 这个接口是用户视频列表url&#xff0c;参数叫x-tt-params&#xff0c;该接口中还有其他参数像msToken&#xff0c;X-Bogus&#xff0c; _sig…

【51单片机】点亮一个LED灯(看开发板原理图十分重要)

&#x1f38a;专栏【51单片机】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【The Right Path】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f354;基础内容 &#x1f3f3…

项目集的定义及管理

一、什么是项目集 项目集是相互关联且被协调管理的项目、子项目集和项目集活动&#xff0c;以便获得分别管理所无法获 得的效益。 以项目集的形式管理项目、子项目集及项目集活动能确保项目集组件的战略和工作计划根据各组 件的成果做出相应调整&#xff0c;或者按照发起组织的…