SpringBoot+shardingsphere实现按月分表功能

SpringBoot+shardingsphere实现按月分表功能

文章目录


前言

ShardingSphere 是一套开源的分布式数据库中间件解决方案,旨在简化数据库分片、读写分离、分布式事务等复杂场景的管理。它由 Apache 软件基金会支持,广泛应用于需要处理大规模数据的系统中


一、ShardingSphere 是什么?

主要是为了防止一张表的数据量过大而设计的,数据库本身就支持,但是由于自行设计需要满足跨表查询,事务一致性,分页聚合等很多的复杂场景,还需要很多的配套监控,设计,扩容等方案,所以总体来说是一个任务量很大的任务,故而这里采用ShardingSphere 来实现。

二、使用步骤

1.引入库

<!-- 分库分表 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.0</version>
        </dependency>

2.环境配置+Mysql表

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  `gender` tinyint(4) NOT NULL COMMENT '0:男 1:女',
  `createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1890651990057906179 DEFAULT CHARSET=utf8mb4;
# 配置服务器端口
server:
  port: 9999

# Spring框架下的ShardingSphere配置
spring:
  shardingsphere:
    # 模式配置,设置为独立模式
    mode:
      type: Standalone
    # 数据源配置
    datasource:
      # 定义数据源名称
      names: ds0
      # 数据源ds0的具体配置
      ds0:
        # 数据源类型为HikariCP
        type: com.zaxxer.hikari.HikariDataSource
        # 数据库驱动类名称
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 数据库连接URL,包含时区设置
        jdbc-url: jdbc:mysql://localhost:3306/sharding_db?serverTimezone=Asia/Shanghai
        # 数据库用户名
        username: root
        # 数据库密码
        password: root
    # 规则配置
    rules:
      # 分片规则配置
      sharding:
        # 定义分片的表
        tables:
          user:
            # 只配置基础表,其他表会动态创建
            actual-data-nodes: ds0.user,ds0.user_202401,ds0.user_202402,ds0.user_202403,ds0.user_202404,ds0.user_202405
            table-strategy:
              standard:
                sharding-column: createtime
                sharding-algorithm-name: user_inline
            # 添加主键生成策略
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
        sharding-algorithms:
          user_inline:
            type: CLASS_BASED
            props:
              strategy: standard
              algorithmClassName: com.hhh.sharding.standa.UserShardingAlgorithm
        # 配置主键生成器
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 123
        # 添加默认分片策略
        default-sharding-column: gender
    # 属性配置
    props:
      # 是否显示SQL语句
      sql-show: true

# MyBatis-Plus配置
mybatis-plus:
  configuration:
    # 不将下划线转换为驼峰命名
    map-underscore-to-camel-case: false
    # 使用标准输出日志实现
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    enable-sql-runner: true

这里有一个注意事项,那就是id一定要使用bigint使用雪花策略算法来实现,至于为什么这样呢,是为了防止分表的主键id一致的情况,这里首先推荐就是使用mybatisPlus来实现,因为他天然支持雪花算法

3.分表代码实现

主要是两个文件一个是自己实现分表算法的UserShardingAlgorithm文件

package com.hhh.sharding.standa;

import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.hhh.sharding.domain.User;
import com.hhh.sharding.service.UserService;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
import org.apache.shardingsphere.infra.metadata.database.rule.ShardingSphereRuleMetaData;
import org.apache.shardingsphere.mode.manager.ContextManager;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.rule.ShardingRule;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class DynamicShardingManager {

    @Resource
    private DataSource dataSource;

    @Resource
    private UserService userService;


    private static final String LOGIC_TABLE_NAME = "user";

    private static final String DATABASE_NAME = "sharding_db"; // 配置文件中的数据库名称

    @PostConstruct
    public void initialize() {
        log.info("初始化动态分表配置...");
        updateShardingTableNodes();
    }


    /**
     * 获取所有用户相关的表名
     * 此方法旨在动态地收集所有用户表的表名,以支持可能存在的不同性别用户表
     * 如果无法获取动态表名或列表为空,则默认返回包含单一的默认用户表名"user"
     *
     * @return 包含所有用户表名的集合
     */
    private Set<String> fetchAllUserTableNames() {
        //获取所有动态化表名
        Set<String> tableNames = new HashSet<>();
        try {
            // 获取用户列表
            List<User> users = userService.list();
            // 如果用户列表不为空,则映射每个用户到对应的表名,并收集到集合中
            if (users != null) {
                tableNames = users.stream()
                        .map(user -> "user_" + user.getGender())
                        .collect(Collectors.toSet());
            }
            // 确保至少包含默认表
            tableNames.add("user");
        } catch (Exception e) {
            // 记录获取表名时发生的错误
            log.error("获取所有动态化表名失败", e);
            // 发生异常时至少返回默认表
            tableNames.add("user");
        }
        // 返回收集到的表名集合
        return tableNames;
    }


    /**
     * 动态更新分片表节点配置
     * 
     * 本方法旨在根据当前的用户表名称,动态地更新分片表的节点配置
     * 它首先获取所有用户表的名称,然后构建新的分片表节点配置,并尝试更新到数据库的元数据中
     */
    private void updateShardingTableNodes() {
        try {
            // 获取所有用户表的名称
            Set<String> tableNames = fetchAllUserTableNames();
            if (tableNames.isEmpty()) {
                // 如果未获取到任何表名,则使用默认的表配置
                log.warn("未获取到任何表名,将使用默认表配置");
                tableNames.add("user");
            }
    
            // 确保包含所有可能的表
            tableNames.add("user");
            tableNames.add("user_0");
            tableNames.add("user_1");
    
            // 构建新的分片表节点配置
            String newActualDataNodes = tableNames.stream()
                    .distinct()
                    .map(tableName -> "ds0." + tableName)
                    .collect(Collectors.joining(","));
            log.info("动态分表 actual-data-nodes 配置: {}", newActualDataNodes);
    
            // 获取 ContextManager 实例
            ContextManager contextManager = getContextManager();
            if (contextManager == null) {
                log.error("获取 ContextManager 失败");
                return;
            }
    
            // 获取 MetaDataContexts 实例
            var metaDataContexts = contextManager.getMetaDataContexts();
            if (metaDataContexts == null) {
                log.error("获取 MetaDataContexts 失败");
                return;
            }
    
            // 获取 MetaData 实例
            var metaData = metaDataContexts.getMetaData();
            if (metaData == null) {
                log.error("获取 MetaData 失败");
                return;
            }
    
            // 检查数据库是否存在
            var databases = metaData.getDatabases();
            if (databases == null || !databases.containsKey(DATABASE_NAME)) {
                log.error("数据库 {} 不存在", DATABASE_NAME);
                return;
            }
    
            // 获取 ShardingSphere 的规则元数据
            ShardingSphereRuleMetaData ruleMetaData = databases.get(DATABASE_NAME).getRuleMetaData();
            if (ruleMetaData == null) {
                log.error("获取规则元数据失败");
                return;
            }
    
            // 查找 ShardingRule
            Optional<ShardingRule> shardingRule = ruleMetaData.findSingleRule(ShardingRule.class);
            if (shardingRule.isPresent()) {
                // 获取分片规则配置
                ShardingRuleConfiguration ruleConfig = (ShardingRuleConfiguration) shardingRule.get().getConfiguration();
                if (ruleConfig.getTables() == null || ruleConfig.getTables().isEmpty()) {
                    log.error("分片规则配置为空");
                    return;
                }
    
                // 更新分片表规则配置
                List<ShardingTableRuleConfiguration> updatedRules = ruleConfig.getTables()
                        .stream()
                        .map(oldTableRule -> {
                            if (LOGIC_TABLE_NAME.equals(oldTableRule.getLogicTable())) {
                                ShardingTableRuleConfiguration newTableRuleConfig = new ShardingTableRuleConfiguration(LOGIC_TABLE_NAME, newActualDataNodes);
                                newTableRuleConfig.setDatabaseShardingStrategy(oldTableRule.getDatabaseShardingStrategy());
                                newTableRuleConfig.setTableShardingStrategy(oldTableRule.getTableShardingStrategy());
                                newTableRuleConfig.setKeyGenerateStrategy(oldTableRule.getKeyGenerateStrategy());
                                newTableRuleConfig.setAuditStrategy(oldTableRule.getAuditStrategy());
                                return newTableRuleConfig;
                            }
                            return oldTableRule;
                        })
                        .collect(Collectors.toList());
                ruleConfig.setTables(updatedRules);
                
                // 尝试更新分片规则配置
                try {
                    contextManager.alterRuleConfiguration(DATABASE_NAME, Collections.singleton(ruleConfig));
                    contextManager.reloadDatabase(DATABASE_NAME);
                    log.info("动态分表规则更新成功!");
                } catch (Exception e) {
                    log.error("更新分片规则失败", e);
                }
            } else {
                log.error("未找到 ShardingSphere 的分片规则配置,动态分表更新失败。");
            }
        } catch (Exception e) {
            log.error("更新分片规则时发生异常", e);
        }
    }

    /**
     * 获取 ShardingSphere ContextManager
     */
    private ContextManager getContextManager() {
        try {
            if (dataSource == null) {
                log.error("数据源未注入");
                return null;
            }
            
            var connection = dataSource.getConnection();
            if (connection == null) {
                log.error("获取数据库连接失败");
                return null;
            }

            ShardingSphereConnection shardingConnection = connection.unwrap(ShardingSphereConnection.class);
            if (shardingConnection == null) {
                log.error("无法获取 ShardingSphereConnection");
                connection.close();
                return null;
            }

            ContextManager contextManager = shardingConnection.getContextManager();
            connection.close();
            return contextManager;
        } catch (SQLException e) {
            log.error("获取 ShardingSphere ContextManager 失败", e);
            return null;
        }
    }

    /**
     * 根据用户信息创建用户表
     * 表名基于用户创建时间生成,格式为:LOGIC_TABLE_NAME_YYYYMM
     * 如果表已存在,则不进行创建操作
     * 
     * @param user 用户对象,包含用户创建时间等信息
     */
    public void createUserTable(User user) {
        // 获取用户创建时间
        Date createTime = user.getCreatetime();
        // 创建日期格式化对象,用于生成表名
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
        // 生成完整的表名
        String tableName = LOGIC_TABLE_NAME + "_" + dateFormat.format(createTime);
        
        try {
            // 首先检查表是否已存在
            String checkTableSql = "SHOW TABLES LIKE '" + tableName + "'";
            List<Map<String, Object>> tables = SqlRunner.db().selectList(checkTableSql);
            
            // 如果表存在,记录日志并结束方法
            if (tables != null && !tables.isEmpty()) {
                log.info("表 {} 已经存在,无需创建", tableName);
                return;
            }
            
            // 创建表
            String createTableSql = "CREATE TABLE IF NOT EXISTS " + tableName + " LIKE user";
            log.info("开始创建表,SQL: {}", createTableSql);
            
            SqlRunner.db().update(createTableSql);
            log.info("表 {} 创建成功", tableName);
            
            // 更新分片配置
            updateShardingTableNodes();
        } catch (Exception e) {
            log.error("创建分表 {} 失败: {}", tableName, e.getMessage(), e);
            // 检查异常消息,如果表已存在,则记录日志并结束方法
            if (e.getMessage() != null && e.getMessage().contains("already exists")) {
                log.info("表 {} 已经存在,继续处理", tableName);
                return;
            }
            // 如果异常与表已存在无关,则抛出运行时异常
            throw new RuntimeException("创建分表失败: " + e.getMessage(), e);
        }
    }

}
package com.hhh.sharding.standa;

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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Properties;

@Slf4j
public class UserShardingAlgorithm implements StandardShardingAlgorithm<Date> {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMM");

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> preciseShardingValue) {
        Date createTime = preciseShardingValue.getValue();
        String logicTableName = preciseShardingValue.getLogicTableName();
        
        log.info("分片算法执行 - 可用目标表: {}, 分片值: {}, 逻辑表名: {}", 
                availableTargetNames, createTime, logicTableName);
        
        if (createTime == null) {
            log.info("createTime为空,返回逻辑表名: {}", logicTableName);
            return logicTableName;
        }
        
        // 根据 createTime 动态生成分表名
        String suffix = DATE_FORMAT.format(createTime);
        String realTableName = "user_" + suffix;
        log.info("计算得到的实际表名: {}", realTableName);
        
        if (availableTargetNames.contains(realTableName)) {
            log.info("找到匹配的目标表: {}", realTableName);
            return realTableName;
        } else {
            log.warn("未找到匹配的目标表,返回逻辑表名: {}", logicTableName);
            return logicTableName;
        }
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {
        return new ArrayList<>();
    }

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

    @Override
    public void init(Properties properties) {
        // 可以添加初始化逻辑
    }
}

 4.测试用例

package com.hhh.sharding.controller;


import cn.hutool.core.util.RandomUtil;
import com.hhh.sharding.domain.User;
import com.hhh.sharding.service.UserService;
import com.hhh.sharding.standa.DynamicShardingManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.text.SimpleDateFormat;

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @Resource
    private DynamicShardingManager dynamicShardingManager;

    @GetMapping("/add")
    public Boolean user() {
        // 创建一些2024年的随机日期
        Date[] dates = {
            getDate("2024-01-15"),
            getDate("2024-02-20"),
            getDate("2024-03-10"),
            getDate("2024-04-05"),
            getDate("2024-05-25")
        };
        
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setUsername(generateRandomUsername());
            user.setPassword("123456");
            user.setGender(RandomUtil.randomInt(2));
            // 随机选择一个2024年的日期
            Date randomDate = dates[RandomUtil.randomInt(dates.length)];
            user.setCreatetime(randomDate);
            user.setUpdatetime(randomDate);
            //这里每一次新增数据的时候去判断是否要创建出来当月的数据表,这张表一定要在    
            //application.yml中的actual-data-nodes中去添加
            dynamicShardingManager.createUserTable(user);
            userService.save(user);
        }
        return true;
    }

    private Date getDate(String dateStr) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
        } catch (Exception e) {
            return new Date();
        }
    }

    // 生成10位随机数字的用户名
    private String generateRandomUsername() {
        return RandomUtil.randomNumbers(10);  // 生成10位数字
    }

    @GetMapping("/all")
    public List<User> all() {
        return userService.list();
    }
}

 5.测试结果

新增数据

 查询数据

 数据库情况

 数据库表数据展示


总结

由于公司有一个需求那就是按月来分表展示数据,看了好多人的博客都没有效果,最终三天得以解决这个功能,故而写下此博客,希望可以真正的帮助到你

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

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

相关文章

大模型训练为什么依赖GPU

近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;特别是深度学习领域的进步&#xff0c;大模型的训练逐渐成为研究和工业界的热点。作为大模型训练中的核心硬件&#xff0c;GPU&#xff08;图形处理单元&#xff09;扮演了至关重要的角色。那么&#xff0c;为什么大模…

SQL布尔盲注+时间盲注

1.布尔盲注 双重for循环 import requestsurl http://127.0.0.1/sqli-labs-master/Less-8/index.phpdef database_name():datebasename for i in range(1, 9): # 假设数据库名称最多8个字符for j in range(32, 128): # ascii 可见字符范围从32到127payload f"?id1 A…

收银系统源码开发指南:PHP + Flutter + Uniapp 全栈方案

收银系统一般涵盖了收银POS端、线上商城端和管理端&#xff0c;技术栈涉及PHP、Flutter和Uniapp。为了确保系统的稳定运行和持续发展&#xff0c;在开发和运营过程中需要重点关注以下几个方面&#xff1a; 一、系统架构与性能优化 模块化设计: 将系统拆分为独立的模块&#xf…

springCloud-2021.0.9 之 GateWay 示例

文章目录 前言springCloud-2021.0.9 之 GateWay 示例1. GateWay 官网2. GateWay 三个关键名称3. GateWay 工作原理的高级概述4. 示例4.1. POM4.2. 启动类4.3. 过滤器4.4. 配置 5. 启动/测试 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收…

Vue.js 在低代码开发平台中的应用与优化

Vue.js 在低代码开发平台中的应用与优化 在数字化转型的进程中&#xff0c;低代码开发平台成为了企业快速构建应用的得力助手。而 Vue.js 作为一款广受欢迎的前端框架&#xff0c;在低代码开发平台中发挥着举足轻重的作用。它不仅提升了开发效率&#xff0c;还优化了应用的用户…

大模型Deepseek的使用_基于阿里云百炼和Chatbox

目录 前言1. 云服务商2. ChatBox参考 前言 上篇博文中探索了&#xff08;本地&#xff09;部署大语言模型&#xff0c;适合微调、数据高隐私性等场景。随着Deepseek-R1的发布&#xff0c;大语言模型的可及性得到极大提升&#xff0c;应用场景不断增加&#xff0c;对高可用的方…

Android设备 网络安全检测

八、网络与安全机制 6.1 网络框架对比 volley&#xff1a; 功能 基于HttpUrlConnection;封装了UIL图片加载框架&#xff0c;支持图片加载;网络请求的排序、优先级处理缓存;多级别取消请求;Activity和生命周期的联动&#xff08;Activity结束生命周期同时取消所有网络请求 …

[免费]SpringBoot公益众筹爱心捐赠系统【论文+源码+SQL脚本】

大家好&#xff0c;我是老师&#xff0c;看到一个不错的SpringBoot公益众筹爱心捐赠系统&#xff0c;分享下哈。 项目介绍 公益捐助平台的发展背景可以追溯到几十年前&#xff0c;当时人们已经开始通过各种渠道进行公益捐助。随着互联网的普及&#xff0c;本文旨在探讨公益事业…

zyNo.23

SQL注入漏洞 1.SQL语句基础知识 一个数据库由多个表空间组成&#xff0c;sql注入关系到关系型数据库&#xff0c;常见的关系型数据库有MySQL,Postgres,SQLServer,Oracle等 以Mysql为例&#xff0c;输入 mysql-u用户名-p密码 即可登录到MySQL交互式命令行界面。 既然是…

基于大数据的北京市天气数据分析系统的设计与实现

【Flask】基于Flask的北京市天气数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用Python和Flask框架&#xff0c;结合Pandas、NumPy等数据处理库及Echarts进…

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题

【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题问题描述:解决方法方法一:手动中断并重启下载方法二:使用 Bash 脚本自动化下载在…

深入解析操作系统控制台:阿里云Alibaba Cloud Linux(Alinux)的运维利器

作为一位个人开发者兼产品经理&#xff0c;我的工作日常紧密围绕着云资源的运维和管理。在这个过程中&#xff0c;操作系统扮演了至关重要的角色&#xff0c;而操作系统控制台则成为了我们进行系统管理的得力助手。本文将详细介绍阿里云的Alibaba Cloud Linux操作系统控制台的功…

一场因软件技术窃取引发的法律风暴

根据最高人民法院(2020)最高法知民终1101号真实案例改编 第一章&#xff1a;创新的种子 2004年&#xff0c;北京明远软件设计研究院&#xff08;后更名为“明远软件股份有限公司”&#xff0c;以下简称“明远”&#xff09;的办公室里&#xff0c;创始人杨原和技术总监何晨亮…

Python的那些事第二十二篇:基于 Python 的 Django 框架在 Web 开发中的应用研究

基于 Python 的 Django 框架在 Web 开发中的应用研究 摘要 Django 是一个基于 Python 的高级 Web 框架,以其开发效率高、安全性和可扩展性强等特点被广泛应用于现代 Web 开发。本文首先介绍了 Django 的基本架构和核心特性,然后通过一个实际的 Web 开发项目案例,展示了 Dj…

STM32之SG90舵机控制

目录 前言&#xff1a; 一、硬件准备与接线 1.1 硬件清单 1.2 接线 二、 SG90舵机简介 1.1 外观 1.2 基本参数 1.3 引脚说明 1.4 控制原理 1.5 特点 1.6 常见问题 三、 单片机简介 四、 程序设计 4.1 定时器配置 4.2 角度控制函数 4.3 主函数调用 五、 总结 …

【动态规划篇】:当回文串遇上动态规划--如何用二维DP“折叠”字符串?

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;动态规划篇–CSDN博客 文章目录 一.回文串类DP核心思想&#xff08;判断所有子串是否是回文…

DeepSeek正重构具身大模型和人形机器人赛道!

中国人工智能公司DeepSeek&#xff08;深度求索&#xff09;以“低成本、高效率、强开放”的研发范式横空出世&#xff0c;火遍并震撼全球科技圈&#xff1b;DeepSeek展现出来的核心竞争力&#xff0c;除了低成本及推理能力&#xff0c;更重要的是开源模型能力追赶上了最新的闭…

网络工程师 (39)常见广域网技术

一、HDLC 前言 HDLC&#xff08;High-level Data Link Control&#xff0c;高级数据链路控制&#xff09;是一种面向比特的链路层协议。 &#xff08;一&#xff09;定义与历史背景 HDLC是由国际电信联盟&#xff08;ITU&#xff09;标准化的&#xff0c;它基于IBM公司早期的同…

制作Ubuntu根文件

系列文章目录 Linux内核学习 Linux 知识&#xff08;1&#xff09; Linux 知识&#xff08;2&#xff09; WSL Ubuntu QEMU 虚拟机 Linux 调试视频 PCIe 与 USB 的补充知识 vscode 使用说明 树莓派 4B 指南 设备驱动畅想 Linux内核子系统 Linux 文件系统挂载 QEMU 通过网络实现…

SpringMVC详解

文章目录 1 什么是MVC 1.1 MVC设计思想1.2 Spring MVC 2 SpringMVC快速入门3 SpringMVC处理请求 3.1 请求分类及处理方式 3.1.1 静态请求3.1.2 动态请求 3.2 处理静态请求 3.2.1 处理html文件请求3.2.2 处理图片等请求 3.3 处理动态请求 3.3.1 注解说明3.3.2 示例 3.4 常见问题…