JVM基础
JVMJVM(Java Virtual Machine,Java 虚拟机)是 Java 程序的运行环境,是 Java 语言的核心和关键部分之一。
JVM负责将.class字节码转化成机器码,能够根据不同的硬件平台和操作系统进行了优化和适配。
基本组成
类加载子系统:
功能:加载类文件(.class),生成类对象
组成:启动类加载器、扩展类加载器、应用程序类加载器
运行时数据区:
功能:用于存放Java程序运行时的数据,包括类的信息、对象实例、方法调用栈、线程状态等
执行引擎:
功能:负责执行Java程序中的字节码指令,将字节码翻译成硬件支持的指令集格式
组成:解释器、即时编译器(JIT编译器)
本地接口:
功能:允许Java程序调用本地方法库中的本地方法,实现更底层的功能和操作
垃圾回收器(GC)和内存管理子系统:
功能:管理Java程序的内存资源,包括内存分配、回收、释放
组成:GC、堆内存管理器
从功能上看,可以分为类加载系统、内存管理系统、执行引擎、本地接口
类加载机制什么是类加载机制JVM将.class文件依次进行 加载 => 连接(验证、整备、解 ...
AI 后端的可观测性:Trace ID 怎么从 MySQL 串到 Doris
问题:一条请求穿过了 6 个系统,日志散落在 6 个地方在 MySQL / Doris / ES 三角选型 描述的架构中,一条用户请求的链路是这样的:
12345用户请求 → Spring AI 应用 → MySQL(写入订单) → Canal(监听 binlog) → Kafka(消息队列) → Doris Routine Load(消费写入) → ES(双写更新索引)
加上 AI 调用:
123→ Spring AI ChatClient → LLM API(OpenAI/Anthropic) → RAG 检索(ES kNN 查询) → Tool 调用(查 MySQL)
一条请求可能穿过 8 个系统。 当用户反馈”我的订单查不到”时,你怎么定位是哪一环出了问题?
日志在每个系统里:
Sprin ...
Doris 4.x 存算分离:什么时候上、怎么上、成本怎么算
存算分离不是”升级”,是架构选择Apache Doris 从 3.0 开始支持存算分离(Cloud 模式),到 4.0/4.1 已经有 2000+ 家公司在生产环境使用。但”2000 家在用”不等于”你该上”。
先搞清楚一个核心问题:存算分离不是存算一体的”升级版”,而是一种不同的架构选择,有不同的适用场景。
存算一体(Shared-Nothing)123BE 节点 1: [计算 + 本地存储] ← 数据分片 1BE 节点 2: [计算 + 本地存储] ← 数据分片 2BE 节点 3: [计算 + 本地存储] ← 数据分片 3
每个 BE 节点既负责计算也负责存储,数据存在本地磁盘。查到哪个节点,就在哪个节点本地计算。
优势: 计算和存储在同一台机器上,数据读取零网络开销,查询延迟最低。
劣势: 计算和存储耦合,扩容时必须同时加计算和存储资源。存储不够了但计算够用?也必须加整台机器。
存算分离(Cloud 模式)1234计算集群 A: [计算] ←→ 计算集群 B: [计算] ←→ 对象存储 (S3/OSS/MinIO) ↕ ...
Spring AI MCP Server 实战:把现有系统暴露给 AI Agent
为什么 MCP 突然重要了如果你一直在跟 Spring AI,可能已经注意到了一个变化:从 Spring AI 2.0 GA 开始,MCP(Model Context Protocol)不再是社区孵化项目,而是被合并进了 Spring AI 核心。
这意味着什么?
你的 Spring Boot 应用现在可以同时扮演两个角色:
MCP Client:连接外部 MCP Server(文件系统工具、数据库访问、第三方 API)
MCP Server:把自己的业务逻辑暴露为 MCP 工具,供任何 MCP 兼容的 AI 客户端调用
以前你写一个 @Tool 方法,只有你自己的 ChatClient 能调用。现在写一个 @McpTool 方法,任何支持 MCP 的 AI 客户端都能调用——Claude Desktop、Cursor、Windsurf,或者另一个 Spring AI 应用的 MCP Client。
这不只是 API 变了,是架构选择变了。
@McpTool 和 @Tool 有什么区别先搞清楚边界。
维度
@Tool(Spring AI 1.x / 2.0)
@Mc ...
数据三角对账兜底:MySQL + Doris + ES 不一致怎么办
问题在 MySQL / Doris / ES 三角选型 一文中,我给出了”MySQL 做事务、ES 做搜索、Doris 做分析”的分工框架。在 ES 与 MySQL 双写一致性 和 Outbox 模式 中,我写了怎么用 MQ 和 Outbox 尽量保证数据同步的一致性。
但有一个问题一直没正面回答:
同步链路再完善,最终也会不一致。怎么办?
CDC 可能丢消息、MQ 可能重复投递、消费者可能超时、ES 的 refresh interval 可能导致刚写入的数据查不到、Doris 的 Routine Load 可能因为数据格式错误跳过一批。这些不是”如果”,是”什么时候”。
当不一致发生时,你的系统需要一个兜底机制——对账。
先定义”不一致”是什么意思三个系统的数据不一致,有几种典型表现:
不一致类型
表现
典型原因
MySQL 有、ES/Doris 没有
新数据在 MySQL 但搜索/分析查不到
CDC 延迟、MQ 丢消息、消费者宕机
MySQL 没有、ES/Doris 有
已删除的数据在搜索/分析里还在
删除 ...
Canal 替换方案:Debezium / Maxwell / DTS — CDC 选型的真实取舍
问题在 从 Canal 到 Doris 的数据同步链路 一文中,我写了用 Canal 做 MySQL → Kafka → Doris 的同步。但有一个问题一直没展开:
Canal 一定是对的选择吗?
如果你在做 MySQL → Doris 的实时同步,可选的 CDC 工具至少有四个:Canal、Debezium、Maxwell、阿里云 DTS。它们都能读 MySQL binlog,都能把变更发到 Kafka。但选错了,运维成本和踩坑深度完全不同。
四个工具各自是什么Canal(阿里)阿里开源的 MySQL binlog 增量订阅组件。国内 Java 生态使用最广泛。
架构:伪装成 MySQL Slave,接收 binlog 事件
数据格式:自定义 protobuf + JSON
部署模型:Server-Client 模式,Canal Server 作为独立进程
管理界面:Canal Admin(Web UI)
社区活跃度:国内活跃,国际几乎无人用
Debezium(Red Hat)Red Hat 开源,基于 Kafka Connect 框架,国际化标准 CDC 方案。
架构:Ka ...
Spring AI 2.0 Advisor 链顺序:为什么 Memory 要在 Tool 前面
一个被忽略的问题在 Spring AI 2.0 GA 升级清单 一文中,我提到了 Advisor 是 Spring AI 2.0 的核心抽象。但有一件事当时没有展开——Advisor 的顺序。
用 Spring AI 的 ChatClient 写一个带对话记忆 + 工具调用的 AI 助手,你可能会这样写:
1234567ChatClient chatClient = ChatClient.builder(chatModel) .defaultAdvisors( new MessageChatMemoryAdvisor(chatMemory), new ToolCallingAdvisor(toolCallbackProvider), new QuestionAnswerAdvisor(vectorStore) ) .build();
看起来没问题。但这个顺序——Memory → Tool → QA——真的是对的吗?
顺序不同,结果可能完全不同。
Advisor 链是怎么工作的先理解 Spring AI 的 Advisor 机 ...
RocketMQ 事务消息 vs Outbox 模式:AI 后端的一致性选择
问题在 之前的文章 里,我写了用 Outbox 模式保证 RAG 数据一致性。核心思路:业务数据和 outbox 记录在同一个数据库事务里写入,然后用 CDC(Canal/Debezium)把 outbox 记录搬到 MQ,消费者再去同步 ES/Doris。
但有一个问题一直被绕开了:
如果你的 MQ 是 RocketMQ,你还需要 Outbox 吗?
RocketMQ 原生支持事务消息——半消息 + 本地事务 + 回查机制。这本身就是”保证本地事务和消息发送原子性”的方案。既然 MQ 原生就能做,为什么还要写一张 outbox 表?
这是做 AI 后端时真实会碰到的选择。两条路都能到终点,但代价不同。
先搞清楚两个方案各自在做什么Outbox + CDC1应用 → MySQL 事务 { 业务表 + outbox 表 } → CDC 监听 binlog → 发送到 MQ → 消费者
核心思想:把”发消息”变成”写数据库”。 只要业务数据和 outbox 记录在同一个事务里,要么一起成功,要么一起失败。消息的发送由独立的 CDC 组件异步完成,与 ...
