SpringBoot整合Milvus向量数据库

SpringBoot 整合 Milvus 向量数据库实战

从环境搭建到代码落地,一文搞懂如何在 SpringBoot 项目中集成 Milvus 向量数据库,构建自己的 RAG(检索增强生成)知识库系统。


一、前言

在 AI 大模型时代,向量数据库 已成为构建智能应用的核心基础设施。无论是知识库问答(RAG)、语义搜索、图片检索还是推荐系统,都离不开向量数据库的身影。

本文将基于一个真实的 SpringBoot 项目,手把手带你完成 Milvus 向量数据库 的集成,涵盖:

  • Milvus 服务端安装与配置
  • BGE-M3 本地 Embedding 模型部署(CPU/GPU/在线三种方案)
  • SpringBoot 中 Milvus 客户端的配置与 CRUD 操作
  • 一个完整的 RAG 知识库查询流程

二、技术栈概览

组件 版本/选型 说明
SpringBoot 2.0.0.RELEASE 项目基础框架
JDK 1.8 Java 运行环境
Milvus 2.6.1 向量数据库(服务端)
milvus-sdk-java 2.6.1 Java 客户端 SDK
BGE-M3 ONNX 格式 本地 Embedding 模型
ONNX Runtime 1.23.2 模型推理引擎
LangChain4j 0.31.0 文档分割工具
Python 3.10.10 用于 pymilvus 调试管理

三、环境准备

3.1 Milvus 服务端安装(Linux)

Milvus 支持 Docker 部署和 RPM 包安装两种方式。生产环境推荐使用 RPM 包方式,更稳定且便于管理。

下载 RPM 包

Milvus GitHub Releases 下载对应版本的 RPM 包:

bash 复制代码
# 以 milvus 2.6.9 为例(推荐使用与 SDK 匹配的 2.6.x 版本)
wget https://github.com/milvus-io/milvus/releases/download/v2.6.1/milvus_2.6.9-1_amd64.rpm

安装与启动

bash 复制代码
# 安装 RPM 包
yum install -y ./milvus_2.6.9-1_amd64.rpm

# 验证安装
rpm -qa | grep milvus

# 启动 Milvus 服务
systemctl start milvus

# 查看服务状态
systemctl status milvus

# 设置开机自启
systemctl enable milvus

默认启动后,Milvus 监听 19530 端口,支持 gRPC 和 HTTP 两种协议。

3.2 管理工具 Attu

Attu 是 Zilliz 官方出品的 Milvus 可视化管理工具,强烈推荐安装:

  • 支持 Collection 的创建、查看、删除
  • 可视化数据浏览与查询
  • 索引管理

直接下载对应平台的客户端即可使用,连接时填写 Milvus 服务地址和端口(默认 host:19530)。

3.3 Python 客户端(可选)

用于日常调试和数据管理:

bash 复制代码
# Python 版本要求 3.10.10
pip install pymilvus==2.4.6
pip install milvus

3.4 本地 Embedding 模型 BGE-M3

向量化是向量数据库的核心前置步骤。本项目使用 BGE-M3 模型,支持本地 CPU 推理和 GPU 推理两种模式。

模型下载

bge-m3-onnx 下载 ONNX 格式的模型文件,包含两个核心文件:

  • bge_m3_tokenizer.onnx --- 分词器模型
  • bge_m3_model.onnx --- 主模型(输出 1024 维向量)

将模型文件放置到本地目录(如 I:/bgem3/onnx),后续 SpringBoot 会通过 ONNX Runtime 加载。

3.5 Ollama 部署(备选方案)

如果不想在 Java 端加载 ONNX 模型,也可以通过 Ollama 提供 embedding 服务:

安装 Ollama

cnb.cool/hex/ollama 下载 Windows 客户端,或使用 Docker 部署。

配置镜像加速

编辑 %USERPROFILE%\.ollama\config.json

json 复制代码
{
  "registry": {
    "mirrors": {
      "registry.ollama.ai": "https://registry.ollama.ai"
    }
  }
}

拉取 BGE-M3 模型

bash 复制代码
ollama pull bge-m3
ollama list

启动后 Ollama 会在本地 11434 端口提供 API 服务,可作为在线 embedding 的备选方案。


四、项目配置

4.1 Maven 依赖

pom.xml 中引入核心依赖:

xml 复制代码
<!-- Milvus Java SDK -->
<dependency>
    <groupId>io.milvus</groupId>
    <artifactId>milvus-sdk-java</artifactId>
    <version>2.6.1</version>
</dependency>

<!-- ONNX Runtime(本地 CPU 推理) -->
<dependency>
    <groupId>com.microsoft.onnxruntime</groupId>
    <artifactId>onnxruntime_gpu</artifactId>
    <version>1.23.2</version>
</dependency>

<!-- ONNX Runtime 扩展(分词器) -->
<dependency>
    <groupId>com.microsoft.onnxruntime</groupId>
    <artifactId>onnxruntime-extensions</artifactId>
    <version>0.13.0</version>
</dependency>

<!-- LangChain4j(文档分割) -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j</artifactId>
    <version>0.31.0</version>
</dependency>

<!-- FastJSON(JSON 处理) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

版本说明 :Milvus SDK 版本 2.6.1 与服务端 2.6.x 保持一致,使用 V2 版 API(MilvusClientV2),代码风格更简洁,推荐使用。

4.2 配置文件

application.yml 中 Milvus 相关配置:

yaml 复制代码
milvus:
  # 连接协议:grpc 或 http
  proxy: grpc
  # Milvus 服务 IP
  host: 192.168.1.250
  # Milvus 服务端口
  port: 19530
  # 数据库名(默认 default)
  database: default
  # Embedding 硬件模式:cpu / gpu / online
  hardware: cpu
  # ONNX 模型本地路径
  onnxpath: I:/bgem3/onnx
  # 在线 Embedding 模型名
  modelname: bge-m3-yidong

五、核心代码实现

5.1 配置类 --- MilvusConfig

application.yml 中的 Milvus 配置映射为 Java Bean:

java 复制代码
package com.haopan.ai.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "milvus")
public class MilvusConfig {
    // 连接协议:grpc / http
    private String proxy;
    // Milvus 服务 IP
    private String host;
    // Milvus 端口
    private Integer port;
    // 数据库名
    private String database;
    // 硬件模式:cpu / gpu / online
    private String hardware;
    // ONNX 模型路径
    private String onnxpath;
    // 在线 embedding 模型名称
    private String modelname;

    public MilvusConfig() {
        hardware = "cpu";   // 默认 CPU 模式
    }

    // getter / setter 略...
}

5.2 MilvusClient Bean 注册

Application.java 启动类中创建 MilvusClientV2 Bean:

java 复制代码
@Bean(destroyMethod = "close")
@Lazy
public MilvusClientV2 milvusClient(MilvusConfig milvusConfig) {
    ConnectConfig connectConfig = ConnectConfig.builder()
            .uri(milvusConfig.getProxy() + "://" 
                 + milvusConfig.getHost() + ":" 
                 + milvusConfig.getPort())
            .dbName(milvusConfig.getDatabase())
            .build();
    return new MilvusClientV2(connectConfig);
}

关键点说明:

  • @Lazy 延迟初始化,避免启动时连接失败导致项目无法启动
  • destroyMethod = "close" 确保应用关闭时释放连接
  • URI 格式:grpc://192.168.1.250:19530

5.3 Embedding 服务(多模式设计)

项目设计了 策略模式 解耦 Embedding 的实现,通过 milvus.hardware 配置自动切换:

java 复制代码
// 接口定义
public interface EmbeddingService {
    float[] embed(String text);
}
模式一:CPU 本地推理(默认)
java 复制代码
@Service
@ConditionalOnProperty(name = "milvus.hardware", 
    havingValue = "cpu", matchIfMissing = true)
public class CPUEmbeddingService implements EmbeddingService {

    @Autowired
    private MilvusConfig milvusConfig;

    @Override
    public float[] embed(String text) {
        try {
            M3Embedder embedder = M3Embedder
                .getInstance(milvusConfig.getOnnxpath());
            M3EmbeddingOutput result = embedder.generateEmbeddings(text);
            return result.getDenseEmbedding();   // 返回 1024 维向量
        } catch (Exception e) {
            throw new RuntimeException("Local embedding failed", e);
        }
    }
}
模式二:GPU 本地推理
java 复制代码
@Service
@ConditionalOnProperty(name = "milvus.hardware", havingValue = "gpu")
public class GPUEmbeddingService implements EmbeddingService {
    // 与 CPU 模式相同,底层 ONNX Runtime 自动使用 CUDA 加速
    // 需配置 CUDA 环境变量和 onnxruntime_gpu 依赖
}
模式三:在线 API 调用
java 复制代码
@Service
@ConditionalOnProperty(name = "milvus.hardware", havingValue = "online")
public class OnlineEmbeddingService implements EmbeddingService {

    @Autowired
    private AiProperties aiProperties;
    @Autowired
    private MilvusConfig milvusConfig;

    @Override
    public float[] embed(String text) {
        // 从配置中获取模型信息(支持 OpenAI 兼容 API)
        ModelConfig config = aiProperties.getConfig(
            milvusConfig.getModelname());
        String requestUrl = config.getUrl() + "/embeddings";

        // 构建请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("model", config.getModel());
        requestBody.put("input", text);

        // 通过 WebClient 调用 /embeddings 接口
        String result = webClient.post()
                .uri(requestUrl)
                .headers(httpHeaders -> httpHeaders.addAll(headers))
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromObject(requestBody))
                .retrieve()
                .bodyToMono(String.class)
                .block();

        // 解析返回的向量
        JSONObject obj = JSONObject.parseObject(result);
        JSONArray embeddingArray = obj.getJSONArray("data")
                .getJSONObject(0)
                .getJSONArray("embedding");
        float[] queryVector = new float[embeddingArray.size()];
        for (int i = 0; i < embeddingArray.size(); i++) {
            queryVector[i] = embeddingArray.getFloat(i);
        }
        return queryVector;
    }
}

三种模式对比:

模式 优点 缺点 适用场景
CPU 本地 无网络依赖,数据安全 推理速度较慢 小数据量、内网环境
GPU 本地 推理速度极快 需 GPU 硬件、CUDA 环境 大批量数据处理
在线 API 免部署模型,跨平台 依赖网络,有费用 快速验证、云端部署

5.4 核心服务 --- VectorService

VectorService 封装了 Milvus 的全部 CRUD 操作,是整个系统的核心。

5.4.1 向量查询(语义搜索)
java 复制代码
public List<String> query(String queryText, 
        List<String> collectNameList, int limit) {
    
    List<String> queryResult = new ArrayList<>();
    
    // 1. 将查询文本转为向量
    float[] queryVector = getEmbedding(queryText);
    FloatVec floatVector = new FloatVec(queryVector);
    
    // 2. 设置搜索参数(内积相似度)
    JSONObject searchParams = new JSONObject();
    searchParams.put("metric_type", "IP");  // Inner Product
    
    // 3. 遍历多个 Collection 进行搜索
    for (String collectionName : collectNameList) {
        SearchReq searchReq = SearchReq.builder()
                .collectionName(collectionName)
                .data(Collections.singletonList(floatVector))
                .annsField("embeddings")           // 向量字段
                .outputFields(Collections.singletonList("text"))  // 返回字段
                .searchParams(searchParams)
                .topK(limit)                        // 返回 Top-K
                .consistencyLevel(ConsistencyLevel.STRONG)
                .build();
        
        SearchResp searchResp = milvusClient.search(searchReq);
        
        // 4. 提取搜索结果
        if (searchResp.getSearchResults() != null 
                && !searchResp.getSearchResults().isEmpty()) {
            List<SearchResp.SearchResult> searchResultList = 
                searchResp.getSearchResults().get(0);
            for (SearchResp.SearchResult searchItem : searchResultList) {
                String text = String.valueOf(
                    searchItem.getEntity().get("text"));
                queryResult.add(text);
            }
        }
    }
    return queryResult;
}

搜索流程:

复制代码
用户查询文本  →  Embedding 向量化  →  Milvus 相似度搜索  →  返回 Top-K 文本
5.4.2 文档分块

使用 LangChain4j 的 DocumentByCharacterSplitter 进行智能分块:

java 复制代码
public List<String> splitText(String content) {
    // 每块 800 字符,块间重叠 20 字符(保留上下文连贯性)
    DocumentByCharacterSplitter charSplitter = 
        new DocumentByCharacterSplitter(800, 20);
    
    Document document = Document.from(content);
    List<TextSegment> segments = charSplitter.split(document);
    
    return segments.stream()
            .map(TextSegment::text)
            .collect(Collectors.toList());
}
5.4.3 数据插入
java 复制代码
public void insert(List<String> contentList, String source, 
        String user_id, String collection_name) {
    
    List<JsonObject> dataList = new ArrayList<>();
    for (int i = 0; i < contentList.size(); i++) {
        String content = contentList.get(i);
        float[] insertVector = getEmbedding(content);
        
        JsonObject dataItem = new JsonObject();
        dataItem.addProperty("text", content);
        dataItem.addProperty("source", source);
        dataItem.addProperty("uid", user_id);
        dataItem.addProperty("page", i);
        
        // 将 float[] 转为 JSON 数组
        Gson gson = new Gson();
        JsonArray jsonArray = gson.toJsonTree(insertVector, 
            new TypeToken<float[]>(){}.getType()).getAsJsonArray();
        dataItem.add("embeddings", jsonArray);
        dataList.add(dataItem);
    }
    
    // 自动创建 Collection(如果不存在)
    if (!checkCollectionExist(collection_name)) {
        addCollection(collection_name);
    }
    
    // 执行插入
    InsertReq insertReq = InsertReq.builder()
            .data(dataList)
            .collectionName(collection_name)
            .build();
    milvusClient.insert(insertReq);
}
5.4.4 Collection 创建
java 复制代码
public void addCollection(String collection_name) {
    if (!checkCollectionExist(collection_name)) {
        // 定义字段结构
        List<CreateCollectionReq.FieldSchema> fieldSchemaList = new ArrayList<>();
        
        // 主键(自增)
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("id")
                .dataType(DataType.Int64)
                .isPrimaryKey(true)
                .autoID(true)
                .build());
        
        // 用户 ID
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("uid")
                .dataType(DataType.VarChar)
                .maxLength(500)
                .build());
        
        // 文本内容
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("text")
                .dataType(DataType.VarChar)
                .maxLength(50000)
                .build());
        
        // 页码
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("page")
                .dataType(DataType.Int16)
                .build());
        
        // 向量字段(1024 维)
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("embeddings")
                .dataType(DataType.FloatVector)
                .dimension(1024)     // BGE-M3 输出 1024 维
                .build());
        
        // 文档来源
        fieldSchemaList.add(CreateCollectionReq.FieldSchema.builder()
                .name("source")
                .dataType(DataType.VarChar)
                .maxLength(5000)
                .build());
        
        // 构建 Schema
        CreateCollectionReq.CollectionSchema collectionSchema = 
            CreateCollectionReq.CollectionSchema.builder()
                .fieldSchemaList(fieldSchemaList)
                .build();
        
        // 索引配置(IVF_FLAT + 内积相似度)
        HashMap indexExtraParam = new HashMap();
        indexExtraParam.put("nlist", 16384);
        IndexParam indexParam = IndexParam.builder()
                .fieldName("embeddings")
                .metricType(IndexParam.MetricType.IP)
                .indexType(IndexParam.IndexType.IVF_FLAT)
                .extraParams(indexExtraParam)
                .build();
        
        // 创建 Collection
        CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()
                .collectionName(collection_name)
                .description("database")
                .autoID(true)
                .consistencyLevel(ConsistencyLevel.BOUNDED)
                .collectionSchema(collectionSchema)
                .primaryFieldName("id")
                .vectorFieldName("embeddings")
                .indexParam(indexParam)
                .build();
        
        milvusClient.createCollection(createCollectionReq);
    }
}

Collection 字段设计:

字段名 类型 说明
id Int64 主键,自增
uid VarChar(500) 用户标识
text VarChar(50000) 文档分块后的文本
page Int16 分块页码
embeddings FloatVector(1024) BGE-M3 向量
source VarChar(5000) 文档来源标识
5.4.5 数据删除
java 复制代码
// 按 source 删除指定 Collection 中的数据
public void delete(String source, String user_id, 
        String collection_name) {
    if (checkCollectionExist(collection_name)) {
        DeleteReq deleteReq = DeleteReq.builder()
                .collectionName(collection_name)
                .filter("source=='" + source + "'")
                .build();
        milvusClient.delete(deleteReq);
    }
}

// 删除整个 Collection
public void deleteCollection(String collection_name) {
    if (checkCollectionExist(collection_name)) {
        DropCollectionReq deleteReq = DropCollectionReq.builder()
                .collectionName(collection_name)
                .build();
        milvusClient.dropCollection(deleteReq);
    }
}

5.5 REST API 层 --- VectorController

对外暴露 RESTful 接口:

java 复制代码
@RestController
public class VectorController {

    @Autowired
    private VectorService vectorService;

    // 文本向量化
    @PostMapping("/embedding/")
    public Object embedding(@RequestBody Map<String, Object> params) {
        String input = ConvertOp.convert2String(params.get("input"));
        return vectorService.getEmbedding(input);
    }

    // 向量相似度搜索
    @PostMapping("/milvus/query/")
    public Object query(@RequestBody Map<String, Object> params) {
        String query = ConvertOp.convert2String(params.get("query"));
        String collection_name = ConvertOp.convert2String(
            params.get("collection_name"));
        int limit = StringUtils.isEmpty(params.get("limit")) 
            ? 5 : ConvertOp.convert2Int(params.get("limit"));
        
        List<String> collectionNameList = 
            Arrays.asList(collection_name.split("\\;"));
        return vectorService.query(query, collectionNameList, limit);
    }

    // 文本分块 + 插入
    @PostMapping("/milvus/split_insert/")
    public Object split_insert(@RequestBody Map<String, Object> params) {
        String content = ConvertOp.convert2String(params.get("content"));
        String source = ConvertOp.convert2String(params.get("source"));
        String user_id = ConvertOp.convert2String(params.get("user_id"));
        String collection_name = ConvertOp.convert2String(
            params.get("collection_name"));
        
        // 1. 分块
        List<String> splitList = vectorService.splitText(content);
        // 2. 插入
        vectorService.insert(splitList, source, user_id, collection_name);
        return success();
    }

    // 直接插入(已分块数据)
    @PostMapping("/milvus/insert/")
    public Object insert(@RequestBody DocumentModel documentModel) {
        vectorService.insert(
            documentModel.getContent().stream()
                .map(DocumentPageModel::getPage_content)
                .collect(Collectors.toList()),
            documentModel.getSource(),
            documentModel.getUser_id(),
            documentModel.getCollection_name()
        );
        return success();
    }

    // 删除数据
    @PostMapping("/milvus/delete/")
    public Object delete(@RequestBody Map<String, Object> params) {
        vectorService.delete(
            ConvertOp.convert2String(params.get("source")),
            ConvertOp.convert2String(params.get("user_id")),
            ConvertOp.convert2String(params.get("collection_name"))
        );
        return success();
    }

    // 删除 Collection
    @PostMapping("/milvus/delete_collection/")
    public Object delete_collection(@RequestBody Map<String, Object> params) {
        vectorService.deleteCollection(
            ConvertOp.convert2String(params.get("collection_name"))
        );
        return success();
    }
}

5.6 RAG 应用集成

项目将 Milvus 向量搜索深度集成到了大模型对话中(OpenAiService),实现完整的 RAG 流程:

java 复制代码
// 对话时自动进行向量搜索增强
if (aiProperties.isQueryvector()) {
    if (!StringUtils.isEmpty(collection_name) 
            && !collection_name.equals("common")) {
        
        // 1. 向量搜索
        List<String> collectionNameList = 
            Arrays.asList(collection_name.split("\\;"));
        List<String> promptList = vectorService.query(
            query, collectionNameList, 5);
        
        // 2. 组装 Prompt 模板
        String promptTemplate = 
            "你是一位文献专家,请结合上下文和检索出的内容回答问题:\n" +
            "### 上下文\n" + context + "\n" +
            "### 检索内容\n" + String.join(",", promptList) + "\n" +
            "### 问题\n" + query + "\n" +
            "## 回答必须实事求是。\n" +
            "## 若上下文与问题无关请忽略。只根据检索内容回答。\n" +
            "## 若检索内容与问题无关请忽略,自己回答。";
        
        // 3. 发送给大模型
        // ...
    }
}

六、完整调用流程

6.1 知识入库流程

复制代码
┌──────────┐    ┌──────────────┐    ┌───────────┐    ┌──────────┐
│ 原始文档  │ →  │ 智能分块(800) │ →  │ 向量化生成  │ →  │ Milvus   │
│ (PDF等)  │    │ 重叠(20)     │    │ BGE-M3    │    │ Insert   │
└──────────┘    └──────────────┘    └───────────┘    └──────────┘

API 调用示例:

bash 复制代码
curl -X POST http://localhost:9050/milvus/split_insert/ \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Milvus 是由 Zilliz 开发的开源向量数据库...",
    "source": "doc_001",
    "user_id": "user_123",
    "collection_name": "my_knowledge"
  }'

6.2 语义检索流程

复制代码
┌──────────┐    ┌──────────────┐    ┌───────────┐    ┌──────────┐
│ 用户提问  │ →  │ 向量化        │ →  │ Milvus    │ →  │ 检索结果  │
│ "什么是.."│    │ BGE-M3      │    │ Search    │    │ Top-K    │
└──────────┘    └──────────────┘    └───────────┘    └──────────┘
                                                        ↓
┌──────────┐    ┌──────────────┐    ┌───────────┐    ┌──────────┐
│ 最终回答  │ ←  │ LLM 生成      │ ←  │ Prompt    │ ←  │ 拼接上下文│
└──────────┘    └──────────────┘    └───────────┘    └──────────┘

API 调用示例:

bash 复制代码
curl -X POST http://localhost:9050/milvus/query/ \
  -H "Content-Type: application/json" \
  -d '{
    "query": "什么是向量数据库?",
    "collection_name": "my_knowledge",
    "limit": 5
  }'

七、总结

本文完整呈现了 SpringBoot 整合 Milvus 向量数据库的全过程,核心要点回顾:

模块 关键技术 要点
服务端 Milvus 2.6.1 RPM 部署 默认 gRPC 19530 端口
客户端 milvus-sdk-java 2.6.1 使用 MilvusClientV2 API
Embedding BGE-M3 + ONNX Runtime 支持 CPU/GPU/在线三种模式
文档处理 LangChain4j 800 字符分块 + 20 字符重叠
索引策略 IVF_FLAT + 内积相似度 nlist=16384
一致性 BOUNDED 兼顾性能与一致性

项目亮点:

  1. 多模式 Embedding :通过 @ConditionalOnProperty 实现 CPU/GPU/在线三种方案的自动切换,满足不同部署场景
  2. 策略模式解耦EmbeddingService 接口 + 多种实现,清晰分离向量化逻辑与业务代码
  3. 完整的RAG链路:从文档入库 → 分块 → 向量化 → 相似搜索 → LLM 增强回答,端到端闭环
  4. 生产级设计:Lazy 连接、destroyMethod 资源释放、自动建 Collection、异常处理

希望本文能帮助你在自己的项目中快速集成 Milvus 向量数据库,构建出强大的智能应用!


参考资料

相关推荐
苏渡苇1 小时前
Seata 番外篇:使用 docker-compose 部署 Seata Server(TC)及 K8S 部署 Seata 高可用
spring boot·docker·微服务·容器·kubernetes·seata·springcloud
AugustRed1 小时前
Flyway 数据库版本迁移 零基础完整学习文档
数据库·学习
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游
Yvonne爱编码1 小时前
数据库---Day9 视图(附完整数据库脚本+练习题)
数据库·mysql·oracle
sukioe1 小时前
Redis 入门:为什么出现、核心原理与安装配置
数据库·redis·缓存
宇砾1 小时前
浅谈Redis(1)
数据库·redis·缓存
杨运交1 小时前
[025][Web模块]基于 Spring Boot 的请求日志过滤器设计与实现
前端·spring boot·后端
heimeiyingwang1 小时前
【架构实战】Canal数据同步:MySQL数据变更实时捕获
数据库·mysql·架构
cdbqss11 小时前
VB2026 动态生成工具栏类 BqGetToolStrip
数据库·oracle·开源·.net·学习方法·教育电商·basic