Guitar 🎸

指板

chords
在乐音体系中,七个具有独立名称的音级叫做基本音级(也叫自然音级)。 基本音级用“C、D、E、F、G、A、B”七个字母来表示,这就是基本音级的音名唱名是人们在演唱音乐的谱子时所使用的名称,即:“Do,Re,Mi,Fa,Sol,La,Si” 这七个唱名。
简谱上 唱名、音名的表示
十二平均律,是将一个八度的音程等分成十二个半音的律制,各相邻两律之间的波长之比完全相等。一等份为一个半音(小二度),对应吉他一品的距离。两等份为一个全音(大二度)。将一个八度分成12等份有着惊人的一些巧合,这是因为它的纯五度音程的两个音的波长比为(1/2)^(7/12)≈0.6674,与2/3≈0.6667非常接近。 一个八度内 全全半全全全半

C大调音阶在吉他指板上可以有不同的把位 https://www.zhihu.com/tardis/zm/art/496745355?source_id=1005
C大调音阶图

扫弦节奏型:1)下 下 下上 上下上下 下上
节拍

乐理

HUB GUITAR: https://hubguitar.com/zh_han/music-theory 指板: https://hubguitar.com/zh_han/fretboard

弹奏第一品位上的B弦,这就是 C音,一个以一定频率振动的音波。接下来你拨第十三品的同一根弦,同样地,它听上去好像是同一个音,但这个音高更高。这是因为声波的振动是原来的两倍之快,可以表达为1:2, 它们是有同一特性的音。如果两种不同的乐器同时弹奏C音,这个比例是1:1,这称为 同音。在下图中,第一品的音符将会是最左边的C,第十三品的音则是在最右边。所有在这两者之间的音符都是特定的音符,但一旦重新回到C音,就重新按照这个顺序继续下去。

音程:就是两个音符之间的高低关系。在较低的一个 “C” 音和另一个较高的 “C” 音之间的音程就是 八度(八音音阶)。八度是音高的基本来源,其余的还包括将它分成更小的部分而得到的音高,称为 音数。半音就是移动一格,从 C 到 C♯。全音移动两格,从 C 到 D。 大多数现代音乐将八度分割为12级,如图所示,你可以按照这个音符的顺序来弹奏,从第一品的 B 弦开始,每次移动一个品位,直到第十三品,重新回到 C,一边弹奏一边大声说出这个音符的名字。所有这十二个音符一起组成了 半音音阶。音阶就是音符的顺序,并且没有重复的音符,所有的音符以升序,从低到高的顺序来弹奏。

根音?它就是一首歌的主调音,它是一个单音,其余的东西全在它的基础上变化,想像它是重力中心,它是一种吸引力,吸引着一首歌曲里其余所有的音,不管什么情况下都会回到根音上来。

TABS


晴天 - 周杰倫

Hey Jude - The Beatles

小情歌 - 蘇打綠 https://www.bilibili.com/video/BV1R64y1p7WB/

紅豆 - 方大同

找自己 - 陶喆

普通朋友 - 陶喆 https://www.bilibili.com/video/BV1zw4m1a7CU

Canon in C https://www.bilibili.com/video/BV1if4y1A7SZ/

揪心的玩笑與漫長的白日夢 - 萬能青年旅店
https://www.bilibili.com/video/BV1cuHjetENY/?vd_source=ff210768dfaee27c0d74f9c8c50d7274

美 的 Midea

08/07/24

入职第一天,熟悉运维平台,目标实现其自动化,后续参与到美的云,?
熟悉新旧平台的功能和调用关系,拆分业务需求开发步骤,编写文档,每日汇报进展,熟悉开发流程、、
软工院作为非互联网公司的非核心业务的底层平台建设部门,结果导向,组员多一年社招;)无校招培养。?

7.17 ~ 7.29

EDP培训 + MGC(头脑风暴/产品调研/拉通对齐>>技术)
T型人才(广度+深度),开发技术+产品思维->架构师
不设限,主动承担任务,机会莫名来:) take other people’s jobs and become indispensable to the team..

复杂的事情简单化(思考简化),简单的事情复杂化(做到极致)
工作就是生活,生活就是工作,不需要平衡(找到热爱的工作)
成功的百分比 = 做事 / (个人 + 做事);做事的比例越大,成功的概率越大

Allen: 向上管理?× 向上反馈,同步进展
MGC结营
融入团队?主动承担?谈论未知?如何选择自己在团队中的角色,人设??
圈子

8.17

佛山校友会迎新
“努力会发光,先有为后有位”,“头三年不要动,把这一套学会”
程序员的本质核心竞争力是什么?1.开发都是那一套 2.专精一个领域 3.meet新公司的需求 4.解决问题的能力

8.22 需求一

华为云主机开/关机/重启自动化 : mq、定时任务、公有云api、crud、、
完成第一版8.14,自测8.15,,merge request,code review,sit,测试,uat,发版8.22、、
反思、、在开发同事的指导下完成了开发,不具备独立调研和开发能力,,
缺少对产品的思考??没有对需求进行120%的思考和完成。。

8.24

顺德校友会迎新
why Midea?1.生活成本低(特别是住宿好通勤方便)2.相比下工作轻松(能够有自己的时间学习业务以外的东西)
思考自己在..年后会到什么层次(本科毕业+6y ?= 博士毕业起步)。。阶段性目标

8.27 Steven:

深入一个领域,,
先做一点功能点,然后负责一个模块,到不同系统的交互、、
多学基础,与外包的区别。。与人沟通的能力
幂等,整体设计,微服务治理,看项目源码,,
干半年就不是应届生了。社会很残酷,前两年要快速成长;思考两/五年后的情况、、
开发整个过一遍,打包,发版,,
多讨论,多问,code review
失败邮件发送:设计一个功能,,关注点,逻辑路径,通用性,,如何表述。。?! –>

9.9 Java 并发: 先查后改

方法:事务+行锁【悲观锁】,避免在高并发场景下先读后写导致多个线程同时读取相同的值然后同时写入引发数据不一致的问题
测试:线程池多线程访问,打印数据,排查重复值;考虑数据库连接池配置
思考:项目部署到多节点下,则是多进程的多线程环境,需要用Redis分布式锁,或者唯一的全局数据库节点加锁;
单节点的多线程才能用synchronized?、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service
public class NumberService {

@Autowired
private NumberMapper numberMapper;

@Transactional // 在业务逻辑层开启事务,确保整个操作是原子的
public void incrementNumber(Long id) {
// 查询并锁定行
NumberEntity numberEntity = numberMapper.selectForUpdate(id);
// 读取后进行计算并更新,保证只有当前事务可以操作该行
int currentNumber = numberEntity.getNumber();
numberEntity.setNumber(currentNumber + 1);
// 更新数据库
numberMapper.updateById(numberEntity);
// 更新完成后提交事务,释放锁
}
}
1
2
3
4
5
6
7
@Mapper
public interface NumberMapper extends BaseMapper<NumberEntity> {

// 使用 FOR UPDATE 读取并锁定行,防止其他事务并发读取该行
@Select("SELECT * FROM number_table WHERE id = #{id} FOR UPDATE")
NumberEntity selectForUpdate(Long id);
}

优势:完全避免了并发更新导致的数据不一致,是标准的悲观锁实现。
潜在问题:如果方法被高频调用,FOR UPDATE可能导致锁竞争,影响并发性能。
更优方案:直接 UPDATE + SELECT

  1. UPDATE 自带行级锁:执行 UPDATE 时,数据库会自动对目标行加排他锁,确保原子性
  2. 避免两次数据库交互:无需先 SELECT 再 UPDATE,减少一次网络往返
  3. 减少锁持有时间:锁只在 UPDATE 执行期间持有,而非整个事务周期

9.12 需求二

Azure公有云主机申请 :根据云管界面配置配齐参数发送报文到作业平台,完成自动化主机创建和标准化
对其参数,连续加班,9.9完成第一版,9.10上sit前端联调,9.11开发部分发邮件,9.12上uat,6.同步DDL&DML,7.发版,验收成功
接触运维协同,,code review,联调,,集成,部署,流水线,,

9.14

窝囊费:)到账

9.26 需求三:

Azure公有云主机回收/开/关机/重启 : 调研AzureApi和测试方法,开发,,
9.23回收上sit,9.24开关机重启代码重构(原华为云方法过于不通用),9.25bug毁了我的足球梦,9.26配置ngix上uat,验收
思考:自测可以 1.全流程验证 2.单独功能验证 3.考虑开发与测试环境的区别(ping的包装方法/命令行执行在开发/测试环境的区别)
后续:完善公有云开发(Azure回收配额,ip,失败邮件),后续由运维平台MOPS -> 参与到数据库开发

10.12 需求四:

任务触发式失败邮件完成,改造为工单定时任务扫描式,10.17上线
邮件通用性??工单+定时任务层面的通用,,

10.14

数据管控平台DataMars: 云管cmcloud开发功能,先提供内部服务,后到SAAS,,
InfluxDB备份恢复 1 调研 2 手工实现 3 详细文档
2-3月时间,不要求11月上线,整体设计,转正答辩
api,数据库内核,容器,k8s
容器,登录主机,查看docker实例,操作数据库实例

10.22 K8S 验证InfluxDB Cluster 实例导入导出

  1. FinalShell客户端:主要用于服务器管理和运维。支持 SSH、SFTP 等多种协议,方便用户通过图形界面进行远程连接和操作。
  2. 跳板机/堡垒机: SSH连接,登录堡垒机opsec.midea.com,mip账密 + OTP验证
  3. 资产列表中选择指定环境下的主机,InfluxDB多节点部署在对应环境的几台主机上
  4. 切换用户:rouser,apps,root
    1
    ~$ sudo su - apps
  5. K8S入门 https://zhuanlan.zhihu.com/p/32618563
    • Namespace(命名空间,是一个逻辑隔离的环境,用于资源分组和隔离) -> InfluxDB集群(可以有多个),Pod(Kubernetes的基本计算单元) -> InfluxDB集群节点(逻辑上),容器 -> InfluxDB单例(物理上)
    • Namespace:作为最顶层的资源,实现了资源的逻辑隔离。
    • StatefulSet:对于有状态的服务如数据库,K8s 推荐使用 StatefulSet 进行管理,确保每个 Pod 都有一个持久的唯一标识并提供稳定的网络标识和存储。 InfluxDB 集群中,StatefulSet 用于管理数据节点和元节点。
    • Pod 是最基本的部署单元,它是可以被创建和管理的最小部署对象。当创建一个 InfluxDB 集群实例时,StatefulSet 会用来管理 Pod 的生命周期(而不是直接创建Pod)。
    • InfluxDB 集群部署:对于一个 Namespace下的几个 Pod,这些节点会通过 StatefulSet或者 Deployment来进行管理和部署。在实际操作中,创建 InfluxDB 集群实例的 Helm Chart 或者 Operator 通常会自动化这些资源的创建过程。
    • 通信调度:K8s 中,Pod 之间的通信通常通过 Service 来进行。Service 会为一组 Pod 提供一个稳定的 IP 地址和 DNS 名称。对于 InfluxDB 集群,可能会有一个或多个 Service 来管理数据节点和元数据节点之间的通信。
  6. 连接拉起DB实例的主机,查看 influxdb 命名空间下的所有 Stateful、Pod 的状态和节点信息
    1
    2
    kubectl get sts -n influxdb
    kubectl get pod -n influxdb -o wide
    可以看到,在 Kubernetes上创建了 InfluxDB集群实例,它们共用一个 Namespace:influxdb,使用 StatefulSet 来创建和管理 Pod。这些 Pod 负责运行 InfluxDB 服务,并由 StatefulSet 确保它们的高可用性和数据持久化。
    对于每个集群实例,有 2个 sts为 meta和 data,分别有2和3个复制,即2个元节点和3个数据节点 Pod,部署在3台服务器上。pod内部共用数据卷,pod之间数据不互通,部署在同一主机上的pod之间可以通过本地机器为中介复制文件。
  7. 查看 influxdb 命名空间下的 service 信息。
    1
    kubectl get svc -n influxdb
    有 2 个Service,用来定义一组Pod的访问策略的抽象。它提供了一种方式,使得外部客户端可以通过一个固定的IP地址和端口访问这些Pod,而不需要关心Pod的实际IP地址和端口。Service会通过选择器(selector)将这些端口映射到后端的Pod上。
  8. kubectl exec 进入指定的 Pod(默认进入其中的第一个容器),并启动一个 bash shell;可以看到当前 InfluxDB 版本是v1.8.10-c1.1.2
    1
    2
    3
    apps@(datamars)mhpl74337-10.20.248.65 ~$ kubectl exec -it influxdb-e2cb6c913a191e56c134e-data-0 -n influxdb -- bash
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influxd version
    InfluxDB v1.8.10-c1.1.2 (git: master 529251fda5d776cf47bb0c247cf81075f2980fed, build: go1.16.15 linux/amd64)
    在使用InfluxDB进行备份和恢复操作时,通常需要在data节点上执行相关命令(meta节点上都没有influx指令)
  9. 进入influx命令行界面,验证身份信息;事实上,进入到influxdb实例中,便无需考虑部署节点/主机的差异了,直接操作数据库
    1
    2
    3
    4
    5
    6
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx
    Connected to http://localhost:8086 version 1.8.10-c1.1.2
    InfluxDB shell version: 1.8.10-c1.1.2
    > auth
    username:
    password:
  10. 数据库导出:容器层面命令,指定 数据文件和 写前日志(WAL)文件的存储目录,将指定数据库中指定时间的数据导出到指定文件。
    1
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_test01_dump_out" -database "test01" -start "2024-10-22T00:00:00Z"
    数据文件复制:1、从pod1复制到本地机器 2、从本地机器复制到部署在同一服务器上的pod2(NODE: mhpl74344)
    1
    2
    sudo kubectl cp influxdb/influxdb-e2cb6c913a191e56c134e-data-1:/influxdb_test01_dump_out data/influxdb_test01_dump_out
    sudo kubectl cp data/influxdb_test01_dump_out influxdb/influxdb-e73f149ff7192bd87d190-data-1:/influxdb_test01_dump_out
    获取密码:查看对应实例的admin账密,解密data
    1
    2
    kubectl get secrets -n influxdb # 看命名空间
    kubectl get secrets -n influxdb influxdb-e73f149ff7192bd87d190-influxdb -o yaml # 看选定实例
    数据库导入:容器层面执行命令,使用admin账号,指定文件、数据库、时间戳精度
    1
    root@influxdb-e73f149ff7192bd87d190-data-1:/# influx -import -path='influxdb_test01_dump_out' -precision=ns -username='admin' -password=''
    实例导出:不加 -database,把influxdb集群实例中所有数据库的数据导出,加 -compress 导出压缩文件
    实例导入:加-compressed 导入压缩文件,本质上是先解压后倒入

11.11 InfluxDB服务化 :实例备份(调研&开发)

10.14-10.18:看文档,建立InfluxDB集群概念(前期已经也在看了..),建立整体框架概念(apiserver–bakserver–agent)
10.21-10.25:本地容器搭建influxdb集群×,连接服务器测试实例验证功能,了解K8S概念,手动验证实例(库级)导入导出即逻辑备份
10.28-11.01:对其需求(能做但没用户??),完成技术文档框架,开始将功能接入datamars-bakserver,了解golang开发
11.04-11.08:搭建go开发和agent项目环境,,无法理解go项目结构,尝试从bakserver侧理解task下发-接收-执行全流程
11.11:理解所有业务代码(after2weeks)发现task下发无需改动,只需适配influxdb(修改配置类和表),接着打包至sit环境打印log调试
11.12-11.15:研究pod添加container(改sts后delete pod重建),go项目构建(windows尝试配齐开发工具但有些包依赖linux环境)》。
11.18-11.27:本机wsl的ubuntu成功构建起datamars-agent,边抄边做,不用理解其框架?.. 及时请教专家
11.28-12.03:kun手改agent代码:)打包image push到dockerhub,宿主机拉取镜像后本地grpcurl调试 pod ip:port 验证功能
12.04-12.09:bakserver打日志流水线部署到uat(集群实例所在环境),通过apiserver-bakserver-agent中的日志验证全流程功能
12.10:验证通过
技术文档先行,将需求拆分成一步步,重要的是要有产出,,能汇报进度。。buffer。。好心态😇不怕叼
关注重要的事情(功能接入已有框架/理解业务逻辑×,功能验证和对齐需求√,开发卡点及时请教)
1/2时间幻想(串联已知信息且验证,对齐上下游并重复验证,本质是开发环境,业务不熟悉),1/4等回复(线上下请教+准备),1/4开发(快乐短暂)
与人沟通是重要的能力。。拉群问。。软件开发还是很残酷的。。

2025.04:重新考虑… 1 influx_inspect export只是把该pod指定数据库(或所有库)的指定时间段的分片数据(tsm&wal)导出line protocol文件,各Data-Pod数据不一致时不能代表整个实例;2 influx -import本质是将数据重新写入,前提要先恢复好shard元数据,,

12.12 年终述职

  • 大家好,那我现在开始~
    七月份入职后,我先是接触了MOPS自动化运维的开发任务:)
  • 总的来说,这五个月以来,我作为团队和开发领域的新人,得到了循序渐进的挑战和成长。了解到如何作为团队中的一个成员进行开发,能够理清一些比较复杂的业务逻辑,能够尝试进行调研和方案设计 ~
    相比于赋能团队,我觉得更多的是还我自身需要补齐能力;也许目前开发效率低,本质是对业务的不熟悉和开发技能的缺失,但是除了我觉得提升开发能力,还应该具备产品思考的能力,服务客户。我的汇报就到这里。

12.27 成长对话

  1. 本年度关键战役及成果产出
    MOPS开发任务
    1 华为云主机开/关机/重启自动化:进行了华为云api的验证并接入代码,对开发环境和流程有了基本认识,学会将需求拆分进行开发和逐步验证。
    2 Azure公有云主机申请自动化:开发初期遇到文档和会议理解上的困难,沟通后明确需要配置参数构造Azure主机申请报文,发送至作业平台完成自动化主机创建和标准化,经过与前端、运维同事协同开发上线,申请数达20+。
    3 Azure公有云主机回收/开/关机/重启自动化:区别于华为云api会返回job状态,为Azure任务设计了新的验证方式,如开机成功是能够ping通主机,重启是先ping不通后ping通,并为适配原业务逻辑进行了代码重构。
    4 公有云主机邮件发送:为公有云消息通知功能设计了多版方案,最终采用定时任务扫描工单表的方式,以工单执行状态和执行时间作为邮件发送条件,简化了发送条件且便于理解和维护,发送邮件数达40+。
    DBEngine开发任务
    1 InfluxDB备份与恢复:先进行了InfluxDB Cluster实例导入导出功能的验证,确定了先导出物理备份文件后上传OSS的技术方案,功能分别接入 apiserver,bakserver,agent 各系统,在配置开发环境及开发验证的过程中补齐了K8s、Linux等能力;目前备份功能进入联调阶段,同步开发备份集恢复功能。
  2. 面对未来1-2年的职业规划
    作为团队和开发新人,在这个阶段希望能锻炼自己的专业能力,补齐开发所需地各项能力,积累实战经验,能够快速开发需求和输出文档;而从需求开发和解决问题的经验中抽象出能力,无非就是在沟通时抓住重点,开发时做到极致,这也许就是院长所说的“复杂的事情简单化,简单的事情复杂化”;
    除了专业能力的提升,还应该具备产品侧思考的能力,对一个系统有深入的认知,能够独当一面,做到专精一个领域。
  3. 希望提升的1-3项核心能力项,计划如何提升
    1 快速学习的能力。通过自主学习/经验复用,辨别需求开发中问题的关键,快速掌握解决问题所需能力,不企图全面构建知识体系
    2 时间管理的能力。目前,需求开发中实际用于写代码的时间很少,大部分的时间用于串联和验证已知或猜测的信息,对齐上下游,这也许是因为对开发环境、业务的不熟悉和开发技能的缺失。另外,由于当前需求开发很依赖他人的讲解,做到高效提问,不耽误他人时间也是很重要的。以及要学会在开发中预留buffer,在实际开发中提高效率。
    3 精确表达的能力。描述调研方案或解释曾做过的功能时,往往没法在当时呈现所有的思考结果。在对齐需求和协同开发中,有时会抓不住重点,重复提问,效率不高。需要向同事请教如何提升这项能力。
  4. 上级总结
    24年成果:
    1)mops完成2个公有云的主机开/关机/重启自动化的能力研发
    2)完成数据库influxdb的备份和恢复任务开发
    做的好的:
    1)作为应届生有一定的主动性,对于不懂的积极学习
    2)能够在同学指导下完成工作
    待改进:
    1)技术能力需要加强和提升,需要快速学习新技能
    明确员工亟待提升的核心能力,并为其制定成长计划:需要提升代码开发基本技能,加强基础学习
  5. more thinking
    无法独立开发,效率低,代码量少;问人很正常,在群里问,卡点,同步领导;值班,锻炼解决问题的能力;长期发展,先补齐技能,有好奇心,,提高工作日效率,边工作边学习成长,,用心,真诚 :_
    阶段目标、、开发效率,高级开发,项目管理
    2024:校招,旅行,吉他
    2025:职场,矫正,足球
    【程序员如何快速成长,这几点值得重点参考,我只教一遍!】 https://www.bilibili.com/video/BV1bK4y1B7rj
    【【社区分享】程序员宝藏推荐!提升天花板!覆盖学生到架构师!】 https://www.bilibili.com/video/BV1Ta411s7ij/
    【建议收藏,高级开发如何提升产品能力!我常用的5个网站!】 https://www.bilibili.com/video/BV1y2C3YpEaL/

2025.01.16 试用期转正答辩汇报

  • 尊敬的领导、各位同事:
  • 大家好!我是蔡枫,今天非常荣幸能在这里与大家分享我在试用期的工作成果和心得。【翻页】我将从试用期工作内容、工作改进点、下季度工作计划以及问题与建议,四个方面进行汇报。【翻页】
  • 试用期工作内容
    这段时间,我主要参与到两个项目中:分别是MOPS公有云自动化和InfluxDB服务化。
    我首先接触到Mops华为云和Azure两个云厂商公有云主机的 “开/关机/重启/申请/回收自动化” 的需求,在业务熟悉的同时,进行了云主机运维功能的完善。以及,我参与到influxdb服务化开发,目前实例备份与恢复功能已开发完成,并将逐步支持其他功能。【翻】
  • ;)
  • 试用期工作改进点
    在试用期间,我意识到需要首先要提高代码能力。在需求开发的过程中,我逐渐熟悉开发环境和流程,初步掌握验证和联调方法。作为团队和开发新人,在这个阶段希望能锻炼自己的专业能力,积累实战经验,能够快速开发需求和输出文档;
    同时,我补充了运维相关的能力,学习并通过实践掌握了K8s,Linux相关知识和基本操作,为后续工作打下坚实基础。
    以及在做需求,协同开发的过程中,我增强了沟通理解的能力,学会从需求开发和解决问题的经验中抽象出能力,要能在沟通时抓住重点,开发时做到极致。【翻】
  • 下季度工作计划
    接下来,我计划完善和支持InfluxDB实例集恢复、扩容、重搭等功能,以进一步提升系统的高可用性。
    总的来说,通过Kubernetes集群和Helm Chart等实现了对InfluxDB集群的高效管理。用户可以通过DataMars控制台方便地进行实例的备份恢复和配置变更,同时支持扩容重搭以应对业务需求的变化。工作流引擎负责处理用户请求并调用相应的Helm Chart模板、通用或专有的apiserver接口等,确保操作的自动化和一致性。相信有了前期开发经验,我能够更快的进行后续的开发产出。【翻】
  • 问题及建议
    最后,希望提出一些建议。首先,在新人指引方面,我认为前期培训缺少技术方面的指引,可能导致新员工在后续开发中理解比较费劲,上手难度大。建议组织开展包括开发流程、开发环境搭建、代码规范等内容的培训,确保新员工能够快速融入。其次,关于文档落实,我发现一些技术文档内容不够详尽,导致无法自行定位问题,需要频繁联系文档编写者。对此首先我应该提高自己的文档撰写水平,确保能够让人快速理解整体流程,找到问题所在,并及时更新文档内容。
  • 总结(的)来说,在各位同事的帮助下,我在试用期间得到了快速成长,收获颇丰。【翻页】以上就是我的汇报内容。感谢各位领导和同事的聆听和支持。我相信,在大家的共同努力下,我们的工作会取得更大的进步。谢谢大家!

1.16 InfluxDB服务化 :备份集恢复

1.02:不上心
1.14:开发uat自测(sit没测)完成,开发分支合dev提测,最后合main上线
1.16:SD/GA测试发版失败,恢复工作流需要人工介入验证,存在问题 1地址没有动态配置!! 2漏配接口/审批流变更
1.21:发布修复版本,生产延后

2.7 复工

当前阶段的关键,,交付能力,,工程能力是练出来的
熬夜是没有对明天的期待、、-》培养兴趣转移注意力、,books
程序员。技术。不要只看自己的一亩三分地。。开源项目
工作以外;:给自己创造需求,根据需求解决问题,在解决问题上配合看书,,从而在某一细分领域有知识图谱,有一技之长,用系统性的看书代替cdsn查找零散的解决方案
“下班的时间放在哪哪里就有提升”
副业?;web3;licai

dataMars服务架构理解

  1. apiserver:1 datamars管控接口 2 mcloud回调接口 3 properties获取apiserver服务自身暴露的域名端口+controller接口拼接url
  2. apiserver-{engine}:引擎专有服务,工作流(其实是workflowclient处理)中调用不同服务暴露的接口(common,,influxdb,,apiserver)
  3. bakserver/metaservice/xx-agent:其他服务,由rpc/grpc暴露服务
  4. 调用架构:api、v1、v2
  5. 服务架构:通过Kubernetes集群和Helm Chart等实现了对InfluxDB集群的高效管理。用户可以通过DataMars控制台方便地进行实例的备份恢复和配置变更,同时支持扩容重搭以应对业务需求的变化。工作流引擎负责处理用户请求并调用相应的Helm Chart模板、通用或专有的apiserver/k8srepository接口等,确保操作的自动化和一致性。

todo 手画图

2.18 InfluxDB服务化 :本地变配

2.6-2.16:现有InfluxDB集群实例(只考虑data节点)的CPU、内存和存储资源已无法满足需求,需对资源配置进行扩展,以提升性能和稳定性。通过修改StatefulSet中cpu、memory配置并删除Pod触发StatefulSet控制器重建data节点Pod以应用新配置,通过修改data节点Pod对应的pvc中storage配置以触发pv的存储扩容(只能增加),实现资源变配(本地变配)
2.17:前端对齐开发,准备进入sit联调
2.18:插入需求“meta节点自定义创建”
2.28:整合已知信息已读代码->无法理解多节点类型实例如何发起变配,应该果断求助
3.3-3.10:改造influxdb集群为父子实例模式,改配置,传参调试,适配已有功能,考虑存量实例的影响
3.11-3.13:data变配基础上开发meta变配,云管订单遇到配额不匹配问题,上线延期下周
3.14:跨团队求助无果,请求协助,,新需求着手开发
3.17:搁置,开发新需求
3.18:实操tidb复现该“问题”,考虑转向与云管沟通。。
3.27:发版 =》InfluxDB父子实例改造/本地变配/节点/实例重启

2.22 正畸, 启动

读书时无所事事的日子,今天拔完牙和妈妈一起冰敷等待的日子,还有多少
刚开始普遍很难,易的是背八股,难的是落实和推进
如何跳出这个困境?如何跳出程序员行业?30岁,35岁
熬夜是因为没有对明天的渴望。但是在晚上的当下,有很多事情想做😿
喜欢一个人独处,是因为不想自己长期以来形成的情绪稳定被打破。害怕形成亲密关系,有时无法融入团体😿

2.27 InfluxDB服务化 :实例创建自定义Meta节点规格

2.18:本需求作为其他需求开发的前置条件
2.19:apisever打log上uat调试创建流程,从已部署分支git branch新分支以免影响正在使用者
2.21:提sql变更 1 dataspace提工单 2 直接进入各环境metadb(其本身为容器部署的mariadb服务) 3 某些配置项可通过datamars控制台修改
2.25:云管运营端商品信息变更,考虑是否影响存量实例;;1 云管释放旧实例将计价报错->调datamars管控接口释放/发版前释放旧实例 2 可以发起工单但无法下单->手动修改配额发起工单后过云管审批
2.27:发版流程、、代码合master,流水线打包使用(发版版本)部署,sql变更(定时,增加条件避免误订正),云运营变更(改一次console-cloud即各环境共用)
Steven👨‍🦲:裁员,残酷,危机感,,工作就是生活的很大一部分

  1. meta规格,配置到instance_spec,engine = “InfluxDBMeta”
  2. apiserver实例创建逻辑
    meta信息通过request.getExtraJson()传入,ClusterInstanceService#initClusterInstanceExtend保存在cluster_instance_extend;initClusterInstanceParams能适配保存meta节点的param吗(iops,连接数,influxdb不考虑?)
    不考虑在cluster_instance要新增字段体现meta规格信息
  3. apiserver-influxdb工作流完成实例创建,pv,sts,helm,install,node,instance
    获取meta规格信息,构造临时value文件,生成用于安装InfluxDB集群的Helm命令并执行
  4. 以上只是在k8s环境中实现了自定义meta规格创建,事实上应该参考tidb,将influxdb集群改造为父子实例模式,逻辑上把data/meta节点区分开,进行变配/更新meta信息..
  5. more to concern? param创建,变更?extend父子实例数量不一致?实例创建流程,sts,副本,顺序。。

3.3 阶段目标 :入职半年 ..

deepseek:数据库方向是一个值得长期投入的领域,尤其适合对系统底层感兴趣的程序员。你的现有经验(运维+K8s)可成为切入云数据库或分布式数据库的跳板。建议以​“运维需求驱动内核学习”​为短期目标,逐步掌握分布式一致性、存储引擎等核心技术,同时通过开源贡献和项目实践构建技术影响力。=> https://yuanbao.tencent.com/bot/app/share/chat/c6b48985efa0c1101e5c6ae18c867724
rong teng:在midea得到的成长是显著的;(身兼开发运维多职,具体求职情况如何?)数据库方向有些窄;(作为senior求职需要专精时显得窄?作为基础能力学习可行?)应届生可以提转方向,转团队;以招聘市场心仪岗位的需求作为努力发展的方向!?

3.17 InfluxDB服务化 :实例/节点重启

3.17:简单需求,父子workflow + k8s资源控制器
3.18:接口配置:接口信息查看mariadb已有的相同接口,其他信息参考influxdb自身的其他接口;前端联调完成
3.19:提测,发版:发版分支一周内进行 1 代码扫描-安全扫描 2 安全-软件成分-Web漏洞-灰盒,解决漏洞;代码仓库设置发版分支,史诗中关联所涉及仓库,检索其发版分支的扫描报告,手动关联web漏洞测试报告,质量门禁达标以通过安全卡点

3.24 从零实现Kubernetes环境下的InfluxDB自动化登录工具:Bash与Java的跨语言实践

  • 背景与需求分析
    在云原生环境中,InfluxDB集群常以StatefulSet形式部署于Kubernetes。运维人员需要频繁执行以下操作:
    动态选择特定Data Pod;解密存储在Secret;通过交互式命令登录数据库。
    手工操作存在效率低下、易出错等问题。本工具通过Bash脚本整合Kubernetes CLI、Java加解密等能力,实现全流程自动化。
  • 技术方案设计亮点
    1. 混合编程模式(Bash+Java)
      核心难点:GCM解密在纯Bash环境难以实现(openssl版本低(不会升级…) 没有aes-256-gcm工具)
      创新方案:编写Java脚本,动态生成Java解密类(图1),通过Java标准加密库实现AES-GCM解密
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 生成随机类名避免冲突
      CLASS_NAME="InfluxDecryptor_$(mktemp -u XXXXXXXXXX | tr -dc 'a-zA-Z0-9')"
      # 编译并执行Java代码
      if javac "$TEMP_JAVA"; then
      auth_output=$(java -cp /tmp ${CLASS_NAME})
      echo ""
      echo "$auth_output"
      echo ""
      else
      echo "Java编译失败"
      return 1
      fi
      凭证安全处理机制:
      • 使用临时文件存储解密代码(TEMP_JAVA="/tmp/${CLASS_NAME}.java"
      • 执行后立即清理编译产物(rm -f "$TEMP_JAVA"
      • 避免敏感信息持久化
    2. 跨语言参数传递
      动态生成的Java脚本中,选择标准输出+格式控制方案:
      1
      2
      System.out.println("USERNAME:" + username.trim()); 
      System.out.println("PASSWORD:" + new String(decrypted).trim());
      多行输出解析难题
      1
      2
      # 错误示例:初始方案采用`IFS`分割导致变量截断
      IFS=: read username password <<< "$credentials"
      优化方案:bash脚本中,使用sed精确提取java脚本输出,通过正则表达式过滤前后空格,避免不可见字符影响
      1
      2
      3
      4
      5
      6
      7
      8
      credentials=$(get_auth $1) # get_auth()动态生成java解密脚本并多行输出
      if [ $? -ne 0 ]; then
      echo "解密失败,无法登录"
      exit 1
      fi
      # 提取用户名和密码(处理多行输出)
      username=$(echo "$credentials" | sed -n 's/^USERNAME://p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
      password=$(echo "$credentials" | sed -n 's/^PASSWORD://p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
    3. 交互式Pod选择器
      1
      2
      3
      4
      5
      6
      7
      8
      9
      function select_data_pod() {
      # 过滤带data标签的Pod
      PODS=$(kubectl get pod -n $NAMESPACE | grep "data" | awk '{print $1, $6}')
      # 构建交互式菜单
      select pod_option in $PODS; do
      SELECTED_POD=$(echo $pod_option | awk '{print $1}')
      break
      done
      }
    4. 传递方案对比:
      方案 优点 缺点
      文件存储 实现简单 存在安全风险
      环境变量 进程内可见 长度受限
      标准输出 无持久化风险 需严格格式控制
      网络传输 适合分布式 增加复杂度

      本项目完整代码已开源,读者可通过GitHub仓库获取最新版本。

3.28 InfluxDB服务化 :节点迁移 / 重搭

3.28:需求分析->将pod迁移到集群的另一个资源充足的node上,并且恢复数据以及集群功能(元数据)
3.31-4.1:存量实例问题处理,, 建议客户使用改造后的influxdb实例,存量实例释放/启停/重启等功能遇到问题 =》未考虑好适配
4.2-4.7:出方案 1 sts指定亲合度规则以在指定node重建pod和pvc 2 开源influxdb-cluster功能不完全支持,考虑从分片副本层面恢复数据
4.8-4.10:数据恢复的主要思路=》从健康节点的分片副本copy-shard恢复出迁移节点原有的所有分片副本,?质疑->恢复过程中健康节点的分片副本持续写入的增量数据能否恢复??
4.11:考虑创建第3个Data-Pod替代迁移节点(从仍健康的迁移节点上迁移数据)× ->应该认为原节点完全不可用(相当于节点重搭了)设计方案参考其他数据库产品;;
4.14:推方案不动..自验证copy-shard达到预期效果,但无法解答原理,,自测方式还需模拟实际场景,多线程写入。。
4.15:写bash脚本批量写,开多终端模拟多线程,分片副本达70M+大流量2000point/s写 =》copy-shard恢复出分片副本与健康节点持续写的副本md5值不一致,恢复副本export文件小,猜测丢失数据
4.17:InfluxDB Data节点迁移方案评审:先迁移后逐个恢复分片数据。已验证在分片副本大小70M、写入数据达2000point/s的情况下直接copy-shard会导致增量数据丢失,考虑在copy-shard前先执行truncate-shards截断热分片(集群中所有写入最新数据的分片,截断后关闭写入,变成冷分片),并在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了全部原有分片的新热分片副本,最新数据写入这个副本,然后再逐个从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复;自测符合预期
4.18-4.21:开发。发现原checkPodRebuildReady接口有时不符合预期,执行influxd-ctl指令(硬写成String在代码中)有时失效(meta no leader..),经常需要手工介入。。
4.22:提测
4.23:InfluxDB开源版可靠性不确定,,社区版,单节点,,开发,值班暂停
官方文档 https://influxdb-v1-docs-cn.cnosdb.com/influxdb/v1.8/introduction/install/
开源influxdb-cluster源码 https://github.com/chengshiwen/influxdb-cluster

Data节点迁移方案

  1. 重启所有meta节点,避免后续执行influxd-clt指令时报错“no leader”
  2. 记录待迁移Data节点上的分片副本信息
  3. 迁移节点,sts加affinity节点调度规则,重建pod和pvc,完成后恢复sts
  4. 分片数据和元数据完全丢失,backup工具缺失,考虑逐个恢复分片副本
  5. 迁移节点重新加入集群 influxd-ctl remove/add-data
  6. 截断热分片truncate-shards(集群中所有写入最新数据的分片),在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了原有分片的热分片副本,新数据写入这个副本
  7. 再从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复
  8. 检查分片恢复情况

故障矩阵: 架构 => 2 data 3 meta 副本因子为2

  1. 挂1个data -> 节点迁移、重搭
  2. 挂2个data -> 备份恢复
  3. 挂1个meta -> 未知
  4. 挂两个meta -> 未知
  5. 挂三个meta -> 未知

4.14 DB值班开始 ; )

DBCLOUD开源DB报警群(实例),DBEngine告警群(机器),致命告警->数据库值班告警处理群,MariaDB/MySQL/MongoDB/PostgreSQL 常见问题,,

  • MariaDB容器数据盘使用率过大
    1.异常内容:容器数据盘占用率超过90%
    2.问题定位:
    1)/var/lib/mysql 目录下存在大量临时文件(如 #sql_1_44.MAD,大小达185G)
    2)show process 查看未提交的长事务(WITH RECURSIVE 查询和 Sending data 状态,确认这些递归查询正在生成大临时表)持有临时表资源,导致文件无法自动清理。
    3.处理方案
    1)KILL 15443008, 15443009, 15443010; // 终止进程(替换为实际ID)临时kill了超长事务连接,临时文件自动清理了(/var/lib/mysql挂载点空间得到释放)
    2)联系用户优化sql
  • MariaDB实例备库IO线程停止
    1.告警内容
    实例 IO 线程停止.通常由于无法连接到主库.
    2.问题定位:
    1)服务可用性->观察到发生主从切换,发生时间符合告警情况
    2)kubectl get pod -n mariadb -Lrole -Lhealthy->从库(原主库切换而来)健康为no
    3)kubectl descirbe 从库pod,观察到mysql container发生terminated,OOMKill,,
    4)监控指标:内存缓慢提升->考虑扩容,暴增->dataspace诊断看慢sql
    3.处理方案
    1)备库重搭
    2)联系用户扩容/优化慢sql

数据库开发&值班暂停

4.23 Mops需求:NBU备份自动化

4.25-5.8:手动验证全流程+调通API,分阶段做,卡点及时同步到群聊.. 官网找接口文档/厂家提供,postPolicies接口参数调不好,就先手动设好用get查出来构造requestBody..
5.9-5.13:理清自动化接入和改造方案(先看一期代码,考虑接口和表能否复用),主动拉评审会议
5.14-5.16:开发及时理清需求原型和改造点,开发备份域配置管理界面
5.19-5.23:重难点=》作业平台下发备份客户端安装ansible脚本改造,根据传参when指定不同task,实现在target主机安装指定平台的client,expect实现交互式流程
5.26-5.29:NBU备份申请自动化开发,实现BackupNbuService(备份平台NBU涉及代码,构造RestTemplate调API);备份域配置“增删改查”,分页,模糊查询,@Transation处理先删后插/先改后改/先删后删。。延期->0605
6.3:自测=》页面上发起请求获得requestBody/通过postman调用本地起的后端服务,调试=》注释排查法/计算器debug
6.4:发sit,延期->0612;完善todo
6.5:自测,业务验收,ddl&dml(注意StringEncryptor加密的密钥随env变化!!) 发版,存在问题待完善
11.13:产品化客户验收(产品化环境开发、、接入客户环境、、)

  1. 统一运维平台:就是接各种需求,把主机创建、备份等操作自动化
  2. Ansible入门:脚本在特权机上,指定targetIp执行,,playbook为入口,roles/tasks实现具体逻辑,,安装client注意预检查,常量写在var文件,脚本写在flie/script.py通过scp传到target机器执行
  3. CRUD基本功:1. 分页,baseMapper.selectPage(new Page<>(page, size), getQueryWrapper(req)) 2. 模糊查询,queryWrapper.eqIfNotNull(BackupConfigPO::isDeleted, 0).likeIfNotNull(BackupConfigPO::getConfigKey, condition.getBackupRegion()) 3. 模糊查询条件涉及表中text类型字段的内容为json,本质还是处理wrapper,queryWrapper.apply(“JSON_SEARCH(config_desc, ‘one’, ‘%” + req + “%’, NULL, ‘$.req’) IS NOT NULL”);

6.9 2025成长对话(4.22) & 年中总结

  • 制定2025年度重点工作计划。
    快速开发InfluxDB服务化需求,持续优化已有功能;深度熟悉开源数据库,确保提供稳定服务。对齐其他数据库产品,了解用户实际需求,多方面考虑设计方案。做好值班任务。

  • 希望提升的1-3项核心能力项,计划如何提升。
    1 深入技术栈学习。阅读InfluxDB源码,学习数据库架构设计,K8s应用课程等,掌握高频业务场景的原理和运维技巧。
    2 高效合作开发的能力。在全面思考,明确需求后开始开发,遇到卡点快速解决。

  • 面向未来1年的职业规划。
    本岗位沉淀
    在这个阶段希望提高自己的工程能力,能比较全面地思考设计方案,快速开发和交付需求;还应该具备产品侧思考的能力,对一个系统有深入的认知,能够独当一面,做到专精一个领域。

  • 工作成果
    1.InfluxDB服务化体系构建
    完成父子实例模式改造,新增InfluxDBMeta规格体系,实现节点级独立变配能力,为后续节点重启、迁移奠定基础;
    针对Data节点迁移提出 “热分片截断-冷副本恢复”双阶段法,通过多线程大流量写入压力验证(2000 points/s),解决开源工具增量数据丢失问题;
    参与数据库运维工作。
    2.NBU备份自动化开发
    支持备份平台自动化运维需求,开发基于Ansible的客户端安装脚本,整合NBU API,实现备份申请自动化;
    开发备份域配置管理界面(增删改查+JSON字段模糊查询)

  • 能力提升
    在InfluxDB服务化需求开发中锻炼了“场景抽象-方案验证”的能力,从寻找开源社区方案到定制化能力开发(如备份集恢复、节点迁移及数据一致性保障),梳理故障矩阵(如单Data宕机、多Meta宕机的应对策略);在NBU备份自动化需求开发中能够快速上手Ansible脚本,复用已有能力,与用户及时沟通并完成开发。

  • 存在不足
    数据库开发方面需要积累技术深度,全面考虑方案并推动评审;自动化运维需求开发可以积累解决方案,缩短交付周期。

6.12 智能体人才认证(一级)

0. ChatGPT的基本原理及应用实践分享
  • what is 大语言模型
    流式输出(逐字计算概率),基于Transformer神经网络(本质上一个Encoder+Decoder结构,自然语言 ⇄ 机器理解)
  • why ChatGPT
    除了卷大模型(参数量)&大数据量,有着更好的交互原因:1 指令微调? 2 基于人类反馈的强化学习
  • 提示工程 Prompt Engineering
    提示词尽量简单、明确,最好完整描述以下关键要素:1 指令 2 上下文 3 输入数据 4 输出指示
    提示词使用技巧:1 明确提出(不)应该做什么 2 提供输出的格式提示 3 使用特殊符号指令将需要处理的文本分开 4 增加示例,少样本提示 5 增加任务角色(Role)或场景
  • 应用场景实践
    本地知识库问答:从本地知识库构成的文本向量库中搜索相关知识+用户问题 =》一个提问(增强Prompt 如:“基于以下知识:{text1}…{textN},回答:{question}”)=》 LLM(如 ChatGLM2、GPT-3.5)读取该 Prompt结合自身语言能力生成最终回答;模型参数提供语言能力,但不存储动态知识。语言模型的“理解能力”本质是参数化的统计规律,通过海量通用文本训练获得。专业领域适配需针对性选择微调或 RAG 策略,二者互补而非互斥。当前技术趋势是:通用大模型作“引擎”,领域知识库作“燃料”,Prompt 工程作“方向盘”,三者协同实现高效、低成本的专业化智能问答。
1. 学习 LLM
  • 大语言模型 LLM 的“理解能力”来源:参数、训练与概率生成
    1. 60亿参数的本质
      参数是什么?神经网络中神经元连接的权重值(浮点数矩阵),例如 ChatGLM2-6B 的 60 亿参数即其网络权重总量。
      参数如何产生?通过海量无监督预训练:模型从数万亿 token 的通用文本(网页、书籍、百科等)中学习语言统计规律。例如:GPT-3 训练数据:45TB 原始文本 → 过滤后 570GB,包含近万亿 token;训练目标:预测文本中遮蔽词(如 “猫喜欢抓__” → “老鼠”)或续写句子。
    2. 参数如何实现“理解”?
      概率建模:LLM 本质是概率生成器。给定输入文本,模型计算下一个词的概率分布(如 “天空是___” → “蓝色”概率 80%,“绿色”概率 0.1%);
      上下文编码:通过 Transformer 的自注意力机制,模型捕捉长距离依赖(如代词指代、逻辑关联);
      知识内化:训练中高频出现的知识(如 “水的沸点是 100°C”)被编码到参数中,形成“通用知识库”。
    3. 训练成果与参数的关系
      训练完成后的参数 = 固化后的语言规律与知识表示;
      生成过程:根据输入 Prompt 的语义,激活相关参数路径,按概率生成符合语言习惯的文本。
  • 专业领域模型训练:微调 vs. 知识库增强
    1. 全参数微调(Full Fine-tuning)
      方法:在领域数据上继续训练模型,更新全部参数(如用医疗文献训练 ChatGLM2);
      效果:模型深度内化领域知识,生成更专业、连贯的文本;
      成本:需大量领域数据(GB 级)和 GPU 算力(如 8×A100 训练数天)。
    2. 高效参数微调(PEFT)
      方法:仅训练少量新增参数(如 LoRA、Adapter),冻结原模型参数;
      优势:节省 90% 算力,适合中小机构;
      适用场景:领域术语适应(如法律条文格式),但无法新增未训练过的知识。
    3. 知识库增强(RAG)的定位
      核心价值:无需训练模型,直接注入动态更新的领域知识(如企业最新产品文档);
      局限:依赖检索质量,复杂推理能力受限于 LLM 本身。
  • DeepSeek 之所以能广泛回答各领域问题,并非因为对所有领域都做过“专门训练”,而是通过大规模通用预训练 + 领域增强技术 + 智能调度机制实现的;
    1. 基础:海量通用预训练(广度覆盖)
      DeepSeek 的底层模型(如 DeepSeek-R1)在训练初期使用数万亿 token 的互联网公开文本,覆盖科技、教育、历史、文化、生活、基础学术等广泛领域。
      效果:模型能对大多数常识性问题生成合理回答,类似一个“受过通识教育的聪明助手”。
    2. 增强:垂直领域优化策略(深度强化)为提升专业领域表现,DeepSeek 采用以下技术实现“泛中求精”:
      混合专家模型(MoE):模型内部划分多个“专家子网络”(如医疗、法律、编程等),根据问题自动激活相关专家;
      领域微调(Fine-tuning):对金融、法律、医学等专业领域,用高质量数据二次训练模型,优化参数
      检索增强生成(RAG):对动态知识(如实时政策、企业数据库),通过外部知识库检索最新信息,再生成答案;
    3. 调度:智能路由与知识管理
      动态路由机制:用户提问时,模型自动判断问题类型,分配至: 通用知识层(如“水的沸点是多少”);专业模块(如“心肌梗死的最新诊疗指南”); 外部检索(如“2025 年光伏产业新政策”)。
      知识更新与纠偏:用户反馈可修正错误答案(如律师指出法律条文解读偏差); 结合知识图谱持续更新事实库,减少“知识过期”问题。
    4. 用户建议:如何获得更专业回答?
      明确领域身份: 提问时声明“以金融分析师身份,分析光伏产业趋势”,引导模型调用专业模块。
      开启深度思考模式: 对逻辑问题(如数学、编程),勾选“深度思考(R1)”提升推理质量。
      补充专业资料: 上传领域文档(如论文、手册),用 RAG 增强答案准确性。
      微调定制专家: 企业用户可通过 LoRA 微调,训练专属领域模型(如“医疗问诊助手”)。
2. 大模型提示词工程基础
  • 提示词基本要素
    1. 指令:想要模型执行的特定任务或指令
    2. 上下文:包含上下文信息,引导模型更好地响应
    3. 输入数据:用户输入的内容或问题
    4. 输出指示:指定输出的类型或格式
  • 提示词工程进阶技术
    1. 少样本提示:可以作为一种提示词,以启用上下文学习,我们在提示中提供演示以引导模型实现更好的性能。
    2. 链式思考(CoT)提示:提出问题的同时提供自己的推理方法,供LLM学习参考
    3. 检索增强生成(RAG):如本地知识库问答。从本质上讲,RAG包括一个检索组件、一个外部知识数据库和一个生成组件。整体流程:RAG需要从外部知识数据库中获取文档,然后将这些文档与用户的查询一起被传输到LLM,用于生成响应
    4. 自动推理并使用工具 (ART):?接到一个新任务的时候,从任务库中选择多步推理和使用工具的案例。在测试中,调用外部工具时,先暂停生成,将工具输出整合后继续接着生成。
    5. 自我反思(Reflexion):?自我反思是一个通过语言反馈来强化基于语言的智能体的机制。
      • 在高层次上,自我反思将来自环境的反馈(自由形式的语言或者标量)转换为语言反馈,也被称作 self-reflection,为下一轮中 LLM 智能体提供上下文。
      • 这有助于智能体快速有效地从之前的错误中学习,进而提升许多高级任务的性能。
3. 如何从0-1开展Prompt工程项目
  • Prompt就是给AI的指令,引导大模型生成响应回答。
    进阶例子:“现在你是一名xx专家,以下是xx内容,你的任务是对内容进行xxx,让我们一步一步做:1. 做/不做xx 2. 以json格式输出… 3. …”
  • 什么是Prompt工程?
    是业务服务应用大语言模型的中枢,用于释放LLM的能力,包括 1)单个任务的Prompt撰写调试 2)多个任务Prompt的设计组合
    如:撰写短视频文案 =》 1. 分析热门文案 2. 结合商品信息生成文案
  • Prompt工程开展方式
    根据生成的可行性与效果,明确对模型的输入、输出目标,经设计/调试后,生成效果达成业务目标后,固化为应用服务。

6.20 一个尝试:生成编程指令Prompt

  • 你是一个资深编程助手,擅长根据用户诉求生成可执行的编程指令。让我们一步步做:
    1. 将用户诉求拆解为清晰、可执行的原子化步骤。
    2. 注意环境适配(例如 Linux/macOS 环境优先使用 Bash/Python,Windows 环境优先使用 PowerShell)以及进行包依赖检测。
    3. 考虑错误处理,如网络请求需设置超时(例如 ‘timeout 3’)
    4. 输出规范,代码必须包含完整可执行结构和注释(每20行至少1个注释)
    5. 按照以下格式返回:{步骤拆解:””,生成程序:””}
    6. 不受示例语言的限制,选择最适合的语言实现。
  • 这是一个示例AI问答
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ## 输入
    写一个能够在Linux环境使用的脚本,检测对指定IP和端口是否连通。

    ## 输出
    ### 步骤拆解
    1.参数检查:验证输入参数格式(IP 端口 或 -f 文件)
    2.端口测试:依次尝试 nc → telnet → /dev/tcp 三种方式(3秒超时)
    3.批量处理:支持从文件逐行读取 IP 端口(跳过空行和注释)
    4.结果输出:直接显示开放/关闭状态

    ### 生成程序
    ----------------------------------------------------------------
  • 这是用户的正式诉求(输入用户诉求。)

6.27 Mops需求:负载均衡变更自动化

6.27:页面流程理解+看“申请”自动化代码->考虑改造点,找api,输出文档
6.30-7.4:需求对齐,减少开发量的机会,,对于依赖项的变更(healthCheck->ResourcePool-VirtualServer)采用“蓝绿发布”=》先增后删
7.5-7.9:开发完成
7.10-7.11:自测,前端发起一次请求F12取payload / 后端接口处打印入参 + 按需修改 =》构造入参,走通流程,只需关注新加的代码(无关逻辑如校验/审批代码可以先注释掉。。)
7.14:变更成功后回滚。。手动修改+提sql同步配置;思考=》是不是可以直接另提一次变更,或者另外提供回滚接口,,总之此时回滚和前一次变更已经没有关联
7.17:上线。修复 1 管理员节点自动带出尽可能多参数 2 支持定时执行 3 思考:checkChangeSuccess()方法期望同步response,但方法里调API返回fail会隔10s重复十次,就是一直fail的话接口会隔100s才返回数据,假如加了一个定时任务扫描出待变更工单列表,用for循环执行change()并且各调用了checkChangeSuccess(),会发生什么,for循环卡住?”springboot项目,写了一个变更c…”点击查看元宝的回答
https://yuanbao.tencent.com/bot/app/share/chat/qqYiHTHYRWDv
7.29:支持定时执行,注意避免同一个工单被多次扫除(未完成变更后状态->EXECUTED),注意调度任务下发到本地节点,可能由于代码版本不同造成“灵异事件”
10.16:支持变更页面上的所有参数
10.30:支持回收自动化
11.13:优化提交工单时校验逻辑,避免重复补工单(按需增加存表字段&管理端加编辑后门)

  1. f5理解:F5 BIG-IP 是业界主流的硬件/软件负载均衡解决方案。配置好负载均衡后,一个请求从客户端到后端服务器的生效流程涉及多个关键环节,其核心是​​客户端访问域名 → DNS解析至F5的VIP → F5转发请求至Pool Members → 后端处理并返回响应
  2. Mops服务架构。。用户提交工单-> iflow审批流-> 回调/前端调接口-> 传入xxTicketVo-> convert成Dto/Po处理业务逻辑
  3. 蓝鲸。。统一运维平台竞品
  4. 自动化运维开发总结:工单,,提效,,用户,,,

6.28 Alibaba Java开发手册学习

1. 计算机基础

2. 面向对象

解析值。。序列反序列parse,,JSONNode,,

3. 代码风格

  1. 魔法值 => enum
  2. 变量一般以小驼峰格式命名,但有一种特殊情况:定义类成员变量时,特别是POJO类中,针对布尔值类型的变量不要以“is”开头,而是将数据表中的“is_xxx”字段映射到POJO类中的属性“Xxx”(如is_deleted =》Deleted)
  3. 文档注释 /** */ 加上创建和修改时间,写在代码上方,, Idea怎么配置??

4. 走进 JVM

5. 异常与日志

  1. where to throw Exception?who to solve?how solve?
    如果异常在在当前方法的处理能力范围内且无需透出,就直接捕获异常并处理;否则向上抛出,由上层方法或框架来处理。
    如果在方法内部处理异常,需根据业务场景定制处理,如重试、回滚(还有 ticketDetail.setExecutionStatus(xxEnum.FAIL) 返回)等操作;如果向上抛出异常,需要在异常对象中添加上下文参数、局部变量、运行环境等消息,便于排查问题。
    考虑设计业务逻辑,无论在哪一步终止业务,都能让外界感知此时的状态,并保留错误信息(ststus,log)
  2. 异常分类
    • Error(致命异常),不可控错误,如StackOverFlowError、OutOFMemoryError
    • Exception(非致命异常)
      • checked异常(受检异常)例如:IOException, SQLException。
        • 无能为力型,如SQLException,只好保存现场人工介入
        • 力所能及型,如发生非授权异常可跳转权限申请页面
      • unchecked异常(非受检异常)是运行时异常,继承自 RuntimeException,更像是由 业务逻辑可能导致的异常
        • 可预测异常,如IndexOutOfBoundsException, NullPointerException,应该提前做好边界检查而不是抛出
        • 需捕获异常,如Dubno框架进行RFC调用时产生的超时异常DubboTimeoutException,客户端不能因服务端异常导致不可用,可以重试或降级处理
        • 可透出异常,如Spring框架中抛出的NoSuchRequestHandingMethodException,框架会自行将异常映射到合适的状态码如404
  3. throws关键字用于声明一个方法可能抛出的受检异常(checked)。BusinessException 通常是一个运行时异常,也就是非受检异常,不需要在方法签名中用 throws 声明(显式捕捉和处理),因为它通常表示业务逻辑错误,而不是程序错误。非受检异常的设计目的是让开发者在编写代码时不必显式地捕获或声明它们。
    但是,仍然需要确保在适当的地方捕获和处理,特别是在应用程序的边界层(如控制器层)进行统一的异常处理。??
  4. ​​防御式编程,可以让方法返回null,,防止空指针异常(NPE)上调用方的责任,需要事先判断
  5. 需定位报错行数 → 必须打印 e​​(传入异常对象)会输出完整的堆栈(e.printStackTrace())跟踪,包括类名、方法名、文件名和行号。
    ​​仅需错误描述 → 使用 e.getMessage()​​(适用于前端提示或自定义消息)。
    1
    2
    log.error("查询VirtualServer信息异常:"+e.getMessage(), e);
    throw new BusinessException("查询VirtualServer信息异常:" + e.getMessage());

6. 数据结构与集合

7. 并发与多线程

8. 单元测试

9. 代码规约

todo


7.17 Mops & CMDB发版

“一个Java系统开发完了,是怎么跑起来…” https://yb.tencent.com/s/jj0KGxxEGgzU
“IDEA调试、线上运行,Maven、SpringCloud…” https://yb.tencent.com/s/GB2sYz5WPHbB
集成,部署 “所谓的微服务实际上是怎么部署的呢…” https://yuanbao.tencent.com/bot/app/share/chat/gt96bRkSxuos
K8S&微服务部署方案 “k8s和springcloud是如何关联…” https://yuanbao.tencent.com/bot/app/share/chat/H2FtlSdK24y1

MOPS 开发,测试,发版流程

  1. 开发阶段
    • main:生产环境稳定分支,仅用于发布,禁止直接修改。
    • develop:开发/测试/发版分支,功能合并主干。前一个feature/v1.1.0发版完成后,拉出最多下两个版本的开发分支,如 bugfix/v1.1.0和 feature/v1.2.0,发版完成后合入线上分支和下一个版本分支。
    • feature/xxx:团队开发各自从develop切出的功能分支,开发完成后合并回去,发sit联调,uat验收,发版。
    • bugfix:main切出的紧急修复分支,修复后合并至main和当前develop。
  2. 测试阶段
    • SIT(系统集成测试):前后端联调测试环境,验证功能集成与基础流程。
    • UAT(用户验收测试):预生产环境,业务方验证业务逻辑。关键点:数据与生产环境隔离但配置一致,避免环境差异问题。
  3. 发版准备
    • 发版材料:包含需求清单,DDL&DML,配置变更清单。提交变更实施文档、代码质量报告、版本号(从最新git记录的Copy Revision Number)
    • SQL变更:提变更单,先DDL后DML,设置发版窗口执行,记录到db文件上传git。
    • 针对本次变更的配置备份、数据备份,确保有完整的回滚步骤。
  4. 上线验证
    • 发版分支打Tag(如feature-v1.33.0,相当于是一个快照,后续再合代码要打新tag)
    • 持续集成(指定tag,发版前提前集成)
    • 检查提的sql是否变更成功(提醒先到uat执行一遍)
    • 持续部署,可分批部署到多节点,减小用户影响(提前到uat部署一遍)
      • 灰度发布:先切10%流量验证,逐步全量。
      • 蓝绿部署:并行两套环境,切换流量实现零宕机。
    • 流水线:集成+部署
    • 发版验证:避免业务高峰期发版,异常=》监控平台搜“error”排查,以及先回退旧版本(集成旧tag部署)
    • 发版成功:发版分支合到main和下一个版本分支,SQL变更提交到MOPS_SQL
  5. 事件管理
    • 事件指导致或可能导致服务中断或服务质量下降的任一事态
    • 以恢复业务为第一要务,72h内进行根因查找与5C闭环

CMDB 部署指令

  1. 下发介质(jar包)
    将服务的可执行文件(通常是一个 .jar )下发目标服务器。这个 .jar 文件是通过构建工具(如 Maven 或 Gradle)打包生成,包含了应用程序的所有代码、依赖库和资源文件。
  2. 停止旧服务
    停止正在运行的旧版本服务,释放端口和资源。kill -15:优雅地终止进程,避免强制终止(kill -9)可能导致数据丢失或资源未释放。
  3. 检查进程
    确认服务进程是否已经成功停止。ps -ef:列出所有进程及其详细信息。grep ${package_name}:过滤出与服务相关的进程。
  4. 备份文件
    在部署新版本之前,cp 备份旧版本的 .jar 文件和相关配置文件,以便在新版本出现问题时可以快速回滚。
  5. 下发 OneAgent(监控工具)
  6. 配置服务(生成配置文件)
    通过 Shell 脚本动态生成服务的配置文件(如 application.yml),用于定义服务的端口、文件路径、Redis 配置等。可以根据环境(如开发、测试、生产)动态调整配置。
  7. 启动服务
    nohup /usr/bin/java ${C_CMDB_XXL_JOB_EXECUTOR_ONEAGENT_OPTS} -Xms8g -Xmx8g -jar -Dspring.config.location=xx/application.yml -Djasypt.encryptor.password=xx xx.jar > xx.log 2>&1 &
    启动 Java 服务,并将日志输出到指定文件。
    允许服务在后台运行,即使关闭终端,服务也不会停止。设置 JVM 的初始堆内存为 8GB,设置 JVM 的最大堆内存为 8GB。
    指定 Spring Boot 的配置文件路径,-Djasypt.encryptor.password:指定加密配置的解密密码,指定要运行的 .jar 文件。将标准输出重定向到日志文件,将标准错误输出重定向到标准输出。
  8. ps检查服务是否启动成功

7.18 MOPS & CMDB 运维日志

2024.8.13 Helloworld

运维人员 -> 办公电脑 -> 访问入口 -> 堡垒机(安全审计核心) / 跳板机(简易通道) -> 内网 -> 物理机 / Linux后台(应用载体)

Linux基本命令:
netstat ; top; awk ; dstat; iostat; lsof; free, df;uptime;dmesg ;dig ; nslookup

vim/vi的基本快捷命令(gg, shift + g, :n ; dd, :%d ; yy, p ,u等)

终端的一些快捷命令(ctrl + a; ctrl + e)

2025.10.29 CPU高:

  1. 内存fgc导致 —针对进程 oom?
    先dump,下载heap文件
    jmap -dump:live,format=b,file=heap.hprof 1651260
    通过Eclipse Memory Analyzer (MAT)工具分析heap文件
  2. 线程导致
    找出排查cpu高的线程对应的方法:
    1)top。找出对应的高cpu的pid
    2)top -Hp pid。打印出pid里的线程,找出对应的cpu高的线程tid
    3)jstack -l pid > jstack.txt。打印对应的进程堆栈信息
    4)printf “%x\n” 。找出对应16进制的线程id
    5)vi jstack.txt。搜索第4步的线程id,看其方法
  3. 机器资源不足

2025.11.30 CMDB Zookeeper磁盘占用过高的SOP

  1. 告警确认
    • 监控告警内容:磁盘使用率>80%
    • 初步排查:通过跳板机连接CMDB主机(FinalShell + SSH)逐层定位,确认Zookeeper目录为占用源头(如/apps/zookeeper/v2)
  2. 清理旧日志,并且再次运行 df -h 确认使用率下降。告警解除。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     # 确认分区使用率
    97 2025-07-18 17:38:17 df -h
    # 找到top5磁盘占用的子目录
    98 2025-07-18 17:38:19 du -sh /apps/* | sort -rh | head -n 5
    # 逐步排查子目录 tab自动补齐路径
    99 2025-07-18 17:38:29 du sh /apps/svr/*
    100 2025-07-18 17:38:38 du sh /apps/svr/zookeeper-3.4.6/*
    101 2025-07-18 17:38:44 cd /apps/svr/zookeeper-3.4.6/data
    106 2025-07-18 17:39:16 du -sh *
    107 2025-07-18 17:39:22 cd version-2/
    # 分析日志文件(每日日志61MB积累2个月未清理)
    108 2025-07-18 17:39:26 ll
    # 删除两个月前的日志 检查效果
    109 2025-07-18 17:39:52 find . -type f -name "snapshot.*" -mtime +60 -exec rm -f {} \;
    110 2025-07-18 17:39:57 ll
    112 2025-07-18 17:40:03 df -h
    123 2025-07-20 21:50:41 history
  3. Zookeeper日志管理优化
    配置自动清理:zoo.cfg中启用参数 autopurge.purgeInterval=24(每24h清理),autopurge.snapRetainCount=7(保留7个快照)
    日志轮转,集成Logrotate:配置日志按大小/时间切割并压缩:/etc/logrotate.d/zookeeper 中设置 daily, rotate 30, compress
    自动化清理脚本:编写定时任务,每月清理旧日志(保留30天) => 0 3 * * * find /apps/zookeeper/version-2 -mtime +30 -delete
  4. 为什么df看到 /apps/ 还大?
    逻辑上,层级上,所有目录都在根目录/下面,包括/apps。
    但/apps很可能是一个独立的「挂载点」(单独分区)。可以理解成电脑有一块系统盘挂在/(系统分区,空间小),又插了一块数据盘挂在/apps(数据分区,空间大),它们物理上是两块盘。可以通过 mount | grep apps 进行验证。。

2026.6.12 CMDB 微服务单日志持续写入60G

  1. 现象:data-center 服务写入单个日志文件,持续膨胀、磁盘使用率告警,日志内容可清理
  2. OS 核心概念
    • 文件名:存在目录里,只是一个「名字标签」,用来让人 / 命令行找到文件/inode,不是句柄。
    • inode:文件的本体,存真实数据、权限、大小、磁盘块地址,数据真正存在这里。
    • 文件句柄(FD):进程内部的数字编号,是进程打开文件,拿到 inode后,内核分配给进程的 “访问通道”,后续读写只靠句柄,不再依赖文件名。
    • Linux删除文件的判定规则:预用计数,包括文件名的关联和进程的文件句柄
  3. 标准在线清理 SOP(优先使用,业务零中断)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    df -h
    # Java服务(devops部署)堆积日志的情况与中间件(zookeeper是eops部署/apps/svr目录)有所差异
    cd /apps/devops/data-center/
    # nohup部署命令中指定的服务日志清空掉(Java服务本身还指定了输出/apps/data-center/log/xx)
    cat /dev/null > app.log
    # 验证磁盘空间(最优先,确认是否真释放)✅ 预期:对应分区使用率明显下降 → 清空生效。
    df -h
    # 验证文件真实大小(绕过目录缓存)✅ 预期:文件大小接近 0 → 内容已清空。
    # ps:此时ls -lh /data/logs/app.log大概率仍显示旧大小(目录缓存未刷新),属于正常现象,不是操作失败。
    du -sh app.log
    # 验证日志正常输出(确认业务无影响)✅ 预期:持续打印新业务日志 → 进程句柄正常,业务运行正常。
    tail -f app.log
    如何让 ls/ll 显示真实文件大小?不重启服务、重载 Java 日志文件(Logback/Log4j2 适用):
    1
    2
    3
    4
    5
    6
    # 1. 查找Java应用PID
    ps -ef | grep java | grep -v grep
    # 2. 发送重载信号,日志框架重新打开文件、刷新目录缓存
    kill -USR1 进程PID
    # 3. 再次查看,ll大小恢复正常
    ls -lh app.log
  4. 错误操作复盘
    假设初始状态:日志路径 /data/logs/app.log,Java 进程长期打开该文件,持有 文件句柄 FD=10,绑定唯一 inode-A
    1
    2
    3
    4
    5
    6
    7
    8
    # 1:执行在线清空(命令通过文件名找到inode-A并清空数据块,Java进程仍拿着FD=10向原inode-A写日志)
    cat /dev/null > /data/logs/app.log
    # 文件仍显示 60G(目录缓存未刷新)
    ll /data/logs/app.log
    # 实际磁盘空间已经成功下降
    df -h
    # 服务器内存瞬时暴涨,波动仅持续几秒后自动恢复
    top + E/F
    因疑惑ll大小未变化,以下做了错误操作..
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 2(错误):直接删除活跃日志文件,目录中文件消失,但Java的句柄FD10仍保留,仍持续写数据到inode-A
    # rm 只做了一件事情———将文件名和inode-A的关联删掉,但只要还有进程持有inode-A的句柄,数据就仍保留
    rm /data/logs/app.log
    # 目录里看不到文件app.log,但内核中文件实体若存在、持续被写入 → 成为幽灵文件(deleted file)
    # lsof | grep deleted 遍历系统中所有进程持有的文件句柄,表示该进程(1)在通过句柄(2)写入inode(3)和幽灵文件(4)
    # java(1) 12345(进程ID) root 45r(2) REG 253,0 62914560(3) 1234567(3) /app.log (deleted)(4)
    lsof | grep deleted
    # 3(无效操作):手动重建同名文件,新文件大小始终为 0,应用日志不落到该文件
    # 因为新文件创建并且指向了新inode-B,但Java进程仍绑定inode-A(幽灵文件)
    touch /data/logs/app.log
    # 4(最终解决):查找运行中的 Java 进程并停止服务,OS强制回收该Java进程的所有文件句柄,FD10被关闭
    # inode-A再没有任何引用,内核彻底回收inode-A及占用的内存空间,幽灵文件消失
    ps -ef | grep java
    kill -15 xxx(子进程id
    # 重启Java服务,读取配置中的文件名 /app.log 并且找到新的inode-B,OS分配新的文件句柄
    # 观察到日志正在写入新的日志文件了
    ll /data/logs/app.log
    tail -f /data/logs/app.log
  5. 日志备份清理 方案 A:cp + cat /dev/null >
    1
    2
    cp 原文件 → 备份文件(inode-B)   # 复制数据快照
    cat /dev/null > 原文件 # 清空 inode-A 数据块
    优点: 脚本简单,无需关心 Java 进程, Java 进程完全无感知,不需要发任何信号, 日志连续写入同一 inode,无切换风险
    缺点: cp 期间磁盘空间短暂翻倍(100GB 文件会占用 200GB),耗时较长(100GB ≈ 8 分钟),期间磁盘 IO 高;cp 完成到 cat /dev/null > 之间极短窗口内的日志会丢失(毫秒级,可接受)
  6. 日志备份清理 方案 B:mv + touch + kill -HUP
    1
    2
    3
    mv 原文件 → 备份文件              # 瞬间重命名,inode-A 变成备份文件
    touch 原文件 # 新建 inode-B
    kill -HUP Java进程 # 通知 Logback 重新 open,切换到 inode-B
    优点: mv 瞬间完成,不占额外磁盘空间,无翻倍风险, 适合超大日志文件场景
    缺点: 脚本复杂,kill -HUP 到 Logback 真正重新 open 之间有极短窗口,新日志仍写入备份文件(inode-A)
  7. Java日志系统
    1
    2
    3
    4
    5
    6
    7
    8
    业务代码
    log.info(...) ← 调用 SLF4J 接口

    SLF4J ← 门面,转发给具体实现

    Logback ← 实际写文件,读 logback-spring.xml 配置

    data-center-1.0.log ← 磁盘文件

2026.6.7 CMDB Kafka偶发性消息堆积

  1. 现象:每周日2:30出现某topic消息堆积,1h后开始下降恢复到正常
  2. 根因:CMDB主机采集入库定时任务触发大量CMDB消息订阅回调,Kafka消息生产激增,但该topic的6分区只有1消费者组的1个消费实例在消费,生产 > 消费 → 堆积
  3. 处理:虽然堆积激增,但最终能够消费掉,并且broker磁盘容量健康,无需调整消费示例,可以调高告警条件→消息堆积数>1000k

2026.6.22 CMDB MongoDB实例异常重启排查SOP

  1. 登录崩溃节点,找到对应时间段的日志文件:
    1
    ll /apps/logs/mongodb/archive/
    确认是否为进程崩溃 Got signal: 6 (Aborted)(而非被外部 kill - Got signal: 15 (Terminated)):
    1
    grep "Got signal" /apps/logs/mongodb/archive/mongod30000.log.20XX-XX-XXT17-00-0X
  2. 提取崩溃前后日志窗口,定位最严重的慢查询
    1
    2
    3
    # 替换为实际告警时间,格式:T小时:分
    grep "2026-06-21T07:0[0-1]" /apps/logs/mongodb/archive/mongod30000.log.2026-06-21T17-00-01 > /tmp/crash_window.log
    cat /tmp/crash_window.log
    重点关注崩溃前最后几秒内:有哪些 COLLSCAN(全表扫描)、耗时超过 1000ms 的查询、docsExamined 数量级是否异常大
    找所有 COLLSCAN 慢查询,按表名分类,重点排查 docsExamined 最大、耗时最长的那条,提取完整 filter:
    1
    2
    grep "COLLSCAN" /tmp/crash_window.log | grep -oP '"find": "\K[^"]+' | sort | uniq -c | sort -rn
    grep "目标表名" /tmp/crash_window.log | grep "COLLSCAN"
    从输出中找:
    1
    2
    3
    filter: { field1: "xxx", field2: "yyy" }   ← 这是需要建索引的字段
    docsExamined: 213667 ← 扫描量
    nreturned: 1 ← 实际返回量,两者差距越大越需要索引
  3. 确认副本集状态和主节点
    用 MongoDB Compass 连接集群,在 Compass Shell 执行:
    1
    2
    3
    db.hello()
    // 看 primary 字段,确认主节点 IP
    // isWritablePrimary: true 表示当前连的就是主节点
    注意:3节点中若有一个不在 hosts 列表里,通常是仲裁节点(Arbiter),不存数据,无需关注。
  4. 在主节点上建复合索引,会自动同步从节点。Compass 界面操作 或 Shell 操作(等价):
    1
    2
    3
    4
    db.目标表名.createIndex(
    { field1: 1, field2: 1 },
    { background: true }
    )
  5. 验证复合索引 ip_1_source_1 生效,在 Compass Explain Plan 标签,填入原始 filter: { “ip”: “172.23.135.194”, “source”: “操作系统-其他IP-采集” },Explain 确认 COLLSCAN → IXSCANdocsExamined: 213667 → 0,耗时 5097ms → 0ms

2026.06.25 CMDB 数据连接器/视图卡顿排查 SOP

服务:ops-data-access(对外接口)PRD | 端口:7119
关联:`ops-synapplication(实际入库)、MongoDB、Redis

  1. 看现象定方向 ??
    写入慢 + 查询也 500 → 看线程监控,Tomcat 线程被写入占满
    只有查询慢/报错 → ops-syn 查询接口 RT 是否异常 |
    只有写入慢 → 本次批量数据量 × ops-syn RT,看是哪个大 |
  2. ops-data-access Tomcat 线程监控
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    最大繁忙线程数
    < 80 ✅ 正常
    ≥ 90 ⚠️ 预警,需关注
    = 100 🔴 已打满,新请求被直接拒绝(accept-count=0)

    **打满时的连锁反应:**
    /storages 批量写入占满 100 个线程
    → /view 查询请求进来没有线程可用
    → accept-count=0,不排队,直接拒绝
    → 调用方收到 500
  3. ops-syn 对比基线 ???
    ops-syn RT 正常 → 问题在本服务(并发量/批量条数太多)
    ops-syn RT 升高 → 问题在 ops-syn 或其 MongoDB
  4. 定位元凶调用方
    1
    2
    3
    4
    5
    6
    // MongoDB 审计表,找大批量写入请求
    db.convertViewRecording.find({
    createtime: { $gte: "2026-06-23 15:00:00", $lte: "2026-06-23 16:30:00" },
    type: 0 // 0=写入
    }, { sysName:1, number:1, createtime:1, endtime:1 })
    .sort({ number: -1 }) // 按数据量排序

20260623 CMDB 服务内存异常事故报告

事故时间:2026-06-23 02:30 ~ 04:50
影响服务:data-center(CMDB 数据中心服务)
事故等级:P1(服务中断)

  1. 事故概述
    1. 问题描述
      在执行日志文件清空操作时,触发系统内存暴涨导致 Swap 打满,引发 Swap 抖动,最终导致 data-center 服务 OOM 崩溃。
    2. 影响范围
      受影响服务:data-center(PID 2129767)
      服务状态:04:08 OOM 崩溃,04:48 恢复
      业务影响:服务中断约 40 分钟,有报错,无投诉
    3. 一句话根因
      系统内存配置本身接近极限,长期依赖 Swap 维持运行。cat /dev/null > 清空大文件时暂时占用系统内存,把热数据挤到 Swap 里,导致内存数据混乱分布且无法自动恢复,最终引发 Swap 抖动和服务 OOM。
  2. 故障时间线与监控数据
    1. 触发期(02:30 ~ 02:32)
      操作:执行 cat /dev/null > data-center-1.0.log(50GB 文件)
      系统反应
      • Load:从正常迅速升至 7.54
      • 内存:Page Cache 暴涨处理文件截断,可用内存从 6.7G 骤降到 600M
      • Swap:从 14%(1.1GB)飙升到 99%(7.9GB)
      • 本质:内核 LRU 算法误判,将 7GB Java/ilogtail 数据(含 4GB 活跃对象)换出到 Swap
    2. 持续抖动(02:32 ~ 04:08)
      • 特征:Load 在 6 ~ 16 反复震荡,可用内存 600M ~ 6G 剧烈波动(锯齿状),Swap 始终 100%。
      • vmstat 数据
        1
        2
        3
        `si`(Swap In)持续 1000~2000 KB/s:系统在拼命从 Swap 读取被访问的数据
        `si` = 1504 KB/s,`so` = **4902 KB/s**:**换入换出同时发生**
        `si` = 182 KB/s,`so` = **4734 KB/s**:仍在疯狂换出
      • 为什么无法自愈
        • Swap 里的 8GB 数据中,约 4GB 是热数据(频繁被 Java/ilogtail 访问)
        • 内核不知道哪些是热数据、哪些是冷数据
        • 需要靠 LRU 算法慢慢试探,但试探过程本身就在持续触发 Swap 抖动
        • 系统陷入”永久震荡”,无法回到正常状态的稳定分布
      • 本质:系统在”拆东墙补西墙”
        1
        2
        3
        4
        5
        6
        7
        8
        9
        Java 访问对象 A(在 Swap)→ Swap In

        内存不够 → 把对象 B 换出到 Swap(Swap Out)

        ilogtail 访问数据 B → Swap In

        内存不够 → 把对象 A 再换出(Swap Out)

        (无限循环)
    3. 服务崩溃(04:08)
      • 直接原因:data-center 服务 OOM
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        ## **触发链**
        Java GC 触发,需要扫描整个堆

        部分对象在 Swap 里 → 触发大量 Swap In

        GC 暂停时间从毫秒级延长到秒级

        内存仍然不足 → OOM Killer 启动

        data-center 进程被杀(PID 2129767)
        1
        2
        3
        # **日志证据**
        [2026-06-23 04:08:08] [ERROR] org.apache.zookeeper.KeeperException$ConnectionLossException
        Background operation retry gave up
    4. 人工介入恢复(04:48 ~ 04:50)
      • 操作步骤
        1. 停止 logagent(释放 5.8GB 内存,其中 3.3GB 在 Swap 里)
        2. 执行 swapoff -a && swapon -a(强制清空 Swap,8GB 数据回到内存)
        3. 重启 data-center 服务
        4. 恢复 logagent
      • 关键:停止 logagent 后,可用内存达到 10GB+,满足 swapoff 的物理条件(需要将 8GB Swap 数据全部读回内存)
      • 最终状态
        • Load: 15.69 → 0.40
        • Swap: 100% → 0%
        • 可用内存: 670M → 7.5G
        • 系统重新回到稳定状态
  3. 根因分析
    • 直接原因——错误的日志清空方式:使用 cat /dev/null > data-center-1.0.log 清空 50GB 日志文件。
      • ✅ 正确:只修改文件元数据,不触发内存暴涨,用 truncate -s 0 data-center-1.0.log
        1
        2
        3
        4
        5
        6
        **触发机制**:
        1. `cat /dev/null >` 使用 `O_TRUNC` 标志截断文件
        2. Linux 内核需要处理 50GB 文件的所有脏页(dirty pages)
        3. 触发强制刷盘,瞬间占用大量内存作为 I/O buffer
        4. 内存不足,内核启动 LRU(Least Recently Used)回收算法
        5. **将 Java 服务的 7GB 活跃数据错误地换出到 Swap**(本应只换出冷数据)
    • 根本原因——系统内存配置接近极限,长期依赖 Swap 维持运行
      • 正常状态下的内存分配(稳态)
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        物理内存 13.65GB(91% 使用率):
        Java 活跃堆 + 堆外内存: 10GB
        ilogtail(活跃部分): 2.5GB
        OS + buff/cache: 1.15GB

        Swap 1.1GB(14% 使用率):
        Java 老年代僵尸对象: 0.6GB ← 几天不访问一次(冷数据)
        ilogtail 历史缓存: 0.3GB ← 已归档日志(冷数据)
        其他冷数据: 0.2GB

        ────────────────────────────
        实际总需求: 13.65 + 1.1 = 14.75GB ✓
        关键点
        • 物理内存 15GB 能够容纳所有活跃数据(13.65GB)
        • Swap 里的 1.1GB 是内核主动换出的冷数据(性能优化,不是内存不足)
        • 这些冷数据几乎不会被访问,所以不会触发 Swap In,系统稳定
    • 异常状态下的内存分配(失衡)
      cat /dev/null > 执行瞬间,内核处理 50GB 文件截断,Page Cache 临时占用 8GB。物理内存不足(13.65GB + 8GB = 21.65GB > 15GB),内核 LRU 算法在需要腾出 8GB 时,把刚用过几分钟的 Java/ilogtail 数据也当”冷数据”换出——**这是”误判”**:正常只需换出 1.1GB 冷数据,这次却换出了包含 4GB 活跃对象的 7GB 数据,Swap 从 1.1GB 暴涨到 8GB。
      约 2 分钟后 Page Cache 处理完成,临时占用的 8GB 释放,但后遗症已经形成…
      • 为什么无法自动恢复
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        异常状态(02:32 之后):

        Swap 8GB 的内容:
        Java/ilogtail 热数据: 4GB ← 每秒被访问几十次
        原有冷数据: 1.1GB
        其他数据: 2.9GB

        问题:
        1. 热数据被频繁访问 → 触发 Swap In → 内存又满了
        2. 内存满了 → 再次 Swap Out → 把其他数据挤出去
        3. 被挤出的数据又被访问 → 再次 Swap In
        4. (无限循环)

        内核无法判断哪 4GB 是热数据:
        - 需要通过访问模式慢慢学习(LRU 算法)
        - 但学习过程本身就在触发 Swap 抖动
        - 因为物理内存只够容纳 13.65GB,4GB 热数据无论如何会有一部分在 Swap
        - 系统陷入"永久震荡"
        此时系统整体需求:仍然是 14.75GB(和正常状态一样)
        但问题是:数据分布完全乱了
        状态 内存使用 Swap 使用 Swap 内容 系统稳定性
        正常 13.65GB(活跃数据) 1.1GB(14%) 全是冷数据 ✅ 稳定,Swap 几乎不被访问
        异常 波动 8GB(100%) 4GB 热数据 + 4GB 冷数据 ❌ Swap 抖动,永久震荡
      • 触发链路
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        cat /dev/null > 50GB 文件 → Page Cache 临时占用 8GB

        物理内存不足(13.65GB 活跃数据 + 8GB 临时 = 21.65GB > 15GB)

        内核 LRU 算法将 7GB Java/ilogtail 活跃数据换出到 Swap(Swap: 1.1GB → 8GB)

        Page Cache 处理完成(2分钟后),临时 8GB 释放,但热数据已被困在 Swap 里

        Java/ilogtail 频繁访问 Swap 里的热数据 → si/so 同时暴涨 → Swap 抖动(Thrashing)

        Load 飙升 15+,Java GC 暂停从 ms 级延长到秒级

        OOM Killer 启动 → data-center 进程被杀
  4. 处理过程
    1. 初步诊断(02:30 ~ 03:00)
      • 步骤 1:查看系统负载
        1
        2
        uptime
        # 输出:15.85, 11.40, 9.09 ← 发现 Load 异常高
      • 步骤 2:查看内存和 Swap
        1
        2
        free -h
        # 发现 Swap 打满 8GB(100%),可用内存仅 600M
      • 步骤 3:检查 Swap 换入换出速度
        1
        2
        3
        vmstat 1 5
        # 关注 si(Swap In)和 so(Swap Out)列
        # 发现 si=1504 KB/s, so=4902 KB/s ← 同时发生,典型的 Swap 抖动
      • 步骤 4:查看进程内存占用
        1
        2
        top
        # 发现 ilogtail RES=5.8GB,但实际在内存中只有 2.5GB,其余在 Swap
      • 步骤 5:查看哪些进程占用 Swap
        1
        2
        3
        4
        5
        6
        7
        8
        # 统计有多少进程在用 Swap
        grep VmSwap /proc/*/status 2>/dev/null | grep -v "0 kB" | wc -l

        # 查看哪些进程占用最多
        for file in /proc/*/status; do
        awk '/VmSwap|Name/{printf $2" "$3}END{print ""}' $file 2>/dev/null
        done | grep -v " 0 kB" | sort -k2 -n -r | head -5
        # 发现 3 个 Java 进程共 7GB 数据在 Swap
    2. 尝试自然恢复(03:00 ~ 04:00)
      方案:等待系统自行将 Swap 数据换回内存
      结果:失败。Swap 使用率始终 100%,未见下降;- 内存使用率剧烈波动(60%100%),未能稳定;- Load 在 615 之间震荡,未能恢复;监控图显示 Swap 抖动持续,mem 可用率呈锯齿状波动
      原因:内存总量不足(缺口 2.8GB),活跃数据在 Swap 里反复被访问,触发持续的 Swap In/Out 循环。
    3. 尝试 swapoff(03:00 ~ 04:00,第一次)
      结果:swapoff: /dev/dm-1:swapoff 失败: 无法分配内存
      原因swapoff 需要将 Swap 里的 8GB 数据全部读回内存,但可用内存只有 1.8GB,物理上不够。
    4. 最终解决方案(04:48 ~ 04:50)
      • 步骤 1:停止 logagent 释放内存
        1
        2
        3
        sudo systemctl stop logagent
        sleep 5
        free -h
        为什么选择停 logagent 而不是 Java 服务
        1、logagent(ilogtail)是 C++ 进程,RES 5.8GB 是单进程中占用最大的,且是非核心服务,停止只是暂停日志采集,不影响业务;并且释放 5.8GB 已满足 swapoff 所需的可用内存条件,无需停业务服务
        2、停三个 Java 服务虽然也可行(可释放约 11GB),但会将原本未受影响的 ops-data-access、ops-data-model 也纳入中断,扩大故障范围
        效果:释放 5.8GB 内存,可用内存从 1.8GB 增加到 7.5GB+
      • 步骤 2:清空 Swap
        1
        sudo swapoff -a && sudo swapon -a
        效果:- Swap 从 8GB(100%)降到 0B;- 7GB 数据全部回到物理内存;- 系统负载从 15+ 降到 0.4
      • 步骤 3:重启 data-center 服务,ps -ef 确定进程启动,tail -f 确定日志写入中
      • 步骤 4:恢复 logagent
        1
        sudo systemctl start logagent
        最终状态(04:50)
        1
        2
        3
        Load:  0.40
        Mem: 6.3G used / 7.5G available (42%)
        Swap: 0B used / 8.0G total (0%)
    5. 后续观察与内存恢复预期(04:50 之后)
      05:09 监控数据:Mem 67%(10.05GB),Swap 0%,Mem 使用率持续上涨中
      这是正常的恢复过程:ilogtail、Java 堆、OS Cache 都在从刚重启的状态逐渐增长到稳态。
      系统内存配置本身接近极限,Java Old Gen 后续依然会积累冷数据被内核主动换出到 Swap。只要 Swap 使用率 < 20% 且 si/so 接近 0,就不影响性能。
      状态 Swap 使用率 Load vmstat si/so 是否正常
      健康依赖 5~15% < 2 si=0, so=0 ✅ 正常(冷数据在 Swap)
      危险边缘 80~99% 3~6 si>100 KB/s ⚠️ 警戒(热数据开始被换出)
      Swap 抖动 100% >10 si>1000, so>1000 🔴 故障(热数据混杂,无法恢复)
  5. 整改措施
    • 部署日志备份脚本到生产,配置 crontab:0 2 * * * sh /apps/devops/data-center/data-center-log-backup.sh
    • 配置 Logback 自动按天切割:修改 logback-spring.xml 中 rollingPolicy 为按天滚动 + 大小限制
    • 升级服务器内存(到 32GB)或迁移 ilogtail(到其他机器)

20260626 MOPS 线程池满导致接口pending

  1. 故障现象:validatePorts 接口响应持续 pending,最终超时报错;持续时间约 2 小时,有用户主动投诉,定级:P2
  2. 直接原因:
    checkPort 方法使用了 7+ 个模块共享的线程池 queryCmdbValidIpExecutor(核心线程数 15)。高峰期其他模块的长耗时任务占满了全部 15 个核心线程,checkPort 的任务只能排队等待,而上层 validatePorts 调用的是 Future.join()(无超时阻塞),导致 API 处理线程永久 pending,直到客户端超时。
  3. 根本原因
    线程池设计违反了隔离原则 → 不同业务域、不同 SLA 要求的任务共享同一线程池,高负载时相互干扰,快速任务被慢任务”饿死”。
    同时,服务三节点每个 JVM 节点线程池独立,要避免大量请求同时打到同一节点。
    1
    2
    3
    4
    5
    6
    API 请求
    └─ validatePorts()
    └─ checkPort(ip, port) ← 提交到共享线程池
    └─ CompletableFuture.join() ← 无超时阻塞
    └─ 线程池满,任务排队
    └─ API 线程永久 pending
  4. 自定义线程池解读
    1
    2
    3
    4
    5
    6
    7
    8
    @Bean
    public ThreadPoolExecutor queryCmdbValidIpExecutor() {
    return new ThreadPoolExecutor(15, // 核心线程:15,应该调高
    100, // 最大线程:100
    15L, TimeUnit.MINUTES, // keepAlive:非核心线程空闲
    new LinkedBlockingQueue<>(2000), // 有界队列,最多 2000 个任务排队,应该调低
    ThreadUtil.createThreadFactory("queryCmdbValidIpExecutor-"));
    }

20260630 MOPS Tomcat监控配置开启

  1. 问题描述:监控平台无法拉取 mops-platform-backend 服务的 Tomcat 线程池指标(活跃线程数、连接数、请求数等),导致线上问题缺乏可观测性。
  2. 根因:Spring Boot 内嵌 Tomcat 默认不向 JMX 注册 MBean,监控探针无数据可抓。
  3. 处置:在 application.yml 主配置中添加一行:
    1
    2
    3
    4
    server:
    tomcat:
    mbeanregistry:
    enabled: true
    1
    2
    3
    4
    5
    6
    7
    生效链路 →
    application.yml
    └─ ServerProperties 读取配置
    └─ TomcatServletWebServerFactory 创建 Tomcat 实例
    └─ 注册 MBean 到 JVM 的 MBeanServer
    └─ 监控平台 JMX Exporter 定期抓取
    └─ 监控大盘展示线程数、连接数、请求数等指标
  4. 涉及思想与知识点
    • 约定优于配置(Convention over Configuration)
      Spring Boot 的核心设计哲学。框架为每个配置项预设了合理默认值,开发者无需关心即可运行,只有需要改变默认行为时才显式配置。
      本案例体现:MBean 注册默认关闭,内嵌 Tomcat 默认引入(默认行为,开箱即用),mbeanregistry.enabled=true 覆盖默认值(显式覆盖默认行为)
    • 自动装配(Auto Configuration)
      ?? @EnableAutoConfiguration 扫描 classpath,检测到 tomcat-embed-core 存在,自动创建嵌入式 Tomcat Bean,无需任何 XML 配置。这是”约定”的底层实现机制。
    • 反射??

20260702 CMDB全文检索异常

  1. 现象: CMDB数据检索偶尔失败,queryUserResource 接口偶发 500,错误信息 “ES查询失败”
  2. 故障原因
    httpcomponents JAR 包版本冲突。elasticsearch-rest-high-level-client 7.17.23 需要 httpcore-nio 4.4.14+,但 Spring Boot 1.5.10 将其降级到 4.4.6。ES 服务端今天 10:22 主动断开连接时,客户端处理断连逻辑触发了缺失的构造方法,导致 I/O reactor 线程崩溃且无法自愈,此后所有 ES 请求全部失败。
    为什么重启就恢复、ES 断连后就连不上:正常情况下 RestHighLevelClient 有连接池和自动重连机制,ES 服务端断连后客户端会自动重建连接,不需要重启。但本次由于版本冲突,reactor 在处理断连的过程中自己崩溃了——不是断连本身导致连不上,而是处理断连时抛出 NoSuchMethodError 导致整个客户端实例废掉。
    reactor 的状态是单向不可逆的(RUNNING → STOPPED),没有自愈路径,只能通过重启服务重新创建 RestHighLevelClient 实例来恢复。修复版本冲突后,断连会走正常处理流程,不再触发崩溃。
  3. 临时处置:重启异常服务节点,恢复正常。
  4. 排查思路
    • 第一阶段:定位报错来源
      通过调用链上下文(ops-cmdb → Feign → qz-elasticsearch → es),明确真正的异常在 qz-elasticsearch 侧。
    • 第二阶段:查日志卡住
      INFO.0.log 里搜查询 es接口的错误日志 Error executing search request 没有结果,卡在这里
      关键转折:直接搜具体错误关键词容易因为日志时间窗口、级别分文件等原因漏掉。并不是调查询接口异常,而是都无法成功发起调用。改为搜 ERROR 级别所有日志,反而一次性暴露了两个现象——大量 I/O reactor status: STOPPED 和少量 callPaasError,并且从时间戳上看出 STOPPED 在前、callPaasError 在后,有明确的因果关系。
    • 第三阶段:溯源 reactor 崩溃原因
      通过 grep -n 定位 STOPPED 第一次出现的行号,再用 sed -n 取该行前后的日志,找到崩溃的直接异常 NoSuchMethodError,再往前一屏找到触发原因 ConnectionClosedException: Connection closed unexpectedly
    • 第四阶段:确认版本冲突
      NoSuchMethodError 是版本冲突的典型特征。结合 pom.xmlelasticsearch 7.17.23 + Spring Boot 1.5.10 的组合,直接锁定冲突的 JAR 包和版本差异。
    • 第五阶段:解释为什么今天才出现
      版本冲突代码路径只在处理连接断开时触发。服务长期运行连接稳定,炸弹一直未引爆。今天 ES 服务端主动断连是第一次触发,同时日志里找不到服务启动记录,说明服务已长期运行未重启,也印证了之前从未触发过的判断。
  5. 故障链路
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    10:22:00  ES 服务端主动断开连接

    客户端处理断连,调用 ConnectionClosedException() 无参构造

    NoSuchMethodError(httpcore-nio 版本冲突)

    I/O reactor 线程崩溃 → STOPPED,ES 客户端永久不可用

    10:22:08 所有 ES 请求抛 RuntimeException: I/O reactor status: STOPPED

    ESUtil.search() 吞掉异常,返回 null

    InnerSearchController:76 → searchResponse.getHits() → NullPointerException

    catch 到 NPE → return RetEntity.error(...) [code != 200]

    ops-cmdb IhrController → "ES查询失败" → 用户看到 500

0706 MOPS 备份任务查询慢 SQL 排查

  1. 背景:对备份任务定时采集每日执行情况,写入备份记录表,当前已累积约 35 万条记录。尚未到达分表的通用参考线 500 万行(根据InnoDB引擎特性计算,实际以查询性能和业务 SLA 为准),但已出现明显慢查询。
    根因在于查询条件的构造方式,具体体现在两类问题上:
    双侧 LIKE:备份主机 IP、备份集路径、备份类型等所有字段均使用 LIKE '%值%',无法利用 B-Tree 索引
    大量 IN:接口需要按用户权限过滤所属系统,将用户有权访问的所有系统名称拼入 IN 列表(可达数千条),在与 LIKE 并存时优化器会放弃索引改走全表扫
  2. 接口链路
    1
    2
    3
    4
    5
    POST /backup/getExecRecord
    └─ BackupService.getExecRecord(req)
    ├─ 权限系统填充 permitSystemNameEnList(用户有权限的系统列表)
    └─ 条件查询构造 buildFilterWrapper(所有字段统一使用 `likeIfNotNull`,生成 `LIKE '%值%'`)
    → SQL
  3. 现有索引状况
    1
    2
    3
    4
    KEY idx_system_name   (system_name)    -- 仅无 LIKE 时有效
    KEY backup_set (backup_set) -- %LIKE% 时失效,LIKE 右侧通配或 = 时有效
    -- ip_address:无索引
    -- backup_type:无索引
  4. 慢 SQL 根因分析
    场景矩阵 isHomePage? 有 LIKE? 有 IN? 是否慢? 原因 EXPLAIN
    用户端默认进入,无过滤 true 是(权限列表) idx_system_name 生效 type=ref key=idx_system_name rows=少量
    用户端 + LIKE + 权限 IN true 是(大量) LIKE 导致 IN 索引被放弃 id=1 t ALL possible_keys=idx_system_name key=NULL rows=1,868,591 Using where
    id=1 eq_ref distinct_key rows=1
    id=2 MATERIALIZED rows=2726
    id=3 DERIVED No tables used
    管理端,仅 LIKE,无 IN false 最慢 无任何索引入口,全表扫 type=ALL key=NULL rows=3,405,177 Extra=Using where
    管理端,LIKE + 少量 IN false 是(少量) 有风险 IN 列表很短时 idx_system_name 可能生效,但加了 %LIKE% 后优化器不稳定,数据量大时仍有慢查询风险 ~
  5. 核心规律:%LIKE%会使优化器放弃 B-Tree 索引。
    当同时存在大量 IN 条件时,优化器会对比”多次索引查找 + 回表”与”直接全表扫”的代价——IN 列表越长、LIKE 过滤越宽泛,越容易触发全表扫。管理端无 IN 兜底,是所有场景中最慢的:三个双侧 LIKE 无任何索引入口,全表扫约 35 万行。
    1
    2
    3
    4
    5
    6
    -- **关键 EXPLAIN 对比:**
    -- 有 LIKE 时,idx_system_name 被优化器放弃
    type=ALL key=NULL rows=350,000 Extra=Using where

    -- 无 LIKE 时,idx_system_name 正常生效
    type=ref key=idx_system_name rows=少量
  6. 优化方案
    • backup_type 改精确匹配,枚举下拉值前端只能选固定值,模糊匹配没有意义。
      1
      2
      3
      4
      // 改前
      .likeIfNotNull(RecordPO::getBackupType, condition.getBackupType())
      // 改后
      .eqIfNotNull(RecordPO::getBackupType, condition.getBackupType())
      1
      ALTER TABLE backup_exec_record ADD INDEX idx_backup_type (backup_type);
      效果:用户选了备份类型后,优化器先用 idx_backup_type 将候选集缩小到该类型的子集,后续 LIKE 在小集合上过滤,覆盖绝大多数查询场景。
    • ip_address 有两种典型输入:完整 IP(精确查某台主机)和 IP 段前缀(查某个网段),按格式自动选择匹配方式:
      1
      2
      3
      4
      5
      6
      7
      .and(StrUtil.isNotEmpty(condition.getIpAddress()), w -> {
      if (condition.getIpAddress().matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {
      w.eq(RecordPO::getIpAddress, condition.getIpAddress()); // 完整 IP,精确匹配
      } else {
      w.likeRight(RecordPO::getIpAddress, condition.getIpAddress()); // IP 段,加索引,前缀匹配
      }
      })
      1
      ALTER TABLE backup_exec_record ADD INDEX idx_ip_address (ip_address);
      效果:完整 IP 精确匹配结果降至个位数;IP 段前缀(如 10.25.)走索引范围扫描,避免全表扫。
    • backup_set 改前缀匹配:备份集是文件路径,天然从根目录开始,具有从左到右的层次结构,用户搜索路径前缀是最常见的场景,去掉左侧 % 即可利用索引。
      1
      2
      3
      4
      // 改前
      .likeIfNotNull(RecordPO::getBackupSet, condition.getBackupSet())
      // 改后,并新增索引 `KEY backup_set`
      .likeRightIfNotNull(RecordPO::getBackupSet, condition.getBackupSet())

0712 syn-task Full GC 故障排查报告

  1. 基本信息
    服务:采集代理服务(负责下发采集任务到 Kafka,并消费采集结果入库)
    影响P1,时长 约 80 分钟 → Full GC 失控,Kafka 消费停止,采集任务入库全部失败。
    止血:扩堆至 -Xmx4g 重启后恢复。观察到 Kafka 重新消费(日志出现 Successfully joined,服务加入Kafka消费者组)
    根因:采集入库方法加了全局锁,持锁线程在锁内对每条采集记录反复反序列化 MB 级配置数据,大量对象在持锁期间无法被 GC 回收,1GB 堆打满后 Full GC STW 反过来让入库更慢、持锁时间更长,其余等锁线程也各自持有对象无法释放,最终陷入死循环。
    • 故障时间线
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      T+0   定时任务每 5 分钟触发一次采集任务下发
      接口响应 HTTP 200,但内部抛出异常被 catch 吞掉
      任务状态停留在"执行中",未告警

      T+5 APM Thread 监控:
      裸线程(Thread-*)15 个,内存 193MB
      线程池(pool-*-thread-*)12 个,内存极低(线程池空闲,未被使用)
      → 说明入库走的是裸 new Thread(),线程池形同虚设

      T+17 APM Thread 详情快照(关键证据):
      Thread-BLOCKED,waiting on MessagePartitionServiceImpl 实例锁
      持锁线程调用栈:resolvePartitionMessage() → union() → analysisAutoData()
      → 锁竞争和持锁位置清晰定位

      T+18 Full GC 告警触发:
      O=99.97%,E=100%,S0/S1=0%
      FGC 每秒约 +2 次,单次 STW 约 600ms
      12 分钟内 STW 占比约 78%
      Full GC 后老年代占用无法下降 → 内存泄漏特征

      T+30 Kafka Coordinator Dead(第一次)
      Full GC STW 导致心跳线程无法发送心跳
      → 重连成功 → 4 分钟后再次 Dead
      → 反复 Dead/重连,持续消费新消息,新线程持续创建

      T+39 FGC 累计 +1351 次(12 分钟内)
      Old 区始终 99.97%,判断无法自动恢复,启动人工干预

      T+80 扩堆至 -Xmx4g 重启,Kafka 消费者重新加入消费组,服务恢复
  2. 根因分析
    • 根因 1:全局锁 + 锁内重 IO(直接根因)定位思路⬇️
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      jstat → Old 区 99.97%,Full GC 无法释放内存
      ↓ 什么对象占满了 Old 区?
      jstack → 大量线程 BLOCKED,全部卡在同一把锁上
      ↓ 持锁线程在干什么?为什么不退出?
      grep "locked <锁地址>" jstack.txt → 持锁线程调用栈:
      resolvePartitionMessage()(持锁)
      → union() → analysisAutoData() → batchOperateResult()
      → getCitFieldsInfoByAliasNameFromCache()
      → JsonConvert.json2Obj() ← 卡在 MB 级 JSON 反序列化

      heap dump → MAT Suspects 分析:
      Thread-52454 单线程持有 757MB(占堆 86.92%)
      内存全部来自 Jackson 反序列化字段配置表(citfieldsinfo)时创建的 HashMap 对象
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 锁住整个 Service 实例,所有线程全局串行
      public synchronized void resolvePartitionMessage(...) {
      while (iterator.hasNext()) {
      union(ip, thirdParams, instanceId, taskId);
      // union() → analysisAutoData()
      // → 对每条采集记录调用 getCitFieldsInfoByAliasNameFromCache()
      // → Redis 取出 MB 级 JSON → Jackson 反序列化
      // → 产生大量 HashMap 对象,持锁期间无法 GC
      }
      }
      citfieldsinfo 是什么:字段配置表,包含所有字段定义、脚本映射、主键配置等元数据,单个文档 JSON 序列化后数百 KB 至数 MB。analysisAutoData() 对每条采集记录循环调用一次,在锁持有期间产生大量短生命周期大对象。
      1
      2
      3
      4
      5
      6
      7
      8
      证据链:
      | 多线程并发调用此方法 | 代码:Kafka 消费层每条消息 `new Thread()` |
      | 线程堵在这把锁上 | APM Thread 详情:BLOCKED,waiting on 同一实例 |
      | 持锁线程调用栈 | jstack:resolvePartitionMessage() → union() → analysisAutoData() |
      | 堵塞线程数量 | jstack:18 个线程 waiting to lock 同一对象 |
      | 内存元凶 | MAT heap dump:持锁线程持有 757MB,全部是 citfieldsinfo 反序列化的 HashMap |
      | 累计创建线程数 | jstack 线程编号:服务启动至故障累计创建约 6.7 万个裸线程 |
      | 最终结果 | jstat:Old 区 99.97%,Full GC 每次回收量为零 |
      1
      2
      3
      4
      5
      6
      7
      Full GC STW(每次 600ms)恶性死循环
      → MongoDB/Redis 响应更慢
      → citfieldsinfo 反序列化耗时更长,持锁时间更久
      → 更多线程 BLOCKED,持有对象无法被 GC 回收
      → Old 区更满
      → Full GC 更频繁、STW 更长
      → (回到起点,无法自行终止)
    • 根因 2:JVM 堆配置严重不足
      1
      2
      3
      4
      故障时:-Xms512m -Xmx1g
      服务器:16GB 物理内存,可用约 10GB

      持锁线程峰值内存 757MB + 其余 18 个 BLOCKED 线程 + JVM 基础开销 > 1GB
      若堆为 4GB,同样的锁竞争下有充足缓冲,不会这么快失控。堆配置是加剧因素,不是根因,但大幅降低了容错空间。
    • 根因 3:Kafka 消费层裸 new Thread()(加剧因素)
      1
      2
      // 每条 Kafka 消息创建一个新线程,无上限
      new Thread(() -> operKafkaData(data)).start();
      单独存在时不是根因:线程执行完 operKafkaData() 就退出,对象可以被 GC 回收。
      叠加全局锁后成为加剧因素:线程因等锁 BLOCKED 无法退出,持有的对象持续积压,两者共同导致内存耗尽。
    • 为什么本次无法自动恢复
      根据 syn-task 服务日志 Kafka Coordinator Dead 后反复重连(而非彻底退出消费组),导致新消息持续涌入,新线程持续创建,内存压力持续增大。
      对比能自动恢复的场景:Coordinator Dead 后彻底退出消费组,没有新消息进来,存量线程自然执行完毕,内存得以释放,Old 区缓慢下降。
  3. 改进方案
    • P1:缩小锁粒度,将重 IO 移到锁外(!!TODO:核心理解点
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      // 现状:全局锁,锁内执行数十秒重 IO
      public synchronized void resolvePartitionMessage(...) {
      while (iterator.hasNext()) {
      union(...); // 包含 MB 级反序列化
      }
      }

      // 改法:按 instanceId 细粒度加锁,只保护轻量的 Redis 原子操作
      private final ConcurrentHashMap<String, Object> instanceLocks = new ConcurrentHashMap<>();

      public void resolvePartitionMessage(String instanceId, ...) {
      Object lock = instanceLocks.computeIfAbsent(instanceId, k -> new Object());
      synchronized (lock) {
      try {
      // 只保留"读+删 Redis 分片数据"这个需要原子性的操作(毫秒级)
      } finally {
      instanceLocks.remove(instanceId);
      }
      }
      // 入库和反序列化完全不需要锁,移到锁外并发执行
      citAutoDataOperationService.analysisAutoData(...);
      }
      锁持有时间从数十秒(含入库+反序列化)缩短到毫秒级(只含 Redis 操作)。
    • P2:Kafka 消费层改为有界线程池
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // 现状:无上限裸线程
      new Thread(() -> operKafkaData(data)).start();

      // 改法:有界线程池 + CallerRunsPolicy 形成自然背压
      private final ExecutorService kafkaWorkerPool = new ThreadPoolExecutor(
      10, 20, 60L, TimeUnit.SECONDS,
      new LinkedBlockingQueue<>(100),
      new ThreadPoolExecutor.CallerRunsPolicy() // 满载时阻塞 Kafka 消费线程,背压
      );
      kafkaWorkerPool.submit(() -> operKafkaData(data));
      CallerRunsPolicy 的作用:线程池满时由 Kafka 消费线程自己执行任务,消费速度自动降低,形成背压,避免无限堆积。
    • P3:Kafka offset 改为手动提交
      1
      2
      3
      4
      5
      # 现状:自动提交,重启会丢失已消费未入库的消息
      enable-auto-commit: true

      # 改法:手动提交,在 operKafkaData() 处理完成后显式提交
      enable-auto-commit: false
    • P4:JVM 启动参数固化
      1
      2
      3
      4
      5
      6
      7
      -Xms4g  -Xmx4g                          # 堆大小,建议取可用内存 40~50%
      -XX:+HeapDumpOnOutOfMemoryError # OOM 时自动 dump
      -XX:HeapDumpPath=/tmp/heapdump_oom.hprof
      -XX:+ExitOnOutOfMemoryError # OOM 后立即退出,触发自动重启
      -Xloggc:/path/to/gc.log # GC 日志持久化
      -XX:+PrintGCDateStamps
      -XX:+PrintGCDetails

0723 SpringBoot Actuator 敏感接口未授权访问漏洞处置

  1. 问题:10.247.129.131:7001/actuator/heapdump,/env接口暴露 风险: 中危
  2. 排查过程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1. 登录宿主机,发现 7001 端口由 Docker 容器监听
    sudo netstat -tlnp | grep 7001 # → docker-proxy
    sudo docker ps | grep 7001 # → mcloud-activiti,映射 7001->8080
    2. 验证漏洞,heapdump 可直接下载(93MB 堆文件)
    curl -I localhost:7001/actuator/heapdump # → 200
    sudo docker exec -it 5f6b2e982c53 bash
    curl localhost:8080/actuator
    # 返回列表包含:heapdump、env、beans、configprops、threaddump 等全部高危端点
    3. 根因:定位配置文件 `/apps/cmdb-private/application.yml` 中 `include: "*"` 全量暴露所有端点
    sudo docker inspect 5f6b2e982c53 | grep -A 5 '"Mounts"'
    # Source: /apps/cmdb-private/application.yml
    # Destination: /home/apps/application.yml
  3. 修复:将配置改为仅保留必要端点,vi /apps/cmdb-private/application.yml,重启容器:
    1
    2
    3
    4
    5
    management:
    endpoints:
    web:
    exposure:
    include: "health,info"
    1
    2
    3
    4
    5
    6
    sudo docker restart 5f6b2e982c53

    ## 验证
    /actuator/heapdump → 404 ✓
    /actuator/env → 404 ✓
    /actuator/health → 200 ✓
  4. 知识点
    端口找服务: netstat 看到 docker-proxy 说明是容器端口映射,docker ps | grep 端口 找到对应容器
    容器访问: 宿主机用宿主机端口(7001),容器内用内部端口(8080),两者不能混用
    Actuator: Spring Boot 内置运维模块,include: "*" 会暴露 heapdump(含密码/token)、env(含明文配置)等高危端点,生产环境应只保留 health/info
    配置挂载: docker inspect 看 Mounts,bind mount 类型直接改宿主机文件重启生效,无需重建镜像

0723 CMDB Zookeeper 集群拉起

现象: CMDB 集群整体停止后重新启动,ZooKeeper 3个 Pod 持续处于 CrashLoopBackOff 状态,集群无法自动恢复。
前因后果:

  1. ZooKeeper 集群成员配置持久化在 PVC 里,文件是 /data/conf/zoo.cfg.dynamic.<版本号>,里面记录的是各节点的 Pod IP
  2. Pod IP 每次重建都会变,但 PVC 数据不变,所以旧 IP 一直留在文件里(从启动脚本zookeeperStart.sh可以查到pod第一次启动从镜像内置的 /conf/zoo.cfg 复制到 PVC 的 /data/conf/zoo.cfg,后续每次重启直接读 PVC 里的)
  3. 启动脚本虽然会生成新文件 /data/zoo.cfg.dynamic(含新 IP),但 zoo.cfg 里的指针指向的是版本化文件,新文件根本不会被读到( zoo.cfg 里有:dynamicConfigFile=/data/conf/zoo.cfg.dynamic.400000016,这个指针是 ZooKeeper 自己通过 reconfig 机制写入并维护的,每次配置变更都会更新版本号,PVC 里存的永远是最新版本化文件的路径,启动时就直接读它。)
  4. ZooKeeper 启动时读到旧 IP,尝试 bind(旧IP:3888) → 该 IP 已不存在 → BindException → 进程退出 → CrashLoopBackOff
    手工修复(止血):在三台宿主机上找到 PVC 挂载路径,删除版本化 zoo.cfg.dynamic 文件(内含旧 IP),将 zoo.cfg 指针改为 /data/zoo.cfg.dynamic(启动脚本生成的新文件),等 Pod-0 自动重试启动 → 单节点选主 → 推送配置给 Pod-1/2 → 集群恢复
    永久修复(改进):趁集群健康执行一次 zkCli.sh reconfig,将三节点成员地址从 Pod IP 改为 Headless Service DNS 域名,reconfig 会把域名写入新版本化文件并同步到三个节点的 PVC,验证 delete 三个 Pod,IP 全部变化,集群自动恢复,RESTARTS=0,改进完成
    根本原因未彻底消除(待后续): 启动脚本 zookeeperStart.sh 在镜像里,生成配置时写的是 Pod IP,长期方案是修改镜像,让脚本直接写域名

8.1 Mops需求:DNS变更自动化

8.1:梳理变更场景,4种记录+2种平台,拆解为每种记录只有增删改三种操作,一步步实现即可
8.2-8.10:大假,昆明-大理-贵阳-深圳
8.11-8.15:收集+验证现状,还需拉会议评审
8.17:后续每天定 每日计划
0818:DNS变更,确定所有api/script(√),方案评审(×)
0819:DNS变更主流程(80%)
0820:DNS变更主流程(90%)
0821:管理端加失败工单入库按钮,当日上线
0822-0824:DNS变更开发
0825:DNS变更自测(√),CMDB安全组件安装流程确定(×)
0826:DNS变更sit(√),CMDB安全组件安装(×)
0827:DNS变更全流程报错机制完善,1.流程节点失败就报错 2.ticket.setExecutionLog(+=新日志) 那么可以全程操作溯源、、成功就直接加,失败throwE在catch里加e.getMessage()
0828:涉及定时任务调度/异步的要考虑UserContext空指针;上线
0908:一键回退主流程开发
0916:一键回退上线
1016:全面支持mx+txt申请变更回收,通过workspace让AI“模仿”cname自动化来实现
1030:支持智能解析,todo->cmdb字段回写+移动审批带出
11.18:产品化需求->Windows DNS自动化下发,适配进常规DNS下发流程(midea生产和产品化环境通过前端开放不同入口,实现执行不同部分代码,下发不同platform的DNS配置)
1202:变更一键回退优化(直接挂回原pool),支持申请回退(即回收掉)
后续 → 交接给外包
4.9:不是一股脑交给外包,而是把握、控制好需求、功能后交给外包实现
6.30:变更/回收前同步“元数据”,用于变更/回收脚本下发和一键回退,认为平台值不可信;至此DNS场景基本覆盖完善

  1. 当F5设备被配置为该域名的权威DNS服务器时,nslookup查询才会最终指向它。
    一个域名的权威DNS服务器信息(NS记录)是由其上一级域名注册商管理的(比如.com域名的注册商)。当你在注册商处将域名的NS记录指向你的F5设备(或其管理的DNS服务)后,一个完整的解析流程如下:
    本地输入 nslookup www.yourdomain.com =》本地DNS服务器会从根域名服务器开始,一路查询到.com服务器,最终获知你的域名的权威DNS服务器是你的F5设备 =》本地DNS服务器随后向你的F5设备发起查询,并获得F5上配置的IP地址(VIP)返回给用户。
  2. 如果不指定记录类型(直接 nslookup example.com),默认查询的是A记录。要查询特定类型,需使用 -qt=类型参数
  3. DNS与负载均衡(F5)的配合
    用户访问 app.example.com-> DNS解析返回F5的VIP(如 203.0.113.10) -> 用户请求到达F5 -> F5根据预设的负载均衡算法(如轮询、最小连接数等)将请求转发给后台的Web服务器池(如 10.1.1.101, 10.1.1.102)

10.20 MySQL 实战 45 讲 (https://time.geekbang.org/column/intro/100020801) 【doing…】

  1. 不是要背下所有东西,而是在构建体系后,能快速回忆起来
  2. 一条sql查询如何执行?客户端-> Server层: 连接器-(-查询缓存命中则直接返回)词法/语法分析-优化器(执行计划生成,索引选择)-执行器(操作引擎,返回结果)-> 存储引擎(存储数据,提供读写接口)
  3. 一条sql更新如何执行?把原数据行从硬盘读到内存(或者在缓存命中)-> 写新数据到新行 -> 引擎将新数据更新到内存并且把这个update操作记录到 redo log(InnoDB独有物理日志->“在某个数据页上做了什么修改”,保证事务的​​持久性 Durability,提供 ​​crash-safe​​ 能力)此时redolog处于 prepare 状态随时可以提交事务 -> 执行器生成这个操作的 binlog(Mysql Server层实现的逻辑日志->“给 ID=2 这一行的 c 字段加 1 ”,用于​​主从复制​​和​​数据恢复​,如时间点恢复)并且写入磁盘 -> 执行器调用引擎的提交事务接口,引擎把刚刚写入的redolog改成提交commit状态,更新完成(二阶段提交以保证两份日志保持逻辑一致)-> 两份日志都成功写入磁盘后,被修改的数据页(脏页)并不会立即写入磁盘,而是由 MySQL 在“后台”选择一个合适的时机异步刷盘。
    ​”redolog是如何配合binlog工作的?”https://yuanbao.tencent.com/bot/app/share/chat/vVRlFaO0o3sg
  4. https://time.geekbang.org/column/article/68963 事务隔离。事务就是要保证一组数据库操作,要么全部成功,要么全部失败,InnoDB在引擎层支持事务。
  5. 索引。MySQL中索引是在存储引擎层实现的。InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树的数据模型中的。每一个索引在 InnoDB 里面对应一棵 B+ 树,N叉树的N以整数字段为例差不多是 1200,树高等于4的情况下就能够存储很多数据,以减少单次查询的磁盘访问次数。根据建立索引的字段不同,索引类型分为(1)主键索引,叶子节点存的是整行数据(2)非主键索引,叶子节点内容是主键的值,还需要回表到主键的B+树二次查询。
  6. https://time.geekbang.org/column/article/69636 根据业务需求建索引,包括了覆盖索引、前缀索引、索引下推,,在满足语句需求的情况下, 尽量少地访问资源是数据库设计的重要原则之一

11.20 毕业生回炉

冬藏春生.团队对话-职业生涯教练.孙小红 刚刚好找出这张照片,刚好这一天的头发服帖,刚好朋友拍出了不错的构图,脸上也满是生气,好像有勇气冲到一个新的level 但最近我都在跑医院,想着每个周末只是在修复自己的身体和精神,到了工作日又要回去摧残自己..这有什么意义吗 为了回归到这张照片,想了半天得出一个结论是,不断地修复自己到一个刚刚好的状态就是生活的意义,吗 有点虚无,说了但像什么也没说,什么又是一个刚刚好的状态呢 也许是我刚好能够面对相机,刚好拍下了这张照片 也许人置身生活的洪流,只能不断修复自己 也许可以找到互相修复的人

11.30 CMDB 项目运营

深度参与策略

接手CMDB,,从做需求->做系统 https://yuanbao.tencent.com/bot/app/share/chat/920NVAbsqwtm
最大化CMDB项目的学习价值,, https://yuanbao.tencent.com/bot/app/share/chat/SzQrsGlsuz88

简历模块 平庸写法 高价值写法(量化+技术关键词)
项目经验 参与CMDB需求开发 主导CMDB数据模型重构,设计弹性CI架构,支持200+动态属性扩展,模型变更效率提升40%
技术亮点 使用Spring Cloud开发API 基于Quartz+Netty开发高并发自动发现引擎,单节点支持5000+设备/秒采集
业务价值 提升系统稳定性 通过拓扑影响分析模块,故障定位时效从30min缩短至5min,年止损运维成本200万+

💡 简历筛选口诀:“技术深度×业务影响”双突出——避免写“增删改查”,聚焦架构设计、性能优化、跨系统集成。

CMDB 交接分享

  • 整体理解
    CMDB(配置管理数据库)是IT基础设施与应用配置项的集中存储库,为运维、自动化、各业务系统提供数据支撑。
    数据组织:以配置项(模型就是配置项相当于表)的形式组织各类数据,支持通过模型管理灵活变更字段,通过关系拓扑进一步展开数据间的关系。
    数据使用:支持白屏化检索、维护数据,导入导出,支持查看历史版本,涉及”管理员”字段(管理员信息维护数据字典)自动发起变更流程;对外提供数据连接器(配置项增删改查)、消费视图能力,通过API读写数据;数据订阅服务通过监控oplog触发回调任务;数据审计(数据传入传出、消费视图审计,数据归档)
    数据更新:通过定时任务+采集脚本实现配置项信息的自动发现。
    数据治理:数据质量(自动校验异常数据),IP治理,EAM对账(定时任务拉取数据获取差异并展示)
    系统管理:支持配置用户角色以获得不同权限,对界面上的用户操作有审计记录。
  • 业务梳理
    CMDB 需要关注的是存了哪些配置项,对接的业务方是谁
    具体到每个配置项,关注数据如何写入、接入方是谁,活跃度怎么样
  • 系统架构与组件
    1. SpringCloud微服务架构,用户通过访问CMDB域名经过DNS、负载均衡解析进来K8S集群。Apache主要负责单点登录功能,配置文件中可以设置跳过认证的URL。
    2. Nginx作为反向代理,将请求转发到网关。网关通过注册中心获取服务IP,并根据负载均衡策略选择微服务节点。前端资源部署通过流水线将打包文件放到指定文件夹,静态资源无需进程管理。
    3. CMDB主服务(OPS_CMDB)处理大部分页面请求,代码结构复杂,接口查询逻辑需通过页面查找。MongoDB :主数据库,集群模式,一主一从一隐藏,读写分离。
    4. 数据连接器服务提供外部API,用户通过登录获取token后访问。查询和写入数据需经过参数验证和限流控制(默认1000条)。数据入库前会经过多层级校验(如选项值、关联字段等),采用责任链模式实现。高频接口涉及大量衍生查询,需谨慎修改。消费视图提供(多)配置项数据关联+指定字段查询能力。
    5. 全文检索服务对接开放搜索,查询时通过缓存获取用户可访问的索引列表,支持高亮和加权排序。mongodb数据通过monscache同步es,根据模型字段值检索。
    6. 数据订阅服务通过MongoDB的OP Log捕获数据变更,发送到Kafka后由订阅服务消费并触发后续逻辑(如离职资产交接)。数据质量服务通过配置告警任务,筛出异常数据
    7. 定时任务服务通过注解开发新任务,采集代理服务通过Kafka下发脚本并分批处理返回数据。自动采集:xxljob->ops-syn-task->kafka->ansible->执行消息回调->kafka->syn-task入库
    8. 用户中心服务处理登录初始化,表头配置服务管理页面表格显示字段,均为低改动频率服务。
    9. 组件包版本管理:测试环境用snapshot可随意修改,生产环境需更新release版本号并重新打包部署。
    10. 远程Debug需在启动项添加参数,连接UAT环境IP和端口,适用于本地无法模拟的场景(如采集任务)。

架构 & 流量

  1. ⬇️ 内部 主机部署
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    用户浏览器

    │ https://cmdb.midea.com

    F5 VIP(负载均衡硬件)
    │ 轮询/会话保持
    ├──→ Apache 节点1 (10.18.230.157:80)
    └──→ Apache 节点2 (10.18.230.63?:80) ← 两个 BalancerMember

    │ Apache 做了什么:
    │ 1. CAS/MAS 单点登录校验(signin.midea.com)
    │ 2. 写入 MAS_REMOTE_USER Header(用户身份)
    │ 3. 白名单 URL 跳过登录(/open-api /xxl-job-admin /direct 等)
    │ 4. 反向代理转发到后端 nginx(balancer://server → :7100)


    Nginx (:7100) 节点(10.18.230.63 / 10.18.230.156)

    │ Nginx 做了什么:
    │ 1. 静态资源直接返回(前端 dist 目录)
    │ 2. 按 URL 前缀路由到不同后端

    ├── / → 前端静态文件 /apps/cmdb/ui/master/dist
    ├── /xxl-job-admin → xxl 集群(:8085)直连
    ├── /mediaKit /ciview /deliveryview 等 → 直连具体服务 IP:PORT

    └── 大部分业务请求 → upstream zuulserver(:7004 两节点)

    │ Zuul 网关做了什么:
    │ 按服务名路由到具体微服务(Eureka 注册发现)

    ├── /zuul/ops-cmdb → ops-cmdb 服务
    ├── /zuul/ops-data-model → ops-data-model 服务
    ├── /ops-data-access → ops-data-access 服务
    ├── /qz-elasticsearch → qz-elasticsearch 服务
    ├── /qz-usercenter → qz-usercenter 服务
    ├── /ops-synapplication → ops-synapplication 服务
    └── ...其他微服务
  2. ⬇️ 私有化 k8s容器部署
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    外部请求


    K8s Ingress (cmdb-0a3bc0822a... 类型)
    │ 所有域名 / 路径

    cmdb-nginx Service (:20100)


    cmdb-nginx Pod
    │ 静态资源直接返回
    │ 业务请求 → upstream zuulserver

    cmdb-gateway Service (:7004) ← Zuul 网关
    │ Eureka 服务发现
    ├──→ cmdb-main (:7009) ops-cmdb
    ├──→ cmdb-data-access (:7119)
    ├──→ cmdb-elasticsearch (:7007)
    ├──→ cmdb-synapplication (:7008)
    └──→ 其他微服务...

MongoDB

why mongo?文档型数据库,灵活加减字段(新的记录生效,旧的不影响?)
一主一从一hidden
查数据慢,大概率是mongodb的负载过高,或慢查询(索引没有或者失效)。
负载居高不下,该怎么处理?kill超过**秒的慢查询。 重启CMDB应用,杀掉堆积的大量慢查询请求。。
MongoDB慢sql: CMDB MongoDB 从节点 10.18.52.147 异常崩溃,当时已重启恢复,副本集状态正常;根因是高并发 COLLSCAN 查询叠加导致资源异常,最大问题查询 ip_manager_instance_data 全表扫描 21.3 万文档,耗时 5 秒,读取 12.9MB
测试环境已针对慢查询表和查询条件添加索引,验证已生效,下班后在生产也添加,后续出现类似告警我都跟进下

数据连接器

核心功能,,读写,控制并发,流量多少??
校验 责任链,幂等,,
查询/视图接口慢,,服务负载高,内存高,tomcat,线程池满了,,数据库慢加
[图片]

全文搜索

  1. 整体数据流
    1
    2
    3
    4
    5
    6
    7
    8
    9
    MongoDB(业务数据)
    ↓ Change Stream 实时监听
    Monstache(同步中间件)
    ↓ 写入
    Elasticsearch(搜索引擎)
    ↓ 查询
    qz-elasticsearch(Java服务)

    前端用户
  2. MongoDB:副本集部署,所有写操作记录到 oplog(固定大小环形缓冲区)。Monstache 基于 oplog 实现的 Change Stream 监听数据变更,是实时同步的基础。每个 CIT 类型对应一张表,命名规则 {cit}_instance_data。在 monscache 库维护两个集合:
    • 断点monstache 集合):记录当前同步到的 oplog 位置,重启后续传
    • 全量标记directreads 集合):记录哪些表已完成全量同步,避免重复扫描
  3. Monstache:监听 MongoDB Change Stream,将数据变更实时同步到 ES。内部维护 config.toml ⬇️
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    mongo-url = "mongodb://..."  # MongoDB 连接
    elasticsearch-urls = ["http://..."] # ES 连接

    resume = true # 断点续传
    direct-read-namespaces = ['cmdb.host_instance_data', ...] # 全量同步(启动时执行一次,补全历史数据)
    change-stream-namespaces = ['cmdb.host_instance_data' ...] # 增量同步(持续监听,实时同步变更)

    # 表到索引的映射
    [[mapping]]
    namespace = "cmdb.host_instance_data"
    index = "cmdb_host"
  4. Elasticsearch:索引命名规则 cmdb_{cit别名},对应 CMDB 的一个配置表,新增 CIT 类型需先手动建索引再由 Monstache 同步。
  5. qz-elasticsearch:Java 服务,封装 ES 查询逻辑。查询使用 query_string 全字段匹配,等价 DSL:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "query": {
    "bool": {
    "filter": [{
    "query_string": {
    "query": "关键词",
    "fields": ["*"],
    "analyze_wildcard": true,
    "default_operator": "OR"
    }
    }]
    }
    }
    }
  6. mongo→es 同步流程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Monstache 启动

    读取断点(MongoDB monstache库 → monstache集合)

    检查全量完成标记(MongoDB monstache库 → directreads集合)
    ├── 标记存在 → Skipping direct reads,跳过全量
    └── 标记不存在 → 执行 direct-read 全量扫描同步,完成后写入标记

    建立 Change Stream 连接,从断点位置开始监听增量,延迟通常 < 1s

    持续运行,实时同步 MongoDB 变更到 ES
    如果需要手工全量同步,需要先在ES配置索引和字段,然后进 MongoDB 删除全量完成标记 db.directreads.deleteMany({}),重启 Monstache 即可
  7. 检索的应用:CMDB全局IP查询 https://doc.midea.com/teamKnowledge/detail/docOnline/2067500783818891266?id=2067500783818891266&mc_widget_identifier=com.midea.kmp.teamknowledge

数据订阅

  1. 核心架构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    MongoDB 集群(副本集)

    │ ChangeStream 增量捕获

    ┌──────────┐
    │MongoShake│ Go进程,独立部署,直连Mongo hidden节点读取
    │ │ 按 collection 哈希路由到不同 partition
    │ │ 记录同步位点,重启不丢数据
    └────┬─────┘
    │ JSON 格式消息

    Kafka topic: mongo_sync(8个 partition)

    │ 消费组: ops-subscribe

    ┌──────────────────────────────┐
    │ 数据订阅服务 │
    │ 节点1: 4个消费线程 │
    │ 节点2: 4个消费线程 │
    │ │
    │ ① 幂等校验(Redis partition+offset)│
    │ ② 解析变更(INSERT/UPDATE/DELETE) │
    │ ③ 匹配订阅规则(Redis缓存60min) │
    │ ④ 线程池异步 HTTP 回调 │
    └─────────────────────────────────────┘
  2. 核心代码实现
    幂等处理:key 为 topicname-partition-offset,写入 Redis,TTL 2分钟; 同一条消息重复投递时直接跳过,防止重启后重复回调(??重复消费kafka)
    变更解析:- INSERT:直接从消息体解析完整字段 - UPDATE:从消息体取文档 _id,回查 MongoDB 获取最新完整数据(ChangeStream 只携带变更字段) - DELETE:从消息体取文档主键
    订阅规则匹配:- 订阅配置存储在 MongoDB 的 message_subscriber 集合中,- 匹配维度:collection 名、操作类型(INSERT/UPDATE/DELETE)、字段级过滤 - 配置带 Redis 缓存(TTL 60分钟),减少 MongoDB 查询压力
    异步回调:- 匹配到订阅规则后,将回调任务投入线程池异步执行 - 支持配置失败重试次数,连接超时 1s,读超时 5s - 线程池满时触发 AbortPolicy,任务丢弃并打印告警日志
  3. 故障复盘
    故障现象:UAT 环境数据订阅完全不可用,持续约 3 周未被发现。
    排查过程
    1 查消费组状态 → Kafka 连接超时
    2 测试 broker 端口连通性 → 三台 broker 全部 Connection refused
    3 登录 broker 机器查进程 → Kafka 进程不存在
    4 查 Kafka 日志 → IOException: 设备上没有空间
    5 查磁盘 → /apps 分区 100% 满,api_call_audit topic 数据占用约 30G
    6 分析文件时间 → 某日写入量突增 9 倍,单日将磁盘写满,Kafka 崩溃
    根因api_call_audit topic 写入量突增,叠加 retention 只配置了时间(7天)未限制大小,磁盘被写满,三台 broker 先后崩溃,整个 Kafka 集群不可用。
    处理方式:所有数据文件均已超过 7 天 retention 期,直接删除后重启 Kafka,集群恢复,订阅功能随之恢复。

自动采集(*)

防火墙采集 &fullgc

入库 kafka消费,new Thread,,并发控制
0712 fullgc 看优化 1、锁粒度控制 2、有界线程池 3、citfieldsinfo 用redis缓存

12.11 2025年度总结&成长对话

  • 个人贡献
    1. InfluxDB服务化建设(今年上半年)
      • 支持InfluxDB实例备份与恢复能力
      • 完成InfluxDB父子实例模式改造,新增InfluxDBMeta规格体系,实现节点级独立变配、重启能力
      • 针对Data节点迁移提出 “热分片截断-冷副本恢复” 双阶段法,解决开源工具增量数据丢失问题
    2. MOPS功能开发(下半年主要负责)
      • NBU备份自动化:支持基于NBU的文件、程序、归档与RMAN类型备份申请自动化下发,开发备份域配置管理界面
      • 负载均衡自动化:支持基于F5的负载均衡变更、回收自动化,完善提单校验、实时检测能力,流程中自动带出配置项
      • 域名解析自动化:全面支持基于F5的A, CNAME, MX, TXT类型的申请、变更、回收自动化与一键回退能力,支持“管理员配置+用户执行”两阶段下发(把从前管理员手中的下发权限交到用户手上)
      • Windows Dns:支持基于Windows Server的A, CNAME类型记录的申请、变更、回收自动化,在客户环境验证
      • 运营数据:任务数 、自动化下发数 、自动化率
    3. CMDB开发运维(最近交接)
      • 熟悉CMDB功能与运维场景,日常支持业务方数据运营
      • 治理CMDB表记录,同步到业务系统中(优化用户体验)
      • 调研CMDB智能助手,支持自然语言交互(初步设想实现基础的资源查询功能,后续支持数据统计、分析、甚至图表生成,灵活使用CMDB中的数据)
  • 团队赋能
    • MOPS需求, InfluxDB服务化设计文档输出(以及一篇数据库运维的文档)
    • 人肉提醒关注开发人效分和AI代码生成数(有人说可以写个脚本来做,我想了下也是可以实现的)
  • 服务客户
    • MOPS响应用户群问题,实现业务方需求和优化点,产品化客户答疑
    • CMDB值班运维,支持业务方运营
  • 制定2026年度重点工作计划。
    1、MOPS功能优化
    负载均衡自动化:支持一键回退能力,完善流程中配置项自动带出的能力。
    域名解析自动化:完善提交工单时的自动校验能力,避免因平台记录与实际配置不一致导致变更异常。
    2、CMDB项目运营
    负责日常开发运维,支持业务方数据运营,治理历史数据。设计开发CMDB智能助手,利用AI工具赋能,初步设想是支持自然语言交互和提供简单的数据查询能力。
  • 希望提升的1-3项核心能力项,计划如何提升。
    1、对系统有更加深入的理解。通过CMDB项目的运营,了解各微服务和组件是如何协同工作的,对接业务方需求时保证不影响服务的稳定性,面对告警可以快速找到解决思路。
    2、能够独立负责项目的开发。主动承担更加复杂的任务,以及开发需求的同时增加对业务方面的思考。
    3、学习AI领域的知识,通过CMDB智能助手项目深入学习通过AI赋能传统应用。
  • 面向未来1年的职业规划。
    【更高岗位或职级】:经过一年的多项目需求开发锻炼,交付任务数已经达到了团队平均水平,需要主动承担起更加复杂的项目,有独立思考和设计方案的能力。深度参与服务运维和产品化工作,日常开发中也要思考可优化的点。开发需求的同时,应该增加对业务的思考,对一个系统有更加深入的理解。
  • 上级反馈
    做好的:
    1、完成了influxdb的服务化基础能力开发
    2、完成了负载均衡、dns的自动化能力开发
    待改进:
    1、主动解决问题的能力需要加强
    2、需要加强基础能力的学习
    明确员工亟待提升的核心能力,并为其制定成长计划。=》独立自主解决问题的能力,希望能成长成为能够主导一个方向的负责人
  • expectation2me
    1、提高演讲汇报能力(向上管理实际上同步进度给boss很焦虑)__周会主题AI分享,保持好奇心
    2、突破自己=>没有舒适区/合适的时间=>参与mariadb值班=>面对挑战性任务=>积累信心
    3、CMDB稳定性运营&Agent建设(ps.运营交给外包)
    2025:交付(项目/任务数↑),身体/精神修复(hospital&solotrip)
    2026:游泳足球(康复&强化),见朋友(少玩多沉淀),年底tc(专精一个领域?!bitfree.cn论坛学习)

12.14 CMDB-Agent调研

1. 理论知识

  • 一个典型的Agent工作流程如下:
    用户输入自然语言请求。
    Agent将用户输入、对话历史、可用的工具描述(通过Function Calling定义)以及系统提示词组合成一个Prompt,发送给LLM。
    LLM根据Prompt判断是否需要调用工具。如果需要,LLM会返回一个函数调用请求(包括函数名和参数)。
    Agent解析LLM返回的函数调用请求,并执行对应的工具(Tool)。
    工具执行后返回结果,Agent将工具执行结果作为上下文,再次调用LLM,生成最终的自然语言响应。
    Agent将响应返回给用户。
    在这个过程中,RAG可以在第一步之前或之中介入,通过检索外部知识库来增强Prompt的上下文。
  • Spring AI 核心概念
    1. Spring AI 是Spring生态系统中的一个项目,旨在简化AI功能的集成。它提供了统一的API来调用各种AI模型。
    2. Spring AI 集成了以下功能来支持上述流程:
      ChatClient:统一接口,用于与各种LLM交互。
      Prompt Templates:支持参数化提示词模板,方便构建动态Prompt。
      Function Calling:支持定义工具(通过@Description注解等),并自动处理函数调用流程。
      RAG:提供了检索器(Retriever)和向量存储(VectorStore)的支持,可以方便地实现检索增强。
      Agents:提供了Agent的抽象和实现,比如通过Chain(链)来组合多个步骤,包括工具调用。
    3. 官方资源:
      https://spring.io/projects/spring-ai - 必读!
      https://docs.spring.io/spring-ai/reference/index.html
      视频教程(推荐):
      https://www.youtube.com/watch?v=示例 - 搜索”Spring AI Getting Started”
    4. 关键概念速成:
      // Spring AI 核心组件关系
      ChatClient ←→ PromptTemplate ←→ ChatResponse

      Function Calling ←→ Tools ←→ Your CMDB Service
  • CMDB中的具体应用场景
    1. 自然语言查询:”帮我找内存大于8G的Linux服务器”
      1
      2
      3
      // 1. LLM解析 → 需要调用queryHosts工具,参数: osType="Linux", minMemory=8
      // 2. Tool执行 → MongoDB查询: db.hosts.find({os: "Linux", memory: {$gte: 8}})
      // 3. LLM整合 → "找到3台符合条件的Linux服务器..."
    2. 统计分析:”统计不同操作系统的服务器数量”
      1
      2
      3
      // 1. LLM解析 → 需要调用queryHosts工具,参数: osType="Linux", minMemory=8
      // 2. Tool执行 → MongoDB查询: db.hosts.find({os: "Linux", memory: {$gte: 8}})
      // 3. LLM整合 → "找到3台符合条件的Linux服务器..."
    3. 智能建议:”我们的数据库性能有点慢”
      1
      2
      3
      // 1. RAG检索 → 相关性能优化文档
      // 2. LLM分析 → 结合CMDB数据给出具体建议
      // 3. 返回响应 → "建议检查数据库A和B的索引配置..."

2. 竞品分析(理解产品形态)

可体验的竞品:

  1. 维诣科技CMDB助手 - https://www.vertice.cn/solution-details/26737.html 重点分析其交互模式
  2. ServiceNow Now Assist - https://www.servicenow.com/products/now-assist.html
  3. Azure Copilot for IT - https://azure.microsoft.com/en-us/products/copilot-for-it

竞品核心功能提取:

• 自然语言转查询:”内存大于8G的服务器” → MongoDB查询
• 结果格式化:表格、图表、自然语言总结
• 错误处理:查询无结果时的智能建议

3. demo搭建

(Python demo using LangGraph) https://github.com/leo710aka/CMDB-Chat/blob/main/cmdb_chat.py

4. 二开

nl2sql开源项目:https://java2ai.com/agents/dataagent/quick-start/


2026🐴🐎

1.16 OPSpace大洋部署

1.16-1.22:MOPS大洋产品化功能验证。。差异本地起多服务验证,在sit环境验证poc分支代码,要注意配置差异
1.23:容器化服务验证 → image上传-bundle打包load到私有仓-pod重启,分步验证,相信自己能解决问题。
1.26:不管在什么环境,排查问题看日志(项目配置里查,不懂就问,超过1h解决不了就抛出)
2.10:大洋uat演示
2.26:私有化代码迭代规范搞清楚(主干分支,迭代分支,输出分支)
3.20:关键是理解用什么分支,出什么包(什么仓库什么标签的image),部署到什么环境

5.11:NBU整机备份验收
5.15:?windowsDNS改造WinRM下发(解决ssh登录执行ps指令二跳认证问题)

2.12 Java Advanced: 服务 / 部署

1. 项目打包与构建

  • Maven 是Java领域最主流、应用最广的构建工具,依赖管理清晰。Gradle则更为灵活,在大型项目或Android开发中常见。
  • 关键配置文件:Maven的核心是项目根目录下的 pom.xml 文件。它定义了项目坐标、依赖的第三方库(如Spring Boot)、插件等。一个基本的打包命令是 mvn clean package,执行后会在 target/ 目录生成可运行的JAR包。
  • 生成物辨识:打包后会生成两种主要JAR文件。Fat JAR(或Uber JAR)将应用本身、所有依赖库及启动脚本打包成一个文件,通常可以通过 java -jar 直接运行,是Spring Boot的默认方式。Thin JAR 则只包含您自己的代码,运行时需要显式指定依赖库的路径(Classpath)。
    https://leo710aka.github.io/2026/04/11/Maven/

2. 应用部署与进程管理

  • 核心运行命令:使用 java -jar your-application.jar 即可启动应用。您可以通过 --server.port=8081 这样的参数覆盖配置文件中的设置。
  • 进程管理命令:程序启动后,便是一个操作系统进程。
    • 查看进程:使用 ps -ef | grep javajps -l 命令,可以查看当前系统中所有Java进程的PID(进程ID)和主类名。
    • 终止进程:使用 kill -9 <PID> 可以强制终止一个Java进程。更优雅的方式是在应用中通过Actuator端点实现kill -15 <PID>(发送SIGTERM信号),允许程序完成当前任务后再退出。
  • 保持进程稳定:在WSL中,即使关闭终端,也希望应用能继续运行。可以使用 nohup java -jar your-app.jar > /path/to/app.log 2>&1 & 命令实现后台运行并将日志输出到文件。
  • 线程管理:不要为每个任务都创建新线程,应使用线程池(ThreadPoolExecutor)来管理和复用线程,减少资源消耗。在Spring Boot中,利用 @Async 注解可以轻松实现异步方法调用。使用 top -H -p <PID> 命令可以查看某个Java进程内各个线程的CPU和内存占用情况,帮助识别资源消耗大户。

3. 线上问题排查与Arthas利器

  • 日志是首要依据:确保您的应用配置了详细且结构化的日志(如使用Logback/SLF4J),并输出到文件。出现问题首先查看日志文件。
  • Arthas——Java诊断神器:Arthas是Alibaba开源的Java诊断工具,无需重启应用即可动态跟踪问题,非常适合线上调试。https://arthas.aliyun.com/doc/quick-start.html
    • 安装与启动:在WSL中,可以通过 curl -O https://arthas.aliyun.com/arthas-boot.jar 下载,然后用 java -jar arthas-boot.jar 启动并选择要诊断的Java进程。
    • 常用命令
      • dashboard:实时仪表盘,总览系统状态。
      • thread:查看所有线程堆栈,找出阻塞或CPU占用高的线程。
      • watch com.example.YourClass yourMethod "{params, returnObj}" -x 3:动态观察方法调用的入参和返回
      • trace com.example.YourClass yourMethod:追踪方法内部调用路径及每个环节的耗时。

4. WSL完整实验方案

  1. 环境准备:确保WSL内已安装JDK 11/17 和 Maven。可通过 java -versionmvn -v 验证。
  2. 创建与打包:使用 https://start.spring.io/ 生成一个简单的Web项目(选择 Spring Web 依赖)。在项目根目录执行 mvn clean package,观察 target/ 目录下生成的 .jar 文件。
  3. 部署与进程管理
    • 运行 java -jar target/your-demo-app.jar
    • 另开一个终端,用 jps -l 找到该进程的PID,用 ps -ef | grep java 查看详细信息。
    • 访问 http://localhost:8080 验证应用。
    • 使用 kill -15 <PID> 优雅停止应用。
  4. 模拟问题与Arthas诊断
    • 在代码中故意写一个缓慢的方法或死循环。
    • 启动应用,然后启动Arthas,使用 thread 命令找到繁忙线程,再用 stack 命令查看该线程的完整堆栈,定位问题代码。

5. Java高并发实验

^^

// todo:本地?远程linux?实验

2.26 开工

确定自己的项目边界,,杂事给外包??运营工作形成sop
确定主线、支线任务,,每天能回想起自己做了什么吗。。
找到专精??晋升,数据库,,
ai coding

3.2 CMDB-Copilot

1.5:CMDB问数项目启动
1.7:datamax问数体验,开启多轮问答方案调研(确定表-确定字段-确定sql..)
1.9:DataAgent项目体验,SpringAiAlibaba,Apache2.0
1.11:DataAgent配置llm+embedding,初始化数据源embedding到本地vectorStore,,Graph多轮问数
1.12:DataAgent验证业务提供的场景
1.15:多轮验证报告=》可能提高准确性,但需要维护多个节点
2.26-3.6:基于DataAgent二开 → 看详细功能实现,多轮逻辑图,节点路由关系,每个节点做了什么,,
3.7-:定制化改造,结合内部场景验证,完善缺陷。。
laster:CMDB交接,,问数项目sit验证,“后面能不能发都不知道了”

3.6 初级开发的困境 & 破局

  • 当众质疑工作不饱和?本质是没有产出,就没有话语权。不用硬刚,也不用忍气吞声,用结果 + 进度说话。1. 一段声明:不说 “我在看代码”,说 “梳理结构、定位缺陷、理清逻辑”,给出接下来的产出承诺。2. 从今天开始,强制自己 “每天有产出”。3. 脸上表情怎么做:不笑,不怒,不辩解,轻轻点头,眼神平静看他一眼。4. 职场霸凌。当众查员工聊天记录、当众质疑工作量、当众查代码提交量、公开羞辱式管理 —— 在正规大厂,绝大多数情况都属于违规、越权、甚至侵犯隐私,完全可以向 HR / 合规 / 内控举报。5. 当众 / 当面逼你自证工作量的场景:不要当场表演式自证。不卑不亢,拉到私下正式对齐。提前留好工作记录,你就永远站在有理这边。每天 / 每周简单记:故障处理,上线 / 变更,巡检、优化,值班、应急,学习 & 文档
  • 信任破产,boss要的是安全感,你要给的是确定性,,承诺后却没有产出,其他任务是否有记录留痕,无法自证工作是否饱和,很危险。。
  • 代码看得慢:死磕是笨办法 → 做出一个小而确定的成果 → 信心回来 → 状态回来 → 路慢慢打开。形成自己的代码阅读方法论(看架构→看核心流程→看数据流向→看异常处理),上手debug。把 “大难题” 切成 “今天能做完的小事”,把目标由 把缺陷搞定/把代码看懂 → 今天打日志看清楚数据怎么走/明天定位到具体函数改一行代码验证,人不是靠决心撑住的,是靠 “今天又做成了一点” 撑住的。
  • 初级工程师真正的破局路,靠可见成果 + 稳定节奏,不靠拼命。困境只是阶段,不是结局。你现在在爬坡,爬过去,就是新高度。面对组长不慌,面对代码有方法,面对自己能自洽,长期用成果说话。
  • 焦虑,胸闷,腰突发作疼醒。深呼吸4s,含住气4s,长吐气6s
  • 调休请假&同步:周日20:00编辑 → x 哥,跟您同步一下:我明天上午想请半天调休,最近腰疼反复,需要休息调整一下。之前几次请假没详细说明,这次跟您说下情况。另外 AI 项目我这边已经在做代码调优了,我会保证每天都有新进展,尽量不影响进度。

3.19 我为什么怕 & 我怎么自救

  • 先说结论:你现在的痛苦、低效、说不清楚自己在干嘛、怕被质疑、觉得自己该拿低绩效,几乎全是「正常新人在复杂项目里的标准状态」,不是你能力不行,也不是你活该垫底。
  • 先讲你现在的真实处境:完全合理,不是你菜。你结束了学生生涯,经历了第一年的啥也不会期到代码积累期,现在遇到的所有问题,都是工业级真实项目的典型痛点:
    1. AI + 大厂开源项目,本身就比普通业务代码难上手,出现普通Java 系统平时不用的工具、流、懒加载、模型相关链路
    2. 时间被切碎, 主业 + 杂活 + 临时帮忙,根本没法大块专注
    3. 小问题巨耗时间
      看业务逻辑、复现问题、搭环境、读文档、适配公司内部工具(Docker 被禁 → Portman)
      这些事对外说不出口,但对内巨耗时
    4. AI 类工作本身就难出“可见成果”
      调效果、验证、小修小改,两三天可能只推进一个细节
      没有“上线一个功能”“修复一个bug”那么直观
  • 为什么你一被问“时间花哪了”就哑口无言?
    1. 因为你在用 学生思维 衡量工作:我在认真学、认真啃、认真解决问题 → 这就是努力 / 只要我在做事,就应该被看见
    2. 但职场/大厂的逻辑是:你做了什么 ≠ 你产出了什么,你花了时间 ≠ 你有进展**
    3. 组长要的不是“你很辛苦”,而是:今天解决了什么问题,明天能解决什么, 风险在哪,要不要帮忙, 这个事什么时候能结束
      你现在的困境是:大量时间花在“隐性成本”上,却没有转化成“显性成果”。 自己也觉得这些事太小、太基础,不好意思说。
  • 你说:“客观来讲,我确实最该拿低绩效”
    1. 一半对,一半错
      对的一半:产出不清晰- 进度不可控- 风险不提前暴露- 汇报不主动。从管理视角看,确实是低绩效候选人
      错的一半:这不代表你能力差、态度差, 这是工作方法、汇报方式、项目拆解能力没到位
      低绩效,往往不是“做得最差”,而是“看起来最差、最不让人省心”。
    2. 为什么新来的应届生能拿高绩效?老人反而不行?高绩效新人,通常赢在这几点:
      非常会“包装工作”。- 很小的事,也能说出业务价值、用户价值、技术难点- 把“搭环境、看代码”包装成: “熟悉整体架构,梳理链路,为后续迭代打下基础” - 把“调个小参数”说成: “优化模型效果,提升召回/精度/用户体验”
      进度 visible(看得见)。 每天/每两天有小节点, 主动同步:遇到什么问题、怎么解决、下一步干嘛,领导不用追着问
      敢求助,不硬扛。你是:不懂→默默查→花很久→搞完也不说。高绩效新人是:不懂→马上问→快速搞定→显得效率极高
      抓“领导关心的点”:稳不稳定- 有没有线上问题- 能不能按时上线- 有没有风险。不关心:- 你读了多少文档- 你配环境配了多久- 你内心多崩溃
    3. 高绩效,很多时候不是“最厉害”,而是“最省心、最显眼”。
  • 你真正的问题:不是笨,是对“不确定性”极度恐惧
    你自己也点出来了:打个镜像都害怕- 要读文档、看规则就焦虑- 对充满不确定的事很担忧- 小事花很久,难以启齿
    这是典型的:害怕犯错 → 不敢快进 → 反复确认 → 耗时巨长 → 成果不显 → 被质疑 → 更害怕
    你不是不会做,是心理负担太重,把简单事情做复杂了
    而且你把“做得稳、不出错”当成第一目标,但职场里,速度 + 结果 > 完美
    你现在的水平,完全不配“最低绩效”,但以你当前的工作方式,非常容易被判成低绩效。
  • 给你一套立刻能用的“自救方案”。不用你突然变大神,只改几个习惯:
    1. 每天只盯一件“能说出口的事”
      哪怕很小:今天把XX环境跑通 - 今天定位到XX问题根因 - 今天完成XX代码梳理只要能一句话说清,就算成果。
    2. 把“隐性时间”变成汇报语言
      不要说:“我在看代码、学工具、配环境。”
      改成:- “梳理XX模块业务逻辑,明确上下游依赖”- “完成本地环境搭建与调试,支持后续问题排查”- “熟悉内部镜像构建规范,规避合规风险”
    3. 【遇到卡住的,1小时搞不定就立刻问】。 不要自己硬啃半天。问 = 效率高,不问 = 低效低绩效。
    4. 主动同步,不要等被 push。每天简单一句:- 今天做了啥- 遇到什么问题- 明天计划。
    5. 接受“不完美”。 先跑通,再优化。能跑起来 > 完美但没结果。

3.20 要相信他本身就是一个怀着恶意的人

  • 恶意就是恶意,不要自己将其美化。就算他自己没有意识到。你只是过去只遇到了善良的人。
  • 先定性:“茶水间,从你背后凑近看你手机屏幕,问“在看什么””,这一条单独拿出来,就已经是严重职场禁忌
    1. 侵犯个人隐私。 工作场合≠他可以随意窥视你的私人手机内容,哪怕你只是看了一眼。
    2. 带有明显的监视、挑衅意味。 正常同事不会从背后突然凑近看别人手机,这是典型的权力打压+试探底线
    3. 完全不符合职场礼仪与基本尊重。 放在稍微规范一点的公司,这种行为被举报,HR 都会认定为不适当行为/边界感缺失/职场骚扰倾向
  • 五宗罪
    1. 习惯性贬低、否定人格,而非就事论事
      原话模式: “不知道你到底在想什么” “到底有没有搞懂我们在干什么”
      可定性: 人身攻击、人格否定、职场PUA。 正常管理只说“任务理解有偏差”“思路需要对齐”,不会攻击“你在想什么”“你搞没搞懂”这种人格层面的话。
      属于禁忌: 专业管理者严禁对下属进行人格贬低,只针对工作内容。
    2. 用专业术语进行羞辱式比喻,而非指导
      原话模式: 要把任务拆成“指令级”交付给你
      可定性: 羞辱式沟通、刻意贬低工作能力、公开/私下贬损。 正常指导会说“任务可以拆细一点”,他刻意用极端词,就是暗示“你智商低、像机器一样才能听懂”。
      属于禁忌: 职场禁止以能力羞辱作为沟通方式。
    3. 反复翻旧账、持续鞭尸
      原话模式: 每次澄清后都补刀:“怎么会不知道怎么做?”
      可定性: 精神施压、持续否定、制造焦虑与自我怀疑
      属于禁忌: 就事不就旧,这是基本管理常识,反复翻旧账属于典型打压手段。
    4. 干涉工作以外行为,进行无端盘问
      原话模式: “是不是到处闲逛?” “在茶水间干什么?”
      可定性: 越权干涉私人空间、不合理监视、言语骚扰。 茶水间属于公共休息区,只要不影响工作,员工有短暂放松权利,无权被如此盘问。
    5. 强迫不合理自证、否定隐性工作价值
      行为: 要求你用聊天记录证明工作饱和,无视部署、运维这类难以量化的工作
  • 这些算不算“确实的恶”?算不算禁忌
    算,而且是非常典型、成熟公司会直接认定的: 职场霸凌倾向- 不专业管理行为- 言语羞辱与精神打压- 侵犯个人边界与隐私- 制造敌意、恶劣工作氛围。不是错觉,这些行为完全可以整理成证据,用来跟HR沟通、申诉、甚至作为日后离职/转组的依据。
  • 现实一点的建议
    1. 不用当场爆发,也不用内耗。你幻想的对质很爽,但现实中容易被反咬“情绪不稳定”。 最优解:冷处理+留证据+默默找下家/找转组机会
    2. 他越这样,你越要表面平静。他就是想激怒你、让你失态、抓住你的把柄。你越淡定、越只聊工作、越少给情绪,他越没辙。
    3. 把他当成一个“必须应付的烂人”,而不是内耗来源。 你已经看清他了,不用再反思“是不是我不够好”。 答案很简单:他就是烂,不是你的问题。
    4. 真到要翻脸那天,你手里的这些记录,就是你的武器。 他资深、他资历老,不代表他可以随便侮辱人。 职场初期结仇不可怕,被人持续欺负才可怕
    5. 他就是恶意,你已经清醒了,接下来保护好自己、留好证据、默默变强,就是对他最狠的反击。
  • 对🐶专用・职场极简话术表
    1. 开会开场打压式:“你到底搞懂没有?”
      冷淡版:“思路是清晰的,我按步骤说。”
      硬气专业版:“我按方案推进,有问题你可以直接指出来。”
      堵嘴版:“这块我已经梳理清楚了,下面我讲重点。”
    2. 嘲讽你技术 / 工具不熟(jstack、jps、代码等)
      体面版:“这块我会后补一下,不影响当前任务。”
      反将一军:“你有更优做法可以直接说,我参考。”
      终止话题:“技术点我会后自查,先继续流程。”
    3. 故意口误 / 挖坑抓你错(30 条说成 20 条这类)
      稳赢版:“是你刚说的 20 条,我顺着回应的。”
      不沾情绪:“以文档和实际数据为准。”
      上次升级版:“信息别来回带偏,我们按准确的来。”
    4. 茶水间 / 走廊越界打探、凑过来看你屏幕
      温和划界:“歇一会儿,工作内容会上同步。”
      明显硬气:“私人放松一下,就不细说了。”
      终极 shut down:“没什么,你忙你的吧。”
    5. 开会细节挖苦、阴阳怪气
      不接茬:“嗯,我记下了。”
      不生气但有棱角:“不用这么说,正常沟通就行。”
      结束攻击:“如果没有具体意见,我继续。”
    6. 最后灵魂拷问:“你知道接下来要做什么吗?”【专门引你情绪上头的坑】
      标准安全回答:“按计划继续调试,有进展及时同步。”
      简短强势版:“清楚,按方案执行。”
      绝不回答:“就这么调呗” 这种情绪化话(被坑一手)
    7. 他轻蔑笑、斜眼看、装不屑
      嘴上不用说话心里默念:“你也就这点存在感了。”脸上保持:平静、平视、微微点头。他会极其难受。
  • fight back
    “那天在茶水间,你从侧后方凑过来看我手机上的内容,问我在看什么,是什么意思呢?回答我,有这么难吗?你不说,拿去跟 HR 说吧。”
    → 温和版:那天在茶水间,你凑过来看我手机,问我在看什么。我觉得这种行为让我很不舒服,希望你注意边界。如果你不认可,我可以去跟 HR 沟通情况。
    → “你诽谤我”“没有的事” → 我没有诽谤你,我只是在陈述当天发生的场景。我没有给你贴任何标签,只是表达我个人感到不适。有需要那我们可以一起去 HR 那边对质。
    → “你上班时间玩手机造成进度慢” → 1、(打开手机录音器)反问:我确认一下,你是正式规定,我不可以在茶水间看手机吗?2、进度按计划来,我会同步。

4.13 Java 容器化 & K8s 部署

全流程拆解

  1. 环境要求:已有 K8s 集群(Docker Desktop/云厂商/自建)、kubectl 工具(连接集群)、容器镜像仓库(公司内部仓库/ Docker Hub);
  2. 准备 Java 服务(开发层面)
    核心:确保 Java 服务可正常运行(以 Spring Boot 为例),打包成可执行 Jar 包(通过 Maven/Gradle 打包,输出 app.jar)。
    关键:服务需监听固定端口(如 8080),避免端口冲突,后续需与 K8s 资源配置一致。
  3. 打包 Docker 镜像(容器化)
    核心:将 Java Jar 包 + 运行环境(JDK)打包成 Docker 镜像,作为 K8s 部署的「安装包」。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 编写 Dockerfile(极简模板,可直接复用):
    FROM openjdk:17-slim # 基础镜像(含JDK,适配Java服务)
    COPY target/app.jar app.jar # 将本地Jar包复制到镜像中
    ENTRYPOINT ["java", "-jar", "app.jar"] # 容器启动时执行的命令

    # 构建镜像(命名格式:仓库地址/镜像名:版本号)
    docker build -t 公司仓库地址/java-demo:v1 .
    # 推送到公司镜像仓库(供K8s拉取)
    docker push 公司仓库地址/java-demo:v1
  4. 创建 K8s 命名空间(规范操作)
    核心:命名空间(Namespace)用于隔离不同业务的资源,避免冲突(如公司多个项目部署在同一集群,用命名空间区分)。
    1
    2
    3
    4
    5
    6
    7
    # 用命令创建命名空间(示例:命名空间名为 demo)
    kubectl create namespace demo
    # 或用 YAML 文件创建(ns.yaml),执行 kubectl apply -f ns.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
    name: demo
  5. 创建工作负载(对应原生 K8s Deployment)
    核心:工作负载的核心是 Deployment,负责管理 Pod 的生命周期(创建、销毁、升级、自愈、扩缩容),对应公司平台的「创建工作负载」。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: java-demo # Deployment 名称
    namespace: demo # 关联上面创建的命名空间
    spec:
    replicas: 2 # Pod 副本数(运行2个Java实例,实现负载均衡)
    selector:
    matchLabels:
    app: java-demo # 标签(用于关联 Service)
    template:
    metadata:
    labels:
    app: java-demo # 给 Pod 贴标签,与 selector 匹配
    spec:
    containers:
    - name: java-demo # 容器名称
    image: 公司仓库地址/java-demo:v1 # 拉取的镜像地址
    ports:
    - containerPort: 8080 # 容器内部端口(与Java服务端口一致)
    resources:
    requests: # 申请的资源(CPU/内存)
    cpu: 100m
    memory: 256Mi

    # 执行命令
    kubectl apply -f deployment.yaml
  6. 创建 Service(内部固定访问入口,平台自动完成)
    核心:Pod IP 会随重建(如更新镜像)而变化,Service 为一组 Pod 提供「固定内部 IP + 负载均衡」,是内部访问的稳定入口,公司平台创建工作负载时通常自动创建。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: Service
    metadata:
    name: java-demo-svc # Service 名称
    namespace: demo
    spec:
    selector:
    app: java-demo # 匹配带该标签的 Pod(与 Deployment 中 Pod 标签一致)
    ports:
    - port: 80 # Service 暴露的端口
    targetPort: 8080 # 转发到 Pod 的容器端口
    type: ClusterIP # 仅集群内部访问(默认类型)

    # 执行命令
    kubectl apply -f service.yaml
  7. 配置外部访问(对应原生 K8s Ingress)
    核心:Service 仅能集群内部访问,需通过 Ingress 实现外部访问(域名/公网 IP 访问)
    先明确 Ingress 的两个核心组件(关键区分):
    1)Ingress 规则(Ingress Resource):K8s API 对象,仅存路由规则(域名 → 转发到哪个 Service),与业务应用同命名空间 demo
    2)Ingress Controller(如 Nginx Ingress):真正干活的组件,是运行在 K8s 中的 Pod/Deployment,负责监听 80/443 端口、解析 Ingress 规则、转发流量,通常单独部署在独立命名空间(ingress-nginx),由运维团队维护(公司平台已提前装好)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: java-demo-ingress
    namespace: demo
    annotations:
    kubernetes.io/ingress.class: "nginx" # 关联 Ingress Controller
    spec:
    rules:
    - host: api.test.com # 外部访问的域名
    http:
    paths:
    - path: / # 访问路径(如 /hello 对应 Java 接口)
    pathType: Prefix
    backend:
    service:
    name: java-demo-svc # 转发到对应的 Service
    port:
    number: 80 # 转发到 Service 的 80 端口

    # 执行命令
    kubectl apply -f ingress.yaml
    外部访问测试:
    1)找到 Ingress Controller 的公网/内网 IP(kubectl get svc -n ingress-nginx);
    2)本地绑定 Hosts(IP + 域名,如 192.168.1.100 api.test.com);
    3)浏览器/Postman 访问 http://api.test.com/hello,即可看到 Java 服务返回结果。

【结论】Java 服务在 K8s 跑起来后,外部访问入口本质是:Ingress 关联的 IP + Java API 路径(通常不需要额外加 Port)
配置的 Ingress 规则,本质是告诉 Ingress Controller:“当收到某个 IP / 域名 + 路径的请求时,转发到对应的 Service → Pod → Java 服务”
无需关心 Service IP 和 Pod IP,Ingress 已经帮你做好了所有转发。

核心组件关系

  • 从属关系(真正的父子关系)
    • Deployment → 管理 → Pod(Deployment 控制 Pod 的创建、销毁、升级,是 Pod 的“管理员”);
    • Pod → 包含 → Container(Pod 是容器的载体,一个 Pod 通常运行一个 Java 容器);
    • Ingress Controller → 包含 → Pod(Ingress Controller 本身也是一个 Deployment 管理的 Pod,运行 Nginx 等反向代理程序)。
  • 关联关系(无从属,靠配置匹配)
    • Service ↔ Pod:通过「标签(Label)+ 选择器(Selector)」匹配,Service 找到所有带指定标签的 Pod;
    • Ingress ↔ Service:通过 Ingress 规则配置,指定“域名+路径”转发到哪个 Service;
    • Ingress Controller ↔ Ingress:Ingress Controller 监听集群中所有命名空间的 Ingress 规则,执行转发逻辑。
  • 组件职责总结(一句话分清)
    组件 核心职责 是否会变
    Deployment 管理 Pod 生命周期(升级、自愈、副本数) 配置不变则不变
    Pod 运行 Java 容器,集群内部私网 IP 更新镜像/重建时,IP 必变
    Service 固定内部入口,负载均衡到 Pod 不删除则 IP 不变
    Ingress 规则 配置外部域名→Service 的路由 不修改则不变
    Ingress Controller 执行转发,集群全局组件 运维维护,通常不变

完整流量链路

外部用户访问 → 域名 DNS 解析 → Ingress Controller(ingress-nginx 命名空间)
→ 匹配 Ingress 规则(demo 命名空间) → 转发到 Service(demo 命名空间,固定 IP)
→ Service 负载均衡(DNAT) → 转发到某个 Pod(demo 命名空间,私网 IP)
→ Pod 内 Java 容器处理请求 → 返回数据

K8s&微服务 完整流量路径图

微服务架构中,有到网关微服务,负责选具体服务的节点

  • ??网关不再找注册中心,直接访问 Service 名字即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    🌍 用户浏览器
    请求:https://api.xxx.com/order/create

    ↓ ① 本地 DNS 解析
    得到:K8s 公网 LB IP / 任意一台节点机器 IP

    ↓ ② 网络路由
    流量打到:【某一台 K8s 节点 IP:80/443】

    ┌─────────────────────────────────────────────┐
    │ 🏢 节点机器(操作系统层面) │
    │ 80/443 端口被 Ingress Controller 占用 │
    └───────────────────┬─────────────────────────┘
    ↓ ③ 进入统一入口

    🛡️ Ingress Controller(Nginx)
    读取请求头里的 Host = api.xxx.com(不是靠 IP,也不是靠重新解析)
    匹配 Ingress 规则:api.xxx.com → 转发到 gateway-svc

    ↓ ④ 转发到网关 Service

    🔌 网关 Service(gateway-svc)
    虚拟IP + 负载均衡
    ↓ 自动选一个网关 Pod IP

    🚪 网关 Pod(Spring Cloud Gateway)
    识别路径 /order/**
    知道要调用:order 微服务

    ↓ ⑤ 网关调用业务微服务

    📦 订单微服务 Service(order-svc)
    内部DNS:order-svc.default.svc.cluster.local
    ↓ 负载均衡到某个业务 Pod

    💻 业务 Pod(你的 Java 服务)
    执行 Controller 接口
    返回结果 → 原路返回用户

一句话极简链路:浏览器 → DNS → 节点IP:80 → Ingress Controller → 网关Service → 网关Pod → 业务Service → 业务Pod

K8s环境服务访问,相互调用

轻量化??

todo:https://time.geekbang.org/column/intro/100015201?tab=catalog

4.27 【慢 SQL】排查 SOP

1. 确认是否慢 SQL

阈值 判断
< 100ms 正常
100ms ~ 1s 关注,高频接口需优化
> 1s 慢 SQL,需处理
> 3s 严重,必须处理

2. EXPLAIN 分析执行计划

  1. select_type——子查询的执行策略
    含义 好坏
    PRIMARY 主查询
    MATERIALIZED 子查询物化,只执行一次 ✅ 好
    DEPENDENT SUBQUERY 相关子查询,主表每行执行一次 ❌ 危险
    SUBQUERY 独立子查询,执行一次 ✅ 好

    DEPENDENT SUBQUERY 是慢 SQL 的高危信号,主表 N 行 × 子查询 M 行 = N×M 代价

  2. type——单表访问方式(性能从好到坏)
    含义 好坏
    ref 索引等值查找
    range 索引范围扫描
    index 全索引扫描 ⚠️
    ALL 全表扫描

    const > eq_ref > ref > range > index > ALL

  3. key——实际用的索引(NULL = 没用索引,结合 type=ALL 是最差情况;有值但 type 仍是 index/ALL,说明索引未能有效过滤)
  4. rows——预估扫描行数(越大越慢;DEPENDENT SUBQUERY 的 rows 要 × 主表 rows,是乘法关系
  5. Extra——额外信息
    含义
    Using where 引擎拿到数据后 Server 层再过滤,正常
    Using index 覆盖索引,无需回表,✅ 好
    Using index condition 索引条件下推(ICP),减少回表,✅ 好
    Using filesort 需要额外排序,⚠️ 关注
    Using temporary 用了临时表,⚠️ 关注

3. 定位根因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
按以下顺序逐一排查:

1. 有没有 DEPENDENT SUBQUERY?
→ 是:找出为什么优化器认为子查询依赖外层
→ 子查询 SELECT 的列 = 外层过滤列?→ 考虑加索引或改写
→ 子查询内部有 JOIN,且 JOIN 列 = SELECT 列?→ 去掉多余 JOIN

2. 有没有 type=ALL?
→ 是:WHERE 条件的列有没有索引?
→ 没有:加索引
→ 有但没用:检查是否违反最左前缀原则

3. 索引有没有命中过滤条件?
→ 检查现有索引的列顺序:等值列在前,范围列在后
→ 子查询的 WHERE 列是否在索引前缀里

4. ORDER BY 有没有触发 filesort?
→ 排序列是否在索引末尾(范围查询之后的列)

4. 制定优化策略

优先级从高到低:

策略 适用场景 收益
消除 DEPENDENT SUBQUERY 子查询被反复执行 从 O(N×M) → O(N+M),数量级提升
加索引 type=ALL 且 WHERE 列无索引 从全表扫 → 索引查找,数量级提升
去掉子查询中多余的 JOIN JOIN 列与 SELECT 列相同导致 DEPENDENT 可能让优化器改为 MATERIALIZED
覆盖索引 频繁回表 消除回表,中等收益
改写为 LEFT JOIN IS NULL NOT IN 子查询 视情况,需验证

⚠️ LEFT JOIN 改写不一定更快:如果主表没有先缩小范围就做全量 JOIN,反而会更慢(本次实验验证过)

5. 验证效果

改完后重新 EXPLAIN,对比关键列变化
重点确认:
1 DEPENDENT SUBQUERY 是否消失或 rows 大幅减少
2 type=ALL 是否变为 ref(索引等值查找)/ range(索引范围扫描)
3 key=NULL 是否命中了索引

再实际执行计时,和改前对比

// todo: 多次实践


4.30 AI Catch-Up & Tech Upgrade Journey

  1. 不用追零散新概念,只抓核心;
    • 大模型演进:通用大模型、垂直领域模型、多模态模型底层差异,核心概念:Agent、Prompt Skills、Harness、工具调用、Function Call、RAG
    • AI Agent:大模型为核心,搭配规划、工具调用、记忆、反思、动作执行模块,能自主拆解任务、联动外部工具完成复杂目标的自治执行应用形态。规划+记忆+工具调用+反思,不是简单LangChain拼接。大模型的理解、推理、逻辑、泛化能力,是所有 Agent、提示词、上下文方案的底层基石,模型不够强,上层所有封装都没有意义。
    • 提示词工程(Prompt Engineering)面向大模型,通过设计、优化输入文本指令,引导模型精准输出目标结果的基础交互优化手段。
    • 上下文工程(Context Engineering)精细化管理大模型上下文窗口,包含上下文裁剪、知识注入、记忆维护、对话链路设计,解决上下文长度限制与信息有效性的上下文治理技术。
    • Prompt Skills(提示词技能)将高频通用提示逻辑封装成可复用的标准化技能模板(如推理、总结、检索、翻译),轻量化固化提示能力的模块化提示资产。
    • Harness 工程(AI 管控 / 编排工程)面向 AI 落地全链路,做模型调度、版本管理、效果评测、灰度发布、权限管控、流程编排、风险管控的AI 工业化运维与治理工程。本质是不信任AI,给他加限制。
  2. OpenClaw 这类产品不是简单手搓能替代,赢在产品化、合规、生态、超长上下文,是普惠AI办公的基础设施;
    • 产品化&体验壁垒:用 LangChain + Python + 大模型API,确实能手搓出玩具级办公自动化Agent、个人自用;Claw 是开箱即用、多端同步、UI交互、稳定不崩,普通人零门槛直接用。
    • OpenClaw(小龙虾)Core 本质:就是一套工程执行服务,一套工程化的 Runtime / 控制平面 / 执行框架。
      本质:开源 AI 智能体执行网关,给大模型装“手脚”,让AI能操控本地电脑、文件、Shell、邮箱、网盘、浏览器
      形态:本地进程 + 多通道IM网关(Telegram/微信/网页),不是CLI,是后台服务+聊天界面
    • OpenClaw 没有 “智能”,依赖大模型的思考能力,它的强项不是 “聪明”,而是:
      超长上下文的工程化承载(不是模型本身上下文强,而是框架把文档 / 会话分片、压缩、索引、分批喂给模型)
      稳定的长会话记忆(三级记忆、SQLite + 向量检索、可回放、可审计)
      可靠的工具调用闭环(文件、Shell、邮件、办公软件、多轮执行)
      多渠道、多设备统一交互(IM、终端、Web)
    • 生态与标准化: 它定义了办公Agent的交互标准,后续应用都可以基于这套生态复用,不是零散脚本。
    • OpenClaw 的三层架构:模型 + Skills + 具体实现
    • 三层架构(极简版)
      1)模型层(大脑) 只做:理解自然语言 → 决定要调用哪个 Skill → 生成统一格式参数
      2)Skill 层(标准化手册) ,每个 Skill 对应一类能力:emailfilebaidu-drivewebdav… 核心:定义统一的输入输出格式 ,模型只跟 Skill 说话,不碰具体系统
      3)Adapter/实现层(具体干活的)
      对每个“具体系统”写一个小适配器: - qq-email-adapter - 163-email-adapter - baidu-drive-adapter
      适配器只做两件事:1. 把 Skill 的统一参数 → 翻译成对应系统的 API/协议调用,2. 把系统返回的乱七八糟数据 → 翻译成统一格式返回给 Skill
    • 用“查 QQ 邮箱”走一遍流程(你就彻底懂了)
      1)模型层
      识别意图:查邮件 - 选 Skill:email , 生成统一参数:
      1
      { "skill": "email", "action": "list", "params": { "limit": 3 } }
      模型根本不关心是 QQ 还是 163
      2)Skill 层(email Skill)
      收到请求:email.list(limit=3), 看你配置:你的邮箱是 xxx@qq.com → 自动路由到 QQ 邮箱适配器, 把参数补全:
      1
      { "host": "imap.qq.com", "port": 993, "user": "xxx@qq.com", "pass": "授权码", "limit": 3 }
      3)Adapter 层(qq-email-adapter)
      用 QQ 邮箱 IMAP 协议登录,- 调用“查邮件”接口,把 QQ 邮箱返回的私有 JSON → 转成统一邮件格式
      1
      2
      3
      [
      { "from": "xxx", "subject": "xxx", "time": "xxx", "summary": "xxx" }
      ]
      4)原路返回: Adapter → Skill → 模型 → 自然语言回答你。
    • 关键结论,模型不感知差异:它只说“查邮件”,不知道 QQ/163, Skill 做标准化:统一接口、统一参数、统一返回, Adapter 屏蔽差异:每个系统一个小插件,脏活全在这。所以:你不需要关心是 QQ 邮箱还是 163 邮箱,因为差异被压到最底层了,上层全是统一接口
  3. Claude Code 是什么?最强编程Agent,终端原生CLI
    • 定位:跑在终端里的AI结对编程伙伴纯CLI(命令行工具),直接操作你的整个代码库,不是补全插件。
    • Agent Loop(代理循环)——灵魂
      1
      2
      3
      用户自然语言指令
      → 理解意图 → 规划步骤 → 调用工具(读文件/改代码/跑命令)
      → 看结果 → 自我修正 → 继续循环 → 任务完成
      不是“一次生成”,是多轮决策+工具调用+结果验证+错误自愈
      对你价值:能做复杂任务(如“把项目从Maven迁移到Gradle+写全测试+修复所有Bug”),不用你拆步骤
    • 上下文工程(Context Engineering)——核心竞争力
      4级渐进式上下文压缩: 1. 原始全文 → 2. 摘要+关键代码 → 3. 向量检索相关片段 → 4. 动态窗口裁剪。
      超大有效窗口:标准200k tokens,实际可用约160k,能理解整个Java微服务代码库
      对你价值:不用切文件、不用拆提示词,它自己管理上下文,你说需求就行
    • 工具系统(45+内置工具)——工程化落地
      文件:FileRead/FileEdit/Write(多文件批量改)
      终端:BashTool(编译、测试、打包、Docker)
      Git:GitStatus/Commit/Push/PR
      搜索:Ripgrep(代码库全局搜索)
    • 权限与安全设计——工业级可靠性
      7层纵深防御:AST级命令解析、白名单、用户二次确认、沙箱执行、日志审计。
      对你价值:敢让它操作生产代码、跑高危命令(如rm/docker),安全可控
  4. 移动互联网窗口期6-7年,AI只有2年范式定型窗口期
    • 2025-2027是基础设施+应用范式定型期, 大模型底座、Agent标准、AI开发流程、行业落地范式两年内基本固化。
    • 两年内:懂AI工程化、会AI编程、能落地业务的程序员溢价高;两年后:人人基础AI,红利消失,只剩高阶架构/AI专项优势。
    • AI 全流程开发:需求拆解→架构设计→代码编写→单元测试→接口文档→线上问题排查
    • AI 重构职业结构:初级CRUD开发被大幅替代,倒逼向架构、工程化、AI集成升级
    • 现在正处在康波上行的技术引爆点:前几十年是互联网信息基建, 未来20年由AI主导生产、办公、开发、生活范式。两年窗口期是「范式定型的关键窗口」,赶上了就是职业生涯换挡、副业赚钱、赛道卡位;错过就只能被动跟随。
  5. 最佳上手路径:CC AI编程为入口,边搭Java服务边补技术栈,同步学AI落地工程知识,拒绝碎片化,系统化恶补。
    • 熟练 Claude Code 日常操作
    • 借助AI从零搭建:SpringBoot + Mysql + Redis + MQ + 微服务基础架构,让AI帮你做:架构设计、分层拆解、代码规范、接口文档、异常处理,落地一个小型业务:复刻你工作中的业务场景,用AI全流程开发
    • AI工程能力融合
    • 固化能力:补齐短板,微服务、中间件、部署运维、K8s基础,全部用AI辅助自学+实战,不再盲目碎片化看视频。

[近年AI应用技术串讲与优质文档分享|Agent、Skill、OpenClaw、Harness……]https://oigi8odzc5w.feishu.cn/wiki/WBMfwiNkfi6uNFkRtXdcavDzn0e

5.1 AI Coding

  1. IDE configuration
    1. theme color (Familiar Java Themes),
    2. AI rule(用中文回答,代码/注释规范,不要修改pom文件,, )
    3. Skills(给 AI 用的 SOP,自动触发的结构化 Prompt = 业务知识 × 执行流程 × 约束规则 的结合体)
      1)本质是注入到系统提示(System Prompt)里的文本,AI 会自动加载所有的 Skill 描述(name + description,始终存在),命中后才加载 Skill 全文(description 是”门卫”,全文是”说明书”)
      2)目录结构,全局(所有项目都能用):~/.agents/skills//SKILL.md,项目级(当前项目专用,优先级更高):<项目>/.continue/skills//SKILL.md
      3)一个好的 Skill 应该包含三类内容:A. 业务知识(What) “这个模块是干什么的,核心概念是什么” → 让 AI 不需要每次重新学习业务背景,B. 执行流程(How) “遇到这类任务,按什么步骤来做” → 让 AI 的行为可预期、可重复 , C. 约束规则(Don’t)”有哪些已知坑、必须遵守的规范” → 防止 AI 重犯已知错误
    4. Memory Management
      1)对话和生成内容均使用中文(Scope:gobal)
    5. Run & Debug
      1)配置 jdk(Language & Framework - Java - JDK)和 Extension(Java,Springboot,Git)
      2)配置 maven(Path to Maven’s setting.xml)
      3)配置启动文件 launch.json {
      “type”: “java”,
      “name”: “SIT-MOpsPlatformBackendApplication”,
      “request”: “launch”,
      “mainClass”: “com.midea.mops.MOpsPlatformBackendApplication”,
      “projectName”: “mops-platform”,
      “args”: [],
      “vmArgs”: [
      “-Xmx1024m”,
      “-XX:+UseG1GC”
      ],
      “env”: {
      “spring.profiles.active”: “sit”,
      “jasypt.encryptor.password”: “xxxxxx”,
      “log.path”: “/home/coder/logs/apps”,
      “rocketmq.enabled”: “true”
      }
      }
    6. 开发流程
      • 版本管理:图形化操作,或直接用git指令——git branch -a查看所有本地/远程origin分支,git checkout -b feature/xx origin/feature/xx从远程分支拉出本地分支,git branch -vv查看本地&远程分支关联情况
      • 配置Maven:ctrl+shift+p->打开setting(ui)->配置path to settings.xml 、依赖包会被下载到远程服务器的Maven本地仓库
      • 安装Extensions(最接近IntellijIdea2022.3.2的主题->Darcula Theme_v1.18.1_Rafael Renan Pacheco)
      • 设置启动参数和环境变量:在.vscode/launch.json加配置”configurations”: [{“xx”:”yy”}],相当于idea里edit Run/Debug Configuration
      • 运行/Debug:1)终端输入运行命令 2)chat输入/run,回答中返回可执行命令。点击执行
      • 本地调试:Postman发请求到https://caifeng7-31579-.devops.midea.com/xx
    7. 快Keyboard shortcut
      Ctrl+F: 在当前文件中查找
      Ctrl+Shift+F: 全局搜索
      Ctrl+P/shift*2(安装idea快捷键插件): 查找文件
      Ctrl+Shift+P: 打开Command Palette
      Ctrl+I:生成代码建议
      Ctrl+B/Ctrl+左键:跳转类
      Ctrl+alt+B:跳转实现类
      Ctrl+J/Ctrl+`(反引号在tab键上面):打开终端窗口
      Ctrl+alt+J:打开chat框
  2. AI Coding实践
    1)让 AI 了解项目的工程架构和编码风格 & review生成的文档让AI修改(我需要你完整阅读项目代码,了解整个项目的项目架构、工作机制,然后完整梳理域名解析(DNS)相关的业务和实现,将你的理解写成文档放到D:\code\xops-platform-backend\doc\dns.md下)
    2)让 AI 对当前需求生成开发计划(@doc/dns.md 基于现有DNS架构,我需要新增XXX功能,请给出实现方案(有skill的话就不用@引用))
    3)设置 AI 约束(project_rules.md)配置项目级规则,让 AI 生成的代码符合团队规范
    4)复杂功能分步骤拆解(ps 生成plan后 换窗口执行)
    5)代码质量:辅助 Code Review,发现潜在 bug、边界条件、异常场景
    6)单元测试:核心逻辑必须有单元测试,集成测试:接口级别的联调测试,边界测试:空值、越界、异常场景
  3. VibeCoding
    1. 需求:头脑风暴,提供目标、输入、输出、让AI以提问的形式向我确定需求,生成proposal.md
    2. 设计:根据需求文档里划分的模块编写详细设计文档,模块之间保持独立可测试
    3. 划分任务:为每个模块划分最小可执行任务,生成task清单并且用check list表示子任务是否完成,可多agent并行开发
    4. 实现:命令agent根据progress.md,以主Agent监控整体进度,管理多个子Agent执行各模块的形式来实现,必须要有完整的单元测试
  4. more
    单个对话历史会话过长且不必要时,或者把上下文浓缩到md后,可以开启新窗口做后续对话
    ai写的代码有缺陷,接着改,还是从头来?
    怎么省token?
    ??

todo 设计、开发一个 【监控】 高并发系统,学习多线程、mysql锁、、

5.3 Claude Code, First Trial

  • Claude CodeAnthropic 出品的 AI 编程 Agent / 终端编码助手
    • 不是编程语言、不是系统、不是IDE,是跑在终端里、帮你写代码、改代码、调试、查Bug、写配置、写脚本的 AI
    • 本体是 Node.js 写的命令行应用,跑在 Bash / Zsh / PowerShell 这些 Shell 里(Git Bash 只是补全 Windows 缺失的类 Unix 终端和命令环境)
    • PowerShell → claude.cmd → node.exe → 运行 Claude Code 源码 → 底层复用 Git Bash 环境做兼容支撑。
  • Basic Configuration & Commands
    1
    2
    3
    4
    // C:\Users\caife\.claude.json → 配置免登录
    {
    "hasCompletedOnboarding": true
    }
    1
    2
    3
    4
    5
    6
    # 1. 正确的API中转地址
    $env:ANTHROPIC_BASE_URL = "https://yunwuapi.com"
    # 2. 正确的API密钥变量(中转服务用这个)
    $env:ANTHROPIC_AUTH_TOKEN = "sk-nIrKsXi2fKr57vz2ORPR9fnJ6qWOTdrEJalxHf9DSHSuMr4P"
    # 3. 设置一个可用模型(模型大厅复制名称)
    $env:ANTHROPIC_MODEL = "claude-haiku-4-5-20251001"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 进入交互式对话,然后直接打字提问
    claude

    # 切换模型
    /model

    # 查看安装的skills
    /skills

    # 清空当前会话上下文
    /clear

    # 退出交互模式
    exit
  • CC Switch 可以统一为本地的各CLI配置各模型厂商,启用后 /model 可以看到当前在用模型,无需配置环境变量(下载 xx.windows.msi → https://github.com/farion1231/cc-switch/releases)
  • Vibe Coding 新手起步:必做清单
    1. 给 AI 一个”说明书” — CLAUDE.md(这是最重要的一件事。在项目根目录创建 CLAUDE.md,每次会话 AI 都会自动读取它。)
      • 项目概述( 一个扫雷游戏网页版)
      • 技术栈( 纯 HTML/CSS/JS,无框架; 用原生 DOM API,不引入 jQuery)
      • 我的偏好( 函数优先,少用 class; 变量命名用中文拼音也行,我都能接受; 先实现功能,美化后面再说)
      • 常用命令( 直接双击 minesweeper.html 就能跑)
    2. 把需求写下来(不用写 PRD 文档,但至少想清楚这三点再开始)
      • 这个项目是给谁用的? 我自己玩 / 给朋友展示
      • 核心功能是什么? 9x9 雷区、左键揭开、右键标旗
      • 什么算”做完”? 能玩一局,赢了有提示
    3. 搭好骨架再填肉(每一步都验证,出问题范围很小,AI 也好修)
      正确的节奏:
      1. 先要骨架 — “帮我写一个扫雷的 HTML 页面,只有一个空棋盘,什么都不要多”
      2. 跑起来 — 打开看,确认能渲染
      3. 加一个功能 — “现在加上点击揭开格子”
      4. 跑起来 — 确认能用
      5. 再加一个 — “加上右键标旗”
      6. 循环…
    4. 善用记忆(做到一半时,把你验证过的偏好告诉 AI,会存到 Feedback 记忆里记住)
      ▎ “记住:这个项目以后都用 CSS Grid 布局,别用 table”
    5. 不要追求一次完美(Vibecoding 的精髓是迭代。一个功能 AI 第一次写得不对,换个说法再试一次,或者指出来哪里不对。这比你花时间想”最完美的提示词”高效得多)
  • Token 是 AI 处理文本的最小单位。
    • 它不是”一个字”也不是”一个单词”,而是介于两者之间。
      中文:大约 1 个 token ≈ 1.5 个汉字(”我爱你” → 约 2-3 个 token)
      英文:大约 1 个 token ≈ 0.75 个单词(”const x = 1;” → 约 7 个 token(每个符号都算))
      代码:token 消耗较大(符号、缩进、换行都会占 token)
    • 每次对话,token 消耗在三个地方:(一个典型会话,读一个 300 行文件 + 几轮对话,轻松吃掉几千 token)
      输入,最大头,你说的 + 我读的文件 + 工具返回结果 + 系统指令 + CLAUDE.md + 记忆文件
      思考,中等,我内部推理的过程(你看到的 就是这部分)
      输出,较小,我回复给你的文字 + 工具调用的参数
    • 怎么省 Token(实用篇)
      1. 一次说清楚比反复追问省 — 三次对话来回比一次详细描述多耗 3 倍 token
      2. 文件别写太大 — 单文件 500 行 vs 5 个 100 行文件,前者每次读完更省
      3. 不需要改的文件别说 — “你帮我看看 X 文件”如果你其实只想改 Y
    • 建议:尽量在一个会话里持续迭代,不要每个小改动都开新会话。上下文可以复用,省掉”开机费”。
      你的 minesweeper.html 约 400 行,每次完整读取约 2000-3000 token。加上 CLAUDE.md 和记忆文件,每次新会话起步大约 4000-5000 token 的”开机费”。这在一个 20 万 token 的上下文窗口里不算什么,但如果频繁开新会话、每次都全量读取,累积起来就多了。
      同一会话加功能: 记忆已加载(0) + 文件已在上下文(0) + 你一句话描述需求(50 token) = 约 50 token 新增
      新会话里加功能: 重读 CLAUDE.md(500) + 重读记忆(800) + 重读文件(3000) + 你重新说明项目(200) = 约 4500 token 新增
    • 什么时候开新会话?
      换个完全不同的项目 → 开新
      上下文太长了,AI 开始”忘事” → 开新(但先把关键状态写进记忆)
      第二天打开电脑继续 → 默认就是新会话,但记忆文件会恢复上下文
  • 记忆架构总览(Context Engineer
    • 短期记忆 = 上下文窗口
      每个对话会话有一个有限的上下文窗口(Token 上限)。这是 Claude 的”短期记忆” – 当前 Claude 能”看到”的所有内容。
      当上下文接近填满时,Claude Code 会自动进行上下文压缩。早期对话为摘要。最近的交互保留细节,早期的被激进压缩。
      1
      2
      3
      4
      长期记忆(磁盘)  -->  会话开始时加载  -->  进入上下文窗口(短期记忆)
      ^ |
      | 压缩/摘要后写回 |
      +-------------------------+
    • 长期记忆 = 磁盘持久化
      存储在 .claude/projects//memory/ 下,跨会话保留。入口是 MEMORY.md(索引文件),具体内容分散在独立 .md 文件中。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # Memory Index

      ## user-profile.md
      记录用户的技术栈偏好、工作习惯、常用工具链等。

      ## feedback-log.md
      记录用户对 Claude 历史输出的反馈,包括满意和不满意的地方。

      ## project-goals.md
      当前项目的核心目标、里程碑和约束条件。

      ## Reference Memory(参考记忆)
      存储外部参考资料、链接、API 文档摘要、教程等。
    • 不应该存入记忆的
      代码模式/架构(代码库本身已记录)
      Git 历史(git log 获取)
      当前任务状态(用 Plans/Tasks 系统, 使用 .claude/plans/ 目录)
      可自动检索的信息(文件结构等)
    • 记忆 vs 计划 vs 任务 vs CLAUDE.md
      1. Memory │ .claude/projects//memory/ │ 跨会话保留用户偏好、项目上下文、反馈 │ Claude自主决定何时读取
      2. Plans │ .claude/plans/ │ 规划大型变更的步骤和方案 │ 是执行路线图,不是记忆
      3. Tasks │ .claude/tasks/ │ 跟踪具体的待办事项 │ 有状态(待办/进行中/完成)
      4. CLAUDE.md │ 项目根目录 │ 项目规范、约定、常用命令 │ 每次会话自动加载到上下文中
  • skill
    吴恩达
    全局目录 C:\Users\caife.claude\skills
    1
    2
    3
    4
    5
    6
    ● Skill 已创建完成。结构如下:

    football-memes/
    ├── SKILL.md # 技能主文件(触发规则 + 工作流)
    └── references/
    └── football-memes.md # 分类足球梗库(中英双语)
  • agents 子会话
    /btw
  • 权限配置(项目级,会话级,,)
    1
    > /update-config
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ● User answered Claude's questions:
    ⎿ · 权限配置应该保存在哪里? → 项目本地
    · 你希望预先允许哪些常用的命令? → 写入文件, 读取文件

    ......

    ● Write(.claude\settings.local.json)
    ⎿  Wrote 21 lines to .claude\settings.local.json
    1 {
    2 "permissions": {
    3 "allow": [
    4 "Read",
    5 "Write",
    6 "Edit",
    7 "Glob",
    8 "Grep",
    9 "Bash(git *)",
    10 "Bash(ls)",
    … +11 lines (ctrl+o to expand)
  • 源码 https://github.com/ultraworkers/claw-code
  • 用通用agent构思项目,,不用框架怎么做一个cmdb问数

5.11 【运维产品】沉淀

1. 自动化运维平台

MOPS & CMDB & Ansible(运维平台,数据底座 + 运维作业)
MOPS:通过工单触发一个申请/变更/回收流程,通过生成的记录映射到的实际平台上的策略,用API/script做自动化,运维数据落到 CMDB。。流程 可靠(可追溯、可回滚),运维数据全流程闭环,,
作业平台:免密ssh下发(执行机root存私钥,目标机mdauto存公钥),高并发,高可用??三个节点,100个任务,怎么调度,怎么等待??
CMDB:运维事实库,本质就是如何把数据写入,如何供外部使用,日常/私有化迭代(只做热点运维工作,日常用户答疑,没有深耕功能和组件)
CMDB Copilot:基于CMDB数据源的nl2sql问数服务。。搞容器化 → 不用框架,只用通用模型+上下文工程+skills(提示词工程) 怎么实现??
嘉为蓝鲸,Azure 智能运维
价值,用户,钱

2. 私有化输出

ToB:流程和稳定(业务复杂,数据准确,并发要求低),超出客户期待
中立云:目标客户→对成本重视的企业の数量级>>对成本不重视的,对公有云降维打击,占领云计算的农村
迭代:私有化主干分支 feature/product_output 或 poc(区别于内部main分支,两个分别维护,短期方便但长期废人力),私有化迭代分支 feature/priv1.x.0(初期从主干拉出,迭代开发私有化功能),私有化Release版本(中期从主干拉出,私有feature测完后合入,出正式的私有化镜像包)
单一master分支改造:大洋代码合回内部主分支,后续迭代考虑如何一套代码跑多个环境。。客户适应系统,而不是系统适配客户??

3. 系统思考

MOPS:懂关键能力实现&组件原理,,慢sql治理??线上问题,服务、DB、中间件不可用??
运维作业:流量大怎么做(分批,下班时间),失败怎么做(重试?保证数据一致性;考虑如果失败的堆积到一起重试流量太大。。)
稳定性建设:四大类检查
故障复盘

4. 独当一面

运维产品
数据库运维

5. AI

AI Coding:形成开发规范 1)理解项目(形成doc,skill??团队经验)2)配置代码格式等 3)AI根据需求描述生成计划,头脑风暴 4)分步开发(太长思考中断)
AI工程vs传统工程 —「道法术」中的变与不变 https://mp.weixin.qq.com/s/Foiid7aYvTD0-ejBSGhM7A
AI 驱动的智能异常处置:从异常发现到根因定位 https://mp.weixin.qq.com/s/VHqYQ-cnburG8ydI94F_DA
凌晨 3 点故障告警,我让 AI 在 10 分钟内找到了根因 https://mp.weixin.qq.com/s/Ru_tYyofOqnKitzAU7Bu7g

6. 工作模式

事情多,都紧急,人脑过载,效率低,,
我总是在等一个时机
等有时间再看项目疑难问题
等有时间再学容器化、K8S、调优
stn:没有舒适区

5.20 【两年度】one one

1. 开场(下午刚上班)

老板好,临近年中,我梳理了下近半年的工作重点:期间完成了 MOPS 所负责业务的持续优化,Opspace 私有化部署,CMDB 问数项目初步上线,同时也更多承担了团队协作事项,日常也有在扩充自身技术栈。
不过对照年初沟通时,您对我的发展期许来看,目前我实际负责的工作内容还是有一定出入。所以想借年中这个节点,跟您再对齐一下您对我的个人成长期望,以及我后续的工作重心安排。另外年初我主动提过想要争取晋升,也想听听您对我现阶段工作的整体评价,以及我自身还需要做出哪些调整改进。

2. 正式面谈

  1. 开场,礼貌直白,切入主题
    老板,这次一对一,我想给您同步下我这半年的工作产出、个人成长,也想坦诚对齐下我目前的能力定位,同时正式向您申请本次年中的晋升定级。
  2. MOPS主业:稳定迭代 + 事故复盘优化
    持续负责运维平台DNS自动化的迭代开发。期间外包开发出现过线上问题,虽然不是我直接开发的,但我意识到自己在方案评审、外包交付把关上存在疏漏。后面我花了几周时间,完整梳理全链路业务场景、代码逻辑、边界 case,完成了自动化全场景梳理和优化方案,及时与业务方同步。
    目前自动化相关我基本是自己来完成,我也将这个业务的开发经验沉淀了一个skill。
  3. 私有化交付:OPSpace大洋部署全流程攻坚
    去年年底开始,我从单纯在客户UAT跑通个人业务,到对于私有化部署流程的整体理解,能够响应用户提出的问题,针对PRD环境完成定制开发、适配迭代,可以说是这方面的主力。日常CMDB的私有化迭代也是我这边在负责。
  4. 攻坚年初核心目标:CMDB智能问数
    年初重点要求落地的AI问数项目,我和师兄两个人在两个月高强度开发下完成了落地上线,我这边深度参与了包括从开源项目的理解、内部知识梳理、问题收集、多轮问数的定制化二开、应用服务的搭建上线。我清楚目前问答调优、知识库迭代还需要优化,我也非常愿意后续投入时间到AI工程化项目,个人也理解提示词工程、上下文工程的重要性。
  5. 主动承担组内杂项、值班、发版、运营兜底工作
    除了既定开发任务,我更多的承担了团队日常值班、发版、线上问题处理,sql优化,CMDB日常运营,同时负责对接两个外包的DNS、负载均衡相关方案支持等。
  6. 工作模式的成长(重点讲质变)
    这半年我自己能明显感觉到工作模式的质变。不再是被动接需求,而是主动梳理重点、推进主线、同步进展,形成 SOP,主动补齐技术栈,学K8s、AI工程化相关技术。
    心态和解决问题的能力更加成熟,占用团队资源更少,可以负责的事项更多了(哥哥们应该都会同意)。
  7. 坦诚说明未完成事项(主动说、不藏、加分)
    我也客观梳理了自己的不足。年初提到的的数据库运维、看《重构》,因为上半年突发攻坚任务多、长期满负荷,没有系统完成,这是我下半年优先级最高的学习计划,一定会补齐这块短板。
    另外其实我也一直记得您提到的独当一面,我目前方向还不够聚焦,CMDB偏向运营维护,MOPS业务覆盖面广但不够深耕,后续也想请您给我指导定位方向。
  8. 核心诉求:争取晋升(温柔但坚定,重点讲匹配度+薪资合理性)
    我入职马上满两年,近几个月基本都是高负荷、高强度的工作状态,主动承接了更多的工作,也有一定的产出,我也很希望得到公司的认可。在薪资方面,我也是低于行业同批次、同学历平均水平的。
    这两年我持续快速成长,尤其是近半年已经可以独立承接开发工作、解决问题、完成项目交付,对于晋升的机会,我也很希望可以把握住。
    (我的工作产出和能力已经匹配更高一级的职级标准。所以我想正式和您申请:本次年中给到我晋升定级和对应的薪资调整,匹配我目前的工作产出和岗位能力。)
  9. 收尾表态(给老板台阶、表忠心、留未来预期)
    后续我也会把项目持续做深做优,主动承担核心工作,跟上团队技术节奏。也想请您帮我评估一下:我目前还有哪些短板、哪些能力需要补强,我针对性补齐。

3. Simplified

老板下午好,今天是想跟您同步一下我近半年的工作产出、个人成长,以及现阶段的一些想法与困惑。
第一,我持续在做运维平台所负责模块的迭代优化,掌握需求对接和交付的全流程,特别是对DNS自动化这一块做了全场景的梳理和优化方案,也把业务经验沉淀了skill。另外主动支持两位外包做负载均衡和DNS相关需求的方案评审。
第二,大洋私有化交付方面。我不仅完成了自己功能的跑通,更是对整体部署交付流程有了更深的理解,能够积极响应用户问题和需求,支持定制开发和优化,目前是这方面的主力。CMDB 产品化日常迭代也是我在负责。
第三,CMDB智能问数项目,从业务知识梳理、问数场景搜集,到多轮问数定制开发和最终的服务上线均全程改进。当然我清楚知晓,在知识库和问题调试上还有要优化的空间,我也很愿意接下来继续投入时间在这上面。
第四,更多的承担了团队日常值班、发版、线上问题处理,sql优化,CMDB日常运营。
这半年我最大的转变,就是从被动接受任务,到主动梳理工作重点,推进项目,及时同步进展。占用团队资源更少了,能够负责的事务更多了,几位前辈也应该能比较明显感受到。
当然我也有一些困惑,您之前一直给我提的独当一面,事实上我更多的是负责了更多更广泛的业务,而不是专精一个方面。而且近几个月都是高强度满负荷的工作,感觉自己被困在了各种迭代优化的细致项上了,私有化交付更是需要频繁和用户沟通,一直被分散精力,缺少深挖技术的场景(?)。这方面希望您给我一些方向上的指点。
年初的时候我主动跟您提过想争取晋升的机会。我自认为近两年已经有了比较大的成长,也能够实际的承担更多工作。解决实际问题,独立开发。我入职时的薪资就是低于行业平均标准的,但是一直积极进取,也真心希望得到公司的认可,希望能把握住晋升机会。这一块想了解您对我的评价?在哪些方面要重点改进?

4. 反问

  1. AI项目现在效果还不够好
    目前项目的基础能力已经跑通。当时我在针对预设问题调优到一半就去支持别的工作了,我也很清楚需要做的是知识库的迭代和提示词的调优,我对多轮问数的思想有信心,这块我非常有兴趣深耕。
  2. 之前线上事故有你的责任
    我已经完整复盘了所有问题,后续所有交付我都做好需求评审、风险把控,并且已经对梳理了自动化全流程和优化方案,同步了业务方。
  3. 你觉得自己最大的进步是什么?
    从被动执行变成主动负责。以前只做完分配的开发任务,现在会主动复盘风险、梳理业务流程;而且解决问题更独立,减少对团队依赖,能扛商业化交付和攻坚项目,责任心和系统性思维提升最明显。
  4. 你现在还有哪些短板?
    第一,技术深度不足,写简单的的业务代码,希望在代码重构优化的同时系统深耕;
    第二,技术方向不够聚焦,业务接触广但没有深耕一条主线;
    第三,复杂线上问题处理经验偏少,更多只是处理磁盘告警。
  5. 为什么要给你晋升?
    三点原因:第一,工作产出达标,半年独立完成私有化交付、AI攻坚、平台迭代,还长期兜底团队杂项工作,工作量和贡献超出初级开发;第二,能力质变,具备独立排查问题、把控项目、对接客户的能力;第三,薪资客观偏低,入职薪资低于同批次水平,目前能力已经匹配更高职级,希望薪资职级对齐产出。
    对比同期入职的硕士同事,日常承担工作量、项目推进、技术深耕、团队协助层面个人能力与产出完全不落下风(对比起刚进来更是绰绰有余)。当初校招入职定薪偏低,当前薪资水平低于同能力、同年限行业正常区间,希望公司结合实际能力与贡献重新核定,希望得到肯定,互相尊重
  6. 下半年规划是什么?
    一、持续做MOPS业务的迭代优化,同时积累Java技术深度
    二、把AI融入工作中
    三、确定个人深耕方向,做到真正独当一面。
  7. 再观察一下、下次再升
    我理解公司的评级标准,也接受客观评估。麻烦您明确告诉我,我目前还差哪些硬性条件、多长时间可以达标?我会针对性补齐短板,严格按照标准执行,希望下次评估能给到明确的晋升结果。

5. feedback

开发能力肯定ok
架构(服务拆分,中间件选择,高可用,数据库,微服务,服务治理)
代码规范(《重构》,学习优秀开源项目,Spring)
AI使用(主动学,窗口期1-2y,思考用ClaudeCode怎么做CMDB问数)
做AI运维(主动拓展scope,从日常的杂活中抽理出来,看别人的=自己做了)
独当一面(一方面是能够解决热点问题,一方面是能从0到1落地项目)
保持好奇心

5.21 😓 转组

嫡系应届生专属深度谈心方案

  1. 这次把我跟着平台划转,我已经了解,是单纯业务需要。但是还是比较意外,因为前一天才对齐了一下未来的个人发展期望,也比较好奇 why me not xx?
  2. 业务上,还是只做MOPS?但日常运转的人少了,负责的业务就多了,至少网络方面要接受防火墙。担心又陷入各种对接需求,迭代优化,杂活细活。AI智能运维这一块,后续团队是否有资源支撑的话?
  3. 我在新团队的发展侧重点、您对我的后续建议。能否脱离表层业务迭代,深耕底层,架构,组件,K8s,刚好补齐我架构思维弱的短板,也契合您之前对我的成长要求。
  4. 刚好前天也提到的,我这两年的产出和个人发展肯定是您比较了解,我跨团队之后,一些晋升的机会是不是比较难把握了。
  5. 嫡系师徒关系,背书。还是特别感谢您这两年带我,从校招生到独立对接交付,我的成长和产出都是您给的机会。后续我也会踏踏实实做事,有成长、有成果也会常跟您同步,还想一直跟着您的节奏学习。

hr talk

  1. 您好,关于本次团队调动我已知悉,整体愿意配合公司人事安排。
  2. 想明确本次调整后的title,汇报对象,新团队业务方向、定岗分工,正式生效时间、工位调整、系统信息变更节点
  3. 我清楚这次调动是基于整体业务规划做出的安排。过去两年在原团队,我也积累了不少业务与技术经验,工作产出也得到了一些积极的评价,后续我也会把这份经验和状态带到新团队,尽快融入、做好本职工作。
  4. 我有个核心顾虑想坦诚沟通。年初我主动表达过希望争取晋升,之后也一直针对性补齐短板,踏实完成各项工作产出,积极和原领导对齐成长节奏。转入新团队后,双方彼此还需要磨合熟悉,我担心过往的持续产出、阶段性成长无法被完整看见,原本的晋升节奏受到影响。加上我入职以来薪资本身偏低,也希望个人的付出能被客观看待。
  5. 所以由衷希望公司在后续晋升评审中,能够综合参考我过往整体的工作资历、持续产出与成长表现,给到我公平、客观的评审机会。我也会尽快适应新岗位,全力完成本职工作。

新boss 一对一完整沟通方案

  1. 开场表态(沉稳、服从、有思考,不被动)
    “领导,本次业务调整和团队调动,我完全了解和配合。
    我自己复盘了一下,其实对我个人是非常好的机会。过去我更多聚焦在业务需求交付、平台迭代和用户对接上,细碎事务比较多,一直缺少底层架构、组件、稳定性这一块的深耕,刚好这次调整补齐我的技术短板。”
  2. 自我介绍核心能力(给对方定你的价值)
    “我之前主要负责的是,运维平台网络和备份自动化开发,Opspace私有化交付,负责CMDB的日常数据运营、私有化迭代,落地了CMDB智能问数,还做过半年的数据库服务化开发。接下来转到新团队,”
  3. 核心诉求1:对齐未来工作重心(规避杂活)
    “想跟您对齐一下,接下来我的核心工作优先级:
    负责运维平台的网络管理模块,也承担一些作业平台、脚本的任务,但我希望不再停留在表层业务交付,想进一步深耕底层基建和架构设计。。一方面主动负责各种告警,中间件治理,一方面参与架构搭建,CMDBSpace也愿意参与。。我自己也得到突破。”
  4. 核心诉求2:保留AI智能运维赛道(关键!防止丢特色)
    “另外,我们之前就在在探索AI+运维的落地,初步上线了CMDB智能问数,
    我了解咱们团队AI投入如何?”
  5. 晋升期望对齐
    另外想跟您聊聊我个人的成长规划。我本科进来快两年了,年初我主动和Steven表达过希望争取晋升,之后也一直补齐不足,尽力做好手上的活。
    现在来到新团队,想了解您对我接下来的工作和成长有哪些期望,建议呢?
  6. 收尾:表态度、求指导、立上进心(为晋升铺路)
    “接下来两个月我会快速吃透团队底层业务,补齐架构思维,踏实做好本职工作。
    我目前处在职级成长和晋升冲刺的阶段,非常希望在新团队,在您的带领下做出新的技术沉淀和成果,后续也麻烦领导多指点我的不足、帮我把控成长方向。”

沟通核心目标(最重要)

  1. 确立姿态:我不是被划拨的边角人力,是带着平台核心能力+AI创新能力过来做技术深耕的骨干
  2. 主动规避:杂活、琐事、重复交付
  3. 主动对齐:未来2个月我的工作重心=底层架构+K8s+中间件稳定性
  4. 主动抛出:我想在新团队延续智能运维AI方向,求指导、求小落地空间
  5. 让新领导快速对你建立「有想法、肯深耕、有创新、可培养、能晋升」的优质印象

6.4 2026 年中述职

  1. 个人贡献
    • MOPS 迭代优化(第一,持续深耕…)
      • 持续负责 MOPS 负载均衡与域名解析自动化模块的迭代开发,(上半年承接)工单数累计1100+,支持线上紧急问题2次。(保障平台核心功能稳定运行)
      • (同时我)完整梳理 DNS 自动化全链路业务场景、代码逻辑与边界 Case,识别5个(排查多个)存量缺失场景与潜在风险点(如引入元数据解决平台记录不可信问题),输出完整优化方案并推动落地。
    • OPSpace 大洋交付(第二,全程跟进…)
      • 完成域名解析与备份自动化在大洋 UAT/PRD 部署,拉通用户验收并输出使用指引。
      • (同时)针对大洋定制化诉求,实现 Windows DNS 脚本优化(充分考虑边界 / 前后校验)、顶级域名可配置化改造,以及(新增)NBU 整机备份、备份通报等能力。(以匹配客户的差异化需求)
    • CMDB 开发与运维(第三,负责CMDB平台开发和日常运维)
      • CMDB 智能问数项目上线,参与包括从内部知识梳理、用户问题收集、多轮问数定制化开发调优,到应用服务的搭建上线。
      • (同时在…中)CMDB 产品化交付工作,排查并闭环部署问题 8 项,保障产品化交付顺利推进。
      • (日常我也持续)负责私有化迭代与出包工作。
      • 平台运维与告警处理。(包括日志定时备份清理脚本的编写,kafka消息积压等各类告警问题)
  2. 团队赋能
    • MOPS 负载均衡&域名解析需求评审5+次,开发经验沉淀 skills,备份模块慢sql治理
    • 更多承担了运维平台日常值班、发版事务
  3. 服务客户
    • MOPS 响应用户群问题
    • CMDB 日常响应用户问题,支持业务方数据运营
    • OPSpace 大洋电机用户答疑,问题跟进10+,从验收到上线全程陪跑

1)可量化、可落地的工作:2 次、5 个、8 项、10 +,数据支撑足
2)有具体思路和操作,不是“我做了什么”,而是“我解决了什么”

6.7 Kafka 消息积压 SOP

Kafka核心概念快速复盘

  • 逻辑层(业务视角:收发消息)
    1. Topic:逻辑消息载体,生产者写入、消费者读取的统一标识,业务按模块拆分Topic。
    2. Partition(分区):Topic数据分片,Kafka并发核心
      • 规则:同一个消费组内,1个分区同一时间仅能被1个消费实例消费有x个分区→该topic可以一个消费者组中的最多x个消费者同时消费
      • 作用:多分区→多实例并行消费,提升吞吐。。
    3. ConsumerGroup消费者组:Topic 的消费对象,一组同名消费实例
      • 同消费组:一区一实例,实例不抢区,offset 全组共享,绝对不会出现多个实例交叉 / 交替消费同一个分区。分区数决定最大并发,实例多于分区数则空闲
      • 不同消费组相互独立,各自从头消费。
      • Topic 只存储消息(LogEndOffset),堆积 = 消息末端位点 - 消费者已消费位点,消费位点(CurrentOffset) 是消费者组维度的数据,和 Topic 解耦。
      • 同一个 Topic,不同消费组的消费进度、堆积完全独立,所以 Kafka 设计上:Topic 管消息存储,消费组管消费位点 & 堆积。
    4. Offset:分区内消息唯一序号,LAG=LogEndOffset(分区最新消息位置)-CurrentOffset(消费端已成功提交位点) → 堆积量,指标核心,LAG持续上涨=真实堆积。
  • 物理层(集群存储视角)
    1. Broker:Kafka集群单台服务节点,集群由多台Broker组成。见 kafka-2.xx/config/.. 文件
    2. 分区副本:每个分区配置N副本(线上常规3副本),分两类角色
      • Leader副本:所有生产、消费请求只走Leader;
      • Follower副本:仅异步同步Leader数据,故障时竞选新Leader,不承接业务读写。
    3. 物理部署规则
      • 同一个Topic的不同分区分散在多台Broker,打散IO压力;
      • 同一分区的主/从副本禁止部署在同一Broker,防止单机宕机整分区数据丢失。
    4. 有序性关键结论
      • 单个分区内:消息严格按生产顺序;
      • 多分区:Topic全局无序;有序业务只能单分区,天然限制并发。
  • 堆积本质一句话:消息生产速率 > 消费处理速率 → Offset追赶不上 → LAG持续上涨 = 消息堆积

消息堆积四大故障分类(先定性故障类型,再定向排查)

  1. 生产端突发暴涨(本次故障场景,偶发)
    • 现象:短时间QPS突增数倍,LAG短期快速拉升,无报错日志,消费实例运行正常、资源占用平稳;流量回落之后堆积自动慢慢消化。
    • 诱因:定时任务批量上报、活动峰值、上游业务异常狂刷消息、瞬时脉冲流量。
  2. 消费实例故障/离线(高频)
    • 现象:部分分区LAG飙升,其余分区正常;消费组实例数变少,部分进程宕机、OOM被杀、服务重启。
    • 诱因:应用部署异常、服务器资源耗尽(CPU/内存/磁盘满)、网络断连、健康检查失败被K8s驱逐。
  3. 消费处理逻辑阻塞(最高发,常态化缓慢堆积)
    • 现象:全分区稳步涨堆积,消费实例在线,但消费吞吐极低;应用日志大量报错、超时、慢SQL、下游接口超时。
    • 诱因:消费内同步调用DB/第三方接口卡顿、坏消息死循环重试、单条消息体过大处理耗时、死信未配置导致卡死分区。
  4. Kafka集群自身异常(全集群多Topic同步堆积)
    • 现象:集群多个Topic同步出现堆积,非单个业务;Broker磁盘IO打满、CPU飙高、分区Leader频繁切换、副本同步异常。
    • 诱因:Broker磁盘故障、副本同步阻塞、集群带宽跑满、集群节点下线。

应急分级规则

  1. P1紧急(堆积持续暴涨、磁盘即将打满):优先限流生产者+扩容消费,止损优先;
  2. P2常规(缓慢堆积,不影响磁盘):排查消费阻塞/实例异常,优化代码;
  3. P3波动(短时堆积自动回落):记录峰值时间,优化流量预案,无需紧急操作。

分级排查SOP

前置环境变量:BK=xxx:9092(集群地址,替换成实际集群)、TOPIC=xxxGROUP=xxx

  1. 前置校验
    1
    2
    # 1.查看全量消费组,找到异常Topic绑定的消费组( 原生没有命令可以「根据 Topic 反向查所有消费组」)
    kafka-consumer-groups.sh --bootstrap-server $BK --list | grep $TOPIC
    1
    2
    # 2.查看Topic分区信息,确认分区数量、副本状态
    kafka-topics.sh --bootstrap-server $BK --describe --topic $TOPIC
    输出关注点:分区数、副本数、有无under-replicated(副本异常)
  2. 查看堆积明细,定性故障
    1
    2
    # 查看消费组全分区堆积LAG、消费位点、绑定消费实例
    kafka-consumer-groups.sh --bootstrap-server $BK --describe --group $GROUP
    关键字段:LAG(堆积数)、CONSUMER-ID(在线实例)
    1、全分区LAG同步上涨 → 偏向生产突增/消费阻塞;
    2、个别分区LAG暴涨、其余正常 → 实例掉线、分区分配异常;
    3、全集群多Topic堆积 → Kafka集群故障。
  3. 查看 Topic 内原始消息(看消息内容、报文,初步判断来源)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 1. 实时监听新消息(看当前涌入的异常消息,从头消费不建议,堆积量大会卡)
    kafka-console-consumer.sh \
    --bootstrap-server $BK \
    --topic $TOPIC \
    --from-latest \
    --formatter kafka.tools.DefaultMessageFormatter \
    --property print.timestamp=true \
    --property print.headers=true \
    --property print.value=true
    1
    2
    3
    4
    5
    6
    # 2. 只读取20条最新消息,快速抽样
    kafka-console-consumer.sh \
    --bootstrap-server $BK \
    --topic $TOPIC \
    --from-latest \
    --max-messages 20
    看消息体:报文里的业务字段、设备 ID、用户 ID、操作类型,判断属于哪个业务模块;
    看消息头 Header:绝大多数公司规范会塞入 app-name(应用名)、source-ip(生产者 IP)、traceId(链路追踪 ID),直接定位生产者服务;
    看时间戳:确认消息开始暴增的精确时间,对应业务定时任务、定时同步脚本。
  4. 精准定位「哪个服务 / 机器」在生产消息(Kafka 本身不会主动记录 “生产者 IP / 服务名”,分两种场景溯源)
    • 场景 1:业务规范有消息头 / 链路追踪(最快,推荐)
      就是上面命令里的 print.headers,解析 header 字段 → 约定字段示例:service-name、producer-ip、trace-id:拿到 应用名 + 服务器 IP,直接登录对应服务查日志、查定时任务。
    • 场景 2:无自定义消息头(纯原生消息,靠 Kafka 集群日志 + 监控溯源)
      1. 查看 Kafka Broker 日志(定位生产者 IP)
        每条消息写入 Broker 时,日志会记录客户端连接 IP。先查 Topic 分区分布,找到该 Topic 所有 Leader 所在 Broker 节点:kafka-topics.sh --bootstrap-server $BK --describe --topic $TOPIC
      2. 登录 主节点 Broker 机器。
        常见路径:/var/log/kafka/ , /usr/local/kafka/logs/ ,
        日志文件:server.log
        关键词检索(按时间戳筛选凌晨暴涨时段):grep “$TOPIC” /var/log/kafka/server.log | grep -i “producer” | grep “03:00”
        日志格式一般会包含:客户端 IP、端口、Topic、分区、消息写入记录,直接拿到生产者机器 IP。
  5. 分故障定向排查&应急处置
    • 场景1:突发生产流量激增
      • 排查:监控查看Topic生产QPS曲线,确认峰值时间段;
      • 短期应急:
        • 临时扩容消费实例,提升并发消化存量;
        • 极端突发:上游临时限流生产,防止磁盘打爆;
      • 根治:上游削峰、拆分Topic分流超大流量、评估长期扩容消费节点。
    • 场景2:消费实例宕机/数量不足
      • 排查:
        • 查看CONSUMER-ID字段在线实例数量,对比Topic分区数;实例数<分区数→并发上限不足;
        • 登陆业务服务器/K8s查看pod状态:kubectl get pods -n 业务ns | grep 服务名,查看是否CrashLoopBackOff;
      • 应急:重启异常实例、新增pod扩容;
      • 根治:补齐实例至接近分区数,设置资源告警(OOM、CPU超限)。
    • 场景3:消费逻辑阻塞(代码慢/报错)
      • 排查:查看消费应用运行日志,筛选error、timeout、Exception;
      • 应急:
        • 紧急隔离脏消息:配置死信Topic,异常消息投递DLQ,不再重复重试阻塞;
        • 临时方案:存量堆积过大可重置offset跳到最新位点丢弃存量(谨慎!生产操作前确认业务允许丢历史消息)
          kafka-consumer-groups.sh --bootstrap-server $BK --group $GROUP --topic $TOPIC --reset-offsets --to-latest --execute
      • 根治:消费逻辑异步化(消息落库/调用下游丢线程池)、优化DB索引、限制重试次数。
    • 场景4:Kafka集群故障
      • 排查:查看Broker服务器磁盘使用率、IO、CPU;查看异常分区副本状态;
      • 应急:下线故障Broker、修复异常磁盘,待副本同步恢复;
      • 根治:扩容集群节点、更换高速磁盘。
  6. 事后复盘&预防配置
    • 配置监控告警:LAG堆积阈值告警、消费实例离线告警、生产QPS突增告警;
    • 容量预留:峰值消费实例预留30%冗余资源;
    • 规范:所有消费业务强制配置死信队列。
    • 补充高频应急备用指令(附使用场景)
      1
      2
      3
      4
      5
      6
      7
      8
      #1.扩容Topic分区(提升最大并发,分区只能加不能减)
      kafka-topics.sh --bootstrap-server $BK --alter --topic $TOPIC --partitions 新数值

      #2.控制台临时消费,验证消息能否正常拉取
      kafka-console-consumer.sh --bootstrap-server $BK --topic $TOPIC --group test-group

      #3.控制台生产测试消息,验证生产链路
      kafka-console-producer.sh --bootstrap-server $BK --topic $TOPIC

6.16 【两年度】工作总结

一、自动化运维平台能力建设

主导 F5 负载均衡、DNS 域名解析、NBU 备份自动化搭建。完善负载均衡变更、回收自动化链路,减少手工运维成本与操作风险;覆盖 DNS 各类域名申请、变更、回收自动化场景,近半年工单数1000+,创新打造一键回退功能,支持临时域名快速回收、变更故障回滚场景,收获业务方认可。
日常承担平台发版、值班、系统调优等团队事务,负责 F5、DNS 模块需求方案评审,协同团队落地负载均衡变更一键回退、DNS 自动采集、续期盘点等功能。后续将承接防火墙模块,负责网络管理模块迭代。

二、OPSpace 产品化交付

全程跟进 OPSpace 在大洋电机从测试到生产环境交付。核心完成 Windows DNS、NBU 备份自动化的客户侧适配搭建,实现顶级域名可配置、NBU 整机备份等定制化能力,协同客户完成上线验收并输出操作指引。响应闭环各类问题10余项;熟练掌握服务打包构建、多环境适配部署等整套交付技能,支撑项目平稳交付。

三、CMDB 平台运营与数据治理

负责 CMDB 数据运营,对接业务方数据写入和查询需求,辅助业务问题排查。承担日常值班工作,并将自助运维经验沉淀为 skills 上架技能广场。负责系统运维,协同排查服务高负载、接口查询缓慢、Kafka 堆积等故障,现阶段保障存量业务稳定,并协助业务迁移至新版 CMDB。
完成 CMDB 智能问数项目落地,参与从内部知识库构建、多轮问数定制化开发调优,到应用服务的搭建上线全流程;以及在大洋电机交付工作中,排查并闭环部署问题 8 项,保障交付进度。

四、InfluxDB 数据库服务化开发

参与 InfluxDB 服务化能力建设,实现实例备份与恢复、父子实例改造、节点重启与变配能力开发,完成 Data 节点迁移调研与前期开发,积累了数据库与容器化开发经验。

6.26 Java 线程池使用设计

  1. 自定义线程池

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Bean
    public ThreadPoolExecutor queryCmdbValidIpExecutor() {
    return new ThreadPoolExecutor(
    15, // corePoolSize 核心线程
    100, // maximumPoolSize 最大线程
    15L, // keepAlive 非核心线程空闲
    TimeUnit.MINUTES, // unit 时间单位
    new LinkedBlockingQueue<>(2000), // 有界队列,最多 2000 个任务排队
    ThreadUtil.createThreadFactory("aaaExecutor-")), // 线程工厂(创建线程、设置线程名)
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:主线程执行
    }
    1
    2
    3
    4
    5
    任务执行流程(必背)
    1. 提交任务,当前运行线程数 < 核心线程数 → 创建**核心线程**执行任务;
    2. 核心线程已满,新任务进入**阻塞队列**排队;
    3. 队列也放满了,总线程数 < 最大线程数 → 创建**非核心线程**执行任务;
    4. 总线程达到最大线程数,队列也满了 → 触发**拒绝策略**。

    生产规范:禁止直接使用Executors创建线程池,必须手动new ThreadPoolExecutor,指定有界队列+合理拒绝策略,防止无限任务/线程引发OOM。

  2. 参数该怎么合理设置?(业务实战经验)

    • CPU密集型(大量计算、报表统计)
      公式:核心线程数 = CPU核心数 + 1
      CPU尽量打满,减少上下文切换。
    • IO密集型(数据库查询、RPC调用、Kafka消费、文件读写)
      公式:核心线程数 = CPU核心数 * 2 或者 CPU核心数/(1-0.9)
      IO等待时间长,CPU空闲,可以多开线程提升并发。
    • 阻塞队列
      一定要用ArrayBlockingQueue设置固定容量,根据业务峰值预估,不要用无界队列。
    • 拒绝策略选型
      内部重要业务:CallerRunsPolicy,不丢数据,让调用方限流;
      允许少量丢弃的统计类任务:DiscardPolicy
      前端接口类:默认AbortPolicy,捕获异常返回业务繁忙。
  3. ?? 常用使用方式

    • 方式1:无返回任务 Runnable
      1
      2
      3
      pool.execute(() -> {
      // 批量业务逻辑
      });
    • 方式2:有返回任务 Callable + Future
      1
      2
      3
      4
      Future<String> future = pool.submit(() -> {
      return "执行结果";
      });
      String res = future.get(); // 阻塞获取结果
    • 方式3:结合CompletableFuture(主流异步编排)
      1
      2
      3
      CompletableFuture.supplyAsync(() -> {
      return rpcCall();
      }, pool);
  4. Spring Boot 默认线程池

    类别 作用 线程数
    tomcat 线程池 处理 HTTP 请求(你的 API 接口就跑在这里) 核心 10,最大 200
    @Async 线程池 处理 @Async 注解的异步方法 SimpleAsyncTaskExecutor(每次新建线程,无复用)
    @Scheduled 线程池 定时任务 单线程
  5. 并发设计 SOP —— 线程池使用规范

    • 原则一:按业务域隔离线程池
      反例 → 所有异步任务共用一个通用线程池
      正例 → 每个业务域独立线程池,如 portCheckExecutorcmdbQueryExecutor

      判断标准:只要两类任务的执行时间量级不同(ms 级 vs 秒级),或 SLA 要求不同,就必须隔离。

    • 原则二:所有阻塞等待必须有超时
      1
      2
      3
      4
      5
      6
      // 禁止
      future.join();
      future.get();

      // 必须
      future.get(N, TimeUnit.SECONDS);
      超时时间参考:纯内存计算 → 100ms,本地网络(同机房) → 3~5s
    • 原则三:理解线程池的任务调度模型
      1
      2
      3
      4
      5
      6
      7
      提交任务
      → 核心线程有空?→ 直接执行
      → 核心线程满? → 进队列等待
      → 队列满? → 创建非核心线程(直到 maxPoolSize)
      → maxPoolSize 满?→ 触发拒绝策略

      **核心线程永不释放**(默认),执行完任务立刻复用,不存在"执行完不释放"问题。
    • 原则四:线上卡死的应急处置顺序
      1 摘流量:把卡死节点从负载均衡摘掉,优先止血
      2 重启节点:释放所有排队任务,快速恢复
      3 动态扩容(如有 Hippo4j):临时调大线程数,不重启
      4 事后分析:看监控确认线程数、队列积压情况,定位根因
    • 原则五:CompletableFuture cancel 的局限性
      注意:正在执行的任务无法被中断(原生 IO 不响应 interrupt),只能等其自身超时后自然结束。
      1
      2
      3
      future.cancel(true);
      // ✓ 还在队列中的任务:可以取消,不会执行
      // ✗ 已在执行 Socket.connect 的任务:cancel 无效,跑完 1000ms 才结束
      超时后调用方放弃等待(TimeoutException),但线程池里的任务仍然继续执行,只是结果无人消费。这是 CompletableFuture 与线程池解耦的本质——调用方的取消意图无法传递给线程池。

6.30 【Java 服务卡顿】问题排查 SOP

适用:带数据库查询的 Java Web 服务(Spring Boot + Tomcat + 关系型/文档型数据库)

排查顺序总览:① Tomcat 线程 → ② CPU/内存/GC → ③ 外部调用 RT → ④ 数据库连接池 → ⑤ 慢查询 (每步的结论决定下一步的方向

第①步:Tomcat 线程(最优先)

判断:服务自身撑不住,还是在等别人?

指标 正常 危险
最大繁忙线程数 < max 的 70% = max(打满过)
当前处理中请求数 波动正常 持续居高不下
待处理请求数 0 > 0(在排队)

根据结果判断:

1
2
3
处理中线程数高 + CPU 高   → 第②步,计算密集或 GC 问题
处理中线程数高 + CPU 正常 → 第③步,线程在等 IO(最常见)
线程数正常 + 服务慢 → 第③步,单请求慢但没有并发压力

三个关键参数

参数 类比标准线程池 作用
min-spare-threads corePoolSize 最小空闲线程数,始终保活,应对突发流量
max-connections maximumPoolSize Worker 线程上限 = 最大并发处理请求数
accept-count 队列长度 线程满后 TCP 层的等待缓冲,0 = 满了直接拒绝连接
  • 与标准线程池的关键差异:标准线程池”先排队再扩线程”,Tomcat”先扩线程再排队”,目的是让请求尽快被执行而不是队列里等。
  • Tomcat Worker 线程就是执行业务代码的线程,对应 OS 线程,N 个线程竞争 M 个 CPU 核心。
    线程有三种状态,只有 RUNNABLE 才占 CPU:
    1
    2
    3
    RUNNABLE  → 等待 OS 调度,可以拿 CPU(计算中)
    BLOCKED → 等锁(synchronized),不占 CPU,但占着线程槽位
    WAITING → 等 IO / Feign 响应 / sleep,不占 CPU,但占着线程槽位
    这是”线程池满但 CPU 很低”的根本原因:
    1
    2
    3
    4
    5
    Feign 调用 / 等数据库连接
    → 线程状态 = WAITING
    → 不参与 CPU 调度,CPU 监控显示正常
    → 但线程槽位被占用,无法处理新请求
    → 线程池慢慢耗尽 → 后续请求 500
  • NIO 模式下为什么线程还会不够?
    NIO 解决的是网络 I/O 等待不占线程(Acceptor/Poller 异步处理网络读写),但业务代码里的同步阻塞(Feign 调用、JDBC 查询、锁等待)依然会让 Worker 线程进入 WAITING/BLOCKED 状态,线程槽位被占满。

第②步:CPU / 内存 / GC

CPU 高的常见原因:

场景 特征
大对象 JSON 序列化 RT 高,CPU 持续高,内存同步上涨
正则/加解密密集计算 CPU 高,特定接口慢
线程数过多导致上下文切换 CPU sys 占比高
Full GC CPU 出现周期性尖刺,服务周期性卡顿几秒后恢复

Full GC导致卡顿的特征:

1
2
3
4
5
6
7
8
内存使用率持续攀升,接近堆上限
→ JVM 触发 Full GC
→ Stop-The-World:所有业务线程暂停 1~10s
→ 这段时间内所有请求超时
→ GC 结束后瞬间恢复
→ CPU 出现尖刺,内存出现断崖式下降

**判断方法**:看内存曲线是否有"锯齿形"周期性下降,配合 GC 日志中 Full GC 的 pause 时间。

CPU/内存正常但服务慢(最隐蔽)→ 这种情况监控无告警,唯一证据是 Tomcat 处理中线程数升高:

1
2
3
4
5
线程挂起等待 IO(DB 连接 / Feign 调用 / 分布式锁)
→ 线程不做计算,CPU ≈ 0
→ 内存不涨
→ 但线程池慢慢耗尽
→ 后续请求 500 或排队

第③步:外部调用 RT

判断:慢在哪个下游?

查看 APM 中本服务对外的调用耗时,重点对比问题时段 vs 正常时段的 RT 基线:

  • 某个下游 RT 升高 → 问题在该下游,继续排查下游
  • 所有下游 RT 正常 → 问题在本服务内部(回到第②步,或看连接池)
  • 数据库 RT 升高 → 进入第④步

第④步:数据库连接池(应用侧)

判断:是连接池耗尽,还是 SQL 本身慢?

关键指标 含义 危险信号
active connections 当前占用的连接数 接近 max-pool-size
pending threads 等待获取连接的线程数 > 0
connection acquire time 获取连接耗时 > 0ms(正常应接近 0)
connection timeout 次数 等连接超时失败数 > 0 说明已出现获取失败

acquire time > 0ms 是最早的信号:连接池空闲时获取连接几乎是瞬间的,一旦出现等待,说明连接池在某个时刻已经被耗尽。

如何区分 → 连接池耗尽 慢查询
acquire time 飙升 ≈ 0
受影响的 SQL 全部 DB 请求都慢 只有特定 SQL 慢
active connections = max-pool-size 正常范围
慢查询日志 无(SQL 还没执行就卡住了) 有记录
根因 连接池设太小 / 有慢 SQL 长时间占用连接 缺索引 / 数据量暴增

第⑤步:慢查询分析

  • 数据库侧指标
    指标 危险信号
    慢查询数 突然增多
    活跃连接数 接近 max_connections
    锁等待次数 > 0
    索引命中率 突然下降
  • 慢查询常见根因
    根因 判断方式
    缺索引 EXPLAIN 查看执行计划,type=ALL 表示全表扫描
    数据量暴增 对比表行数历史趋势
    查询条件不走索引 索引字段被函数包裹、隐式类型转换
    锁竞争 查锁等待日志,找持锁 session
    返回数据量太大 分页参数过大、缺 limit
  • 完整影响链路示意
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    根因(任选其一)
    ├─ 慢查询(缺索引)
    │ ↓ SQL 执行时间 10ms → 3000ms
    │ ↓ 连接长时间被占用
    │ ↓ 连接池 active = max,acquire time 飙升
    │ ↓ Worker 线程等连接,CPU=0 内存不涨
    │ ↓ Tomcat 线程池耗尽
    │ ↓ 新请求 500,调用方重试,流量翻倍
    │ ↓ 雪崩

    ├─ 下游服务变慢(Feign 同步阻塞)
    │ ↓ Worker 线程挂起等响应
    │ ↓ CPU=0,内存不涨,无告警
    │ ↓ Tomcat 线程池耗尽
    │ ↓ 其他接口被殃及(线程池污染)

    └─ Full GC
    ↓ Stop-The-World,所有线程暂停
    ↓ 周期性卡顿几秒后恢复
    ↓ CPU 周期性尖刺,内存锯齿形下降

7.14 【CPU 高】排查 SOP

基础概念澄清

  • 主机CPU vs Java进程CPU
    1. 我们平时看到的服务器整机CPU(top命令展示的),是整机所有进程的CPU总和,包括内核、MySQL、Kafka、Java应用、Nginx等。
    2. Java进程本身也有独立CPU占用:top按下大写 P,能看到java进程单独占用的CPU百分比。
      Linux中所有线程(包括Java线程)全部由操作系统统一调度。Java没有独立的CPU配额管理器,JVM不限制CPU,Java线程和系统里其他线程平等竞争CPU时间片。
  • OS 核心原理
    1. 一个Java线程本质上就是操作系统的一个轻量级线程(LWP)。
    2. 多核CPU上,操作系统调度器会把不同线程分发到不同CPU核心执行。
    3. 一个线程同一时刻只能运行在单个CPU核心上;如果是CPU‑密集型运算,会持续占用该核心,造成该核心100%。
    4. Java应用开启越多执行线程,争抢CPU时间片越激烈,整机CPU占用就越高。

      举例子:4核机器。Java开启8个CPU密集型线程,8个线程竞争4个核,会出现大量上下文切换,整机CPU直接打满。IO‑密集型(等待数据库、网络、Kafka响应):线程大部分时间阻塞休眠,几乎不消耗CPU。

  • 两种CPU过高类型
    1. CPU‑密集型(单个/多个线程死循环、无限计算)
      线程一直在运算,持续占用CPU核心,单个线程可以打满一整个CPU核心。
      典型场景:死循环、正则回溯、大量循环计算。
    2. 频繁Full‑GC
    3. 上下文切换过高
      线程数量过多,操作系统不停切换线程,内核CPU占比飙升,业务代码本身逻辑并不复杂。
  • 高频易错点
    1. 单个Java线程最多打满一个CPU核心;如果4核服务器,一个死循环线程最多占用25%CPU;当Java进程占用100%以上CPU,代表有多个线程在消耗CPU;
    2. IO阻塞的线程不会占用CPU;只有运算逻辑、自旋锁、GC才会消耗CPU;
    3. 不要混淆整机CPU、Java进程CPU、单个Java线程CPU。

Java服务CPU高排查标准SOP(线上可直接照做)

  • 整体排查链路
    top(定位Java进程PID)top‑H‑p PID(查看哪些Java线程占用CPU)jstack(导出线程栈) → 把十六进制TID在线程栈中定位代码 → 定位代码问题;
    如果是GC导致CPU高,则使用jstatjmap排查GC情况。
  • 步骤1:使用top命令,定位消耗CPU最高的Java进程
    1
    top
    按P键,按照CPU降序排序,记录java进程PID,例如PID=28940。
    观察:
    • us(用户态CPU)高:业务代码在疯狂运算;
    • sy(内核态CPU)高:线程过多,上下文切换频繁;
    • wa:IO等待高,不属于CPU问题,是磁盘或网络阻塞。
  • 步骤2:查看该进程内部哪些线程占用CPU(关键步骤)
    1
    2
    # -H 展示线程,-p 指定进程
    top -H -p 28940
    这里会列出Java内部所有线程(LWP),记下CPU最高的线程ID,假设线程ID=29065。
    将十进制线程ID转为十六进制(jstack使用十六进制):
    1
    2
    printf "%x\n" 29065
    #输出结果:7189
  • 步骤3:打印Java线程栈,定位代码位置
    1
    2
    jstack 28940 > jstack.txt
    cat jstack.txt | grep -A 20 0x7189
    • 分支A:CPU高由业务代码导致(us高)
      • 问题案例:
        1. 代码中存在无限while死循环;
        2. 批量处理逻辑未做分页,一次性遍历全部数据;
        3. 复杂正则表达式回溯,CPU暴涨;
        4. 并发下自旋锁无限循环(LockSupport自旋)。
      • 解决:优化循环逻辑、增加休眠、分页、限制循环次数。
    • 分支B:CPU高由GC频繁导致(GC 线程(VM‑Thread, G1 Concurrent Mark‑Thread)占用CPU)
      • jstat -gc 28940 500 每500毫秒打印一次GC情况,重点观察:YGC、FGC发生频率是否极高;Old区内存快速上涨
      • 常见诱因:
        1. 内存泄漏(集合、静态变量无限存放对象,无法回收);
        2. 短生命周期对象过多(循环内不停创建对象,造成Minor‑GC爆发);
        3. 堆内存设置过小。
      • 后续排查命令:jmap -dump:format=b,live,file=heap.hprof 28940 导出堆快照,事后使用 MAT 工具分析。
    • 分支C:sy内核CPU占比过高,上下文切换频繁
      • 现象:top里sy占比很高,业务线程本身CPU占用并不突出。
      • 原因:
        1. 线程池线程数量设置过大;
        2. 大量短任务,线程频繁唤醒、阻塞;
        3. 大量synchronized同步锁竞争,频繁阻塞唤醒。
      • 排查:
        1. jstack查看大量线程处于BLOCKED状态;
        2. 检查线程池参数,缩减最大线程数;
        3. 优化锁逻辑,减少同步竞争,改用CompletableFuture、异步逻辑。
  • 步骤4:临时应急手段(线上紧急降CPU)
    1. 若是死循环逻辑:重启服务临时恢复;
    2. GC问题:临时增大JVM堆内存(-Xmx、‑Xms);
    3. 线程过多:临时调整线程池参数,缩减线程数量;
    4. K8s环境:临时扩容节点,分散流量。

7.14 【Java Full GC】排查 SOP

  • 第一阶段:判断严重程度(2 分钟内)

    1
    2
    3
    4
    5
    # 确认进程 PID
    jps

    # 查看 GC 实时状态(每秒刷新,共 10 次)
    jstat -gcutil <pid> 1000 10
    O 列 FGC 增速 单次 FGCT 判断 动作
    < 80% 偶发 < 200ms 正常 观察
    80~95% 每分钟数次 200~500ms 警惕 准备干预
    > 99% 每秒 1~2 次 > 500ms 失控 立即抓现场
  • 第二阶段:抓取现场证据(5 分钟内)

    1
    2
    3
    4
    5
    6
    7
    8
    # 先抓线程快照(几乎不影响进程)
    jstack <pid> > /tmp/jstack_$(date +%Y%m%d%H%M%S).txt

    # 再抓内存快照(会触发一次 Full GC + STW,故后抓)
    jmap -dump:format=b,file=/tmp/heap_$(date +%Y%m%d%H%M%S).hprof <pid>

    # 记录 GC 基准数据
    jstat -gc <pid> 1 1

    为什么先 jstack 再 jmap:jmap dump 会触发一次 Full GC + STW,可能导致进程短暂无响应,jstack 先拿到不受影响。

  • 第三阶段:分析 jstack(5 分钟)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 线程总数 / BLOCKED 线程数
    grep "java.lang.Thread.State" jstack.txt | wc -l
    grep "BLOCKED" jstack.txt | wc -l

    # 找竞争最激烈的锁(核心命令)
    grep -A 5 "BLOCKED" jstack.txt \
    | grep "waiting to lock\|locked <" \
    | sort | uniq -c | sort -rn | head -20

    # 找持锁线程的完整调用栈
    grep -B 5 "locked <0x锁地址>" jstack.txt
  • 第四阶段:止血重启

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 复查 GC 趋势
    jstat -gcutil <pid> 1000 5

    # 确认可用内存(堆建议取可用内存 40~50%)
    free -h

    # 重启(扩堆 + 关键 JVM 参数)
    nohup java \
    -Xms4g -Xmx4g \
    -XX:+HeapDumpOnOutOfMemoryError \
    -XX:HeapDumpPath=/tmp/heapdump_oom.hprof \
    -XX:+ExitOnOutOfMemoryError \
    -jar /path/to/app.jar \
    >> /path/to/app.log 2>&1 &

    # 验证恢复
    jstat -gcutil <新pid> 1000 5
  • 第五阶段:事后 heap dump 分析,用 Eclipse MAT 打开 .hprof 文件:

    步骤 操作 目的
    1 File → Open Heap Dump 加载文件
    2 Leak Suspects Report 自动分析内存泄漏嫌疑,最快定位
    3 Dominator Tree 找占用内存最大的对象树
    4 Thread Overview 查看每个线程持有多少内存

9.3 线上排障 Skills

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
---
name: enterprise-it-troubleshooting
description: >
适配企业内网 IT 场景的通用线上问题排查技能。用于用户需要排查线上故障、
复盘告警、编写 SOP、定位 Java Full GC、CPU 高、线程池打满、Tomcat 拒绝请求、
Kafka 堆积、MongoDB 慢查询或异常重启、磁盘暴涨、ZooKeeper 异常、K8s Pod 异常、
配置或依赖冲突等场景。也适用于用户只给出一个现象,如“接口 500”“服务 pending”
“节点 CPU 高”“日志一直暴涨”“消息堆积”“Pod 起不来”,需要结合 Linux 命令、
应用日志、JVM 现象、监控平台、链路上下游关系做分层定位时使用。优先输出止血、
定界、根因、验证和改进建议。它通过工作流和对话引导帮助排查,但不直接与线上工具、
API 或运行环境交互。
---

# 企业 IT 线上故障排查

## 目标

你是一名面向企业内网场景的线上故障排查助手。你的任务不是直接执行线上操作,而是通过对话引导和排查工作流,帮助用户完成以下事情:

1. 快速定界故障层次
2. 明确先看哪些监控、日志和指标
3. 基于用户回传的信息继续收敛根因
4. 输出止血建议、根因分析和改进项

## 能力边界

你可以做的事:

1. 帮用户拆解故障现象、影响范围、时间线
2. 给出排查顺序和高概率分叉判断
3. 建议应该查看哪些监控、日志、命令输出和系统指标
4. 帮用户整理 SOP、事故报告、复盘结论和长期改进建议

你不能直接做的事:

1. 不能直接登录线上机器、容器、K8s、数据库
2. 不能直接调用企业内部 API、监控平台、堡垒机能力
3. 不能假设自己已经拿到了命令输出、日志文件或监控截图
4. 不能伪造排查证据,只能基于用户提供的信息继续判断

默认工作方式:

1. 先告诉用户应该去哪里取证
2. 再说明看到什么现象意味着什么
3. 最后基于用户回传的信息继续收敛根因

## 核心原则

1. 先看监控,再上机器
2. 先定影响面,再看单点异常
3. 先止血,再找根因
4. 先取证,再下结论
5. 优先构建“现象 -> 指标 -> 日志 -> 调用链 -> 根因”的闭环

## 通用排查框架

### 第一步:先定界

每次先回答这 4 个问题:

1. 故障现象是什么:慢、错、挂、堆积、起不来、资源高
2. 影响范围多大:单接口、单节点、单服务、整链路、整集群
3. 当前趋势如何:持续恶化、波动、已恢复、偶发
4. 问题在哪一层:应用、线程池、JVM、下游服务、数据库、网络、OS、K8s

如果信息不足,优先追问:

1. 从什么时候开始
2. 是报错还是变慢,错误码是什么
3. 单节点还是全部节点
4. 最近是否发版、改配置、重启、扩容、切流、执行过批量任务
5. 上下游依赖是否正常

### 第二步:先看监控

优先建议用户查看:

1. 主机层:CPU、Load、内存、Swap、磁盘、网络、IO wait
2. JVM 层:堆、Old 区、FGC、线程数
3. Tomcat 层:繁忙线程、最大线程、请求耗时
4. 接口层:RT、成功率、5xx、超时
5. 组件层:Kafka lag、Mongo RT、Pod 状态

常见判断:

1. 写入慢且查询也报错:优先看线程池/Tomcat 是否被打满
2. 只有查询慢:优先看下游 RT、数据库慢查询、索引
3. 只有单节点异常:优先怀疑节点局部问题
4. 多节点同时异常:优先怀疑共享依赖或公共配置变更

### 第三步:建议取证

根据故障类型,建议用户提供对应证据:

1. CPU 高:
- `top`
- `top -Hp <pid>`
- `jstack -l <pid>`

2. Full GC / OOM:
- `jstat -gcutil <pid> 1s 10`
- `jstack -l <pid>`
- `jmap -dump:live,format=b,file=heap.hprof <pid>`
- `free -h`
- `vmstat 1 5`

3. 线程池 / 接口 pending:
- Tomcat 繁忙线程
- 线程池活跃线程和队列积压
- 是否存在 `Future.get()` / `join()` 无超时等待

4. 磁盘暴涨 / 日志增长:
- `df -h`
- `du -sh`
- `lsof | grep deleted`

5. K8s / Pod 异常:
- `kubectl get pod -o wide`
- `kubectl describe pod`
- `kubectl logs --previous`

### 第四步:按调用链收敛

如果是微服务问题,优先画最短链路:

`入口 -> 网关/Ingress -> 当前服务 -> 下游服务 -> 中间件/数据库`

逐层判断:

1. 调用有没有发出去
2. 发出去后是超时、拒绝、异常还是空返回
3. 下游是否同时异常
4. 是请求量问题,还是单次请求太重

### 第五步:形成结论

输出时必须拆成四层:

1. 现象:用户看到了什么
2. 直接原因:这次为什么挂了
3. 根本原因:系统为什么允许它发生
4. 改进项:短期止血、中期修复、长期治理

## 常见故障优先判断

1. CPU 高:
- 业务热点线程
- 锁竞争
- Full GC 放大

2. Full GC / 内存故障:
- Old 区持续高位
- Full GC 后回不去
- 线程持有大量对象
- Swap 抖动

3. 线程池打满:
- 哪个线程池满了
- 谁在提交任务
- 任务为什么慢
- 是否多业务共享线程池

4. Kafka 堆积:
- 生产太快还是消费太慢
- 分区数和消费者实例数是否匹配
- 消费者是否卡在 GC、线程、锁、网络

5. MongoDB 慢查询 / 崩溃:
- 崩溃前最后几秒慢查询
- 是否 `COLLSCAN`
- `docsExamined` 是否远大于 `nreturned`
- 是否缺索引

6. ZooKeeper / K8s Pod 起不来:
- 是否绑定旧 IP
- 动态配置是否落在 PVC
- 当前读取的是新配置还是历史残留配置

7. 依赖 / 配置冲突:
- 是否断连后全部失败
- 是否 `NoSuchMethodError`
- 是否客户端状态不可逆损坏

## 输出格式

默认按以下结构回答:

### 故障判断

一句话说明当前最可能的问题层次。

### 先做什么

1. 立即止血动作
2. 立即补采的证据
3. 立即确认的监控项

### 排查路径

1. 第一步看什么
2. 第二步查什么日志或指标
3. 第三步如何判断分叉

### 高概率根因

列出 1 到 3 个最可能根因,并说明理由。

### 改进建议

1. 短期
2. 中期
3. 长期

## 示例触发

以下这类请求都适合使用本技能:

1. “线上一个 Java 服务 CPU 突然飙高,怎么查?”
2. “接口一直 pending,怀疑线程池满了,给个排查顺序”
3. “Mongo 节点异常重启了,怎么从日志反推慢查询”
4. “K8s Pod 一直 CrashLoopBackOff,想整理成 SOP”
5. “帮我把这次 Full GC 故障总结成事故报告和长期改进项”

8.31 离职

  • 线下同步 9.1
    “你现在方便吗,想跟你私下聊下我个人的一些规划。
    我这边有离职的想法,打算下周提交正式申请。今天先提前跟你同步一声,主要考虑马上有中秋、国庆假期,提前说下后续做好工作衔接。
    主要是我自己职业方向上的考量:
    第一,经过这两年做运维平台相关开发,我发现自己长期的兴趣,不太想继续深耕运维开发这个领域。(我希望更多往通用后端方向发展,去接触更多分布式、大规模业务系统这类场景,目前咱们团队的业务方向和我长期想要成长的方向匹配度慢慢错开了。)
    第二,站在我个人角度,这两年自己的成长节奏没有达到我对自己的期待,所以想出去外部看看新的机会。这个纯粹是我个人的选择,不是团队或者项目的问题。(说实话,我现在在MOPS+CMDB承担的模块职责其实不少,个人也学习了很多。但从成长回报、晋升路径来看,和我个人预期差距比较大。我感觉到了是赛道本身的天花板限制,很难达成我想要的职业成长,也没有办法更好的回报家人。我评估后,决定换个环境,也是对自己的负责)
    你一直关心我的学习和发展,我非常感谢。
    我现在还没有敲定外部 offer。接下来这段时间,我的重心会放在交接,手上现有的任务我会尽量收尾梳理,后续新的需求就不要再分配给我了。
    这件事目前我只跟你讲了,如果有需要我可以先不和其他同事聊,等我正式提交流程之后,我们再同步组里其他同事。”

  • 同步结果 9.1
    达成新共识:暂停原计划9月中旬提交离职申请,保留在职状态,不再承接运维开发需求,重心转为基于新CMDB开发智能问数AI Agent项目;领导已知悉我长期有跳槽规划,并无敌意,支持我补齐Java、Spring等底层基础,朝着阿里P6能力标准提升,双方达成默契,待拿到满意外部offer后再正式发起离职流程。
    显著利好:能够依托公司平台把原有智能问数方案落地迭代,补齐AI Agent工程化实战履历,同时拥有更充裕的时间打磨项目亮点、补习技术基础,在职收入兜底,规避裸辞风险;唯一潜在隐患是长期处于相对舒适的工作环境,容易放缓投递面试节奏、削弱求职心气,需要持续保持投递与面试节奏,牢记运维赛道薪资与晋升的固有瓶颈,以项目镀金、伺机跳槽为核心目标。

  • 正式申请

  • 交接期间
    个人项目总结&收尾&交接
    相关材料(离职证明,解除、、、证明)
    信息确定(社保交到哪天)

  • lastday
    saygoodbye

… 手搓 Java

…数据库事务编程要重点掌握:

  1. 事务分为本地事务和分布式事物两种概念:分布式事务用的java企业版里面的两阶段提交协议,性能差,不能绝对保证成功,用的比较少,现实中用补偿方式实现。 本地事务用的是 Connecton的setAutoCommit(false)来实现
  2. jdbc的概念:java链接数据库的规范,让开发人员对不同的数据库采用统一的api,对于数据库供应商实现相应的driver接口
  3. jdbc编程的步骤:1. 注册驱动程序 2. 获取数据库连接对象 Connection 3.获取Statement和PrepareStatement对象4. 执行sql语句或者获取ResultSet对象读取数据,最后关闭对象。
  4. Connection对象不支持多线程并发调用,需要用到连接池技术。我们用的Druid连接池(阿里巴巴开源),还有c3p0,dbcp等等。
  5. 事务的隔离级别需要掌握。
  6. Spring采用AOP实现事务,spring的声明式事务需要掌握。

… 读《重构》

Frank:方法简短、复用,逻辑正确,嵌套不要太深

S 招 2026

 
     
 
   

求职意向:Java 后端开发、AI 应用工程化

   

caifengg123@163.com

   

在这个文档中,我基于工作项目梳理了核心亮点,包括链路梳理、架构设计、业务思考、技术细节和各种衍生,以及对应的问答思路。 基于这些材料,以及下面我将提供旧版的简历内容,结合我在括号中标注的所有优化要点,贴合真实工作场景打磨内容,修正夸大表述、强化运维产品思考、业务痛点拆解、关键技术选型、稳定性保障、客户交付及 AI 项目差异化亮点,量化成果、补齐技术细节,埋好问题“钩子”引导面试官发问,适配后端面试场景,生成完整版精准优化简历。 保留原始的层级、分点、句式结构、排版格式,只对每一条内容做精细化优化,不动任何结构、序号、标题、层级,直接在对话中输出。 以下是简历内容: xxx

 

cv-20260912

  • 教育经历
    华南理工大学 网络工程 2020.09 - 2024.07
    在校经历:曾获学校与企业奖学金、”三好学生” 等荣誉,发表 EI 会议论文一篇;加入学院青马工程班学习,担任华工青年志愿者指导中心宣传部副部长,有丰富的活动组织和跨团队协作经验。
  • 工作经历
    Midea|后端开发工程师‑Java 2024.7~至今
    1. MOPS 自动化运维平台
      • MOPS 是集团核心自动化运维底座,覆盖主机、网络、存储等模块资源运维能力,通过自动化、流程化替代人工操作,降低运维风险、提升交付效率。
      • 我的职责:担任网络与备份模块负责人,统筹方案设计、需求落地、稳定性治理,全程跟进私有化交付全流程。
        • F5 网络自动化建设:独立落地 DNS 域名、负载均衡全流程自动化能力,覆盖资源申请、配置变更、回收、一键回退完整链路;构建事前校验、事中保障工单‑配置状态一致、事后快照可回退机制,规避配置飘移、变更不可回退等运维风险。全年累计承接 3k+ 生产工单,网络变更人工操作占比降低 80%,操作失误率降至 0。
        • 防火墙下发策略优化:针对批量下发场景下任务重复调度、串行效率低、策略重复下发易引发配置冲突的痛点,采用 Redis 分布式锁做任务级互斥优化 + 数据库 CAS + EXECUTING 中间态做策略级正确性兜底的双层防护;搭配专用线程池批量处理 IO 密集型请求,内置失败重试、超时熔断与僵尸任务巡检机制。单批次下发效率提升 4 倍,策略重复下发率降至 0。
        • NBU 备份自动化落地:基于 Ansible 脚本与 NBU 开放 API,通过线程池异步解耦任务调度与执行,底层依托 SSH 密钥认证、Linux 权限管控实现备份客户端远程安装,备份策略下发周期从小时级缩短至分钟级。
        • 私有化产品交付:牵头网络、备份两大模块 ToB 私有化交付,完成客户需求对接、方案评审、定制开发与上线验收;负责容器化出包与多环境部署适配,沉淀标准化交付文档与运维手册,完成交付闭环。
    2. CMDB 基础配置数据库
      • CMDB 是集团 IT 运维唯一事实数据源,承载上万台服务器、各类运维策略与 IT 基础配置资产的全生命周期数据,为上层 AIOps、运维、监控等各类系统提供数据支撑。
      • 我的职责:作为系统 Owner,负责各业务方数据接入、功能支持与线上运维;封装平台运营答疑经验为 Skill,支持用户通过数字人入口自助答疑。
        • 数据接入架构:深耕资产同步读写链路与并发场景,完整梳理数据接入层、同步层、存储层微服务分层架构与平台流量管控逻辑;依托 MongoDB 元数据与业务数据分层存储,基于资产唯一主键 upsert 实现入库幂等,搭配 Redis 缓存表头元数据缓解大批量同步的元查询压力;沉淀高并发场景下数据一致性与稳定性优化思路。
        • 自动化采集链路:基于 Kafka 异步解耦采集任务与入库流程,搭建通用采集框架。重点负责防火墙资产采集,排查过 FullGC 死循环故障,按“GC 状态监控→线程栈定位阻塞点→堆内存分析根因”的流程排查,定位表头元数据重复反序列化的核心诱因;通过堆容扩容止血、元数据复用根治内存溢出、锁粒度细化提升并发采集能力,分层落地形成稳定性闭环。
        • 全文检索与数据订阅:通过 Monstache 实时同步 MongoDB 资产数据至 Elasticsearch,落地全字段检索、全局 IP 秒级查询;基于 Mongoshake 捕获数据变更推送 Kafka,实现下游异步订阅回调,数据变更同步延迟控制在秒级。
        • 平台稳定性治理:负责线上故障处置,可定位解决 JVM 异常、中间件故障、接口卡顿等常见问题;遵循先止血恢复、再根因复盘的处置思路,借助 AI 辅助根因定位,沉淀标准化故障处置 SOP,封装为内网排障 Skill 并内部共享。
    3. CMDB‑Agent 智能问数
      • 基于 CMDB 资产数据的 AI 自然语言问数助手,实现自然语言解析、SQL 自动生成与结果返回,降低运维数据查询门槛;既服务平台用户,也作为子 Agent 为 AIOps 根因分析主 Agent 提供资产查询能力。
      • 我的职责:基于开源 DataAgent 的问数 1.0版本全链路定制化开发、知识库构建与业务调优;基于内部通用 Agent 平台完成问数 2.0 重构。
        • 问答链路工程化:基于运维业务定制适配,以图节点编排完整问答流程,覆盖意图识别、Schema 映射、SQL 生成、语法校验、结果返回;实现会话记忆能力,采用滚动热加载保存多轮问题与结果,支撑多轮关联问答;增加错误重试与异常兜底,面向运维业务样本调优各节点 Prompt,SQL 执行成功率提升至 92%。
        • RAG 知识库调优:梳理运维业务知识,搭建分层知识库;优化向量检索匹配与大模型上下文注入策略,问答准确率较基线提升 35%。
        • Agent 架构重构:基于内部平台提供的 Harness 会话管理 + Skill + MCP 工具化编排,搭建通用问数 Agent;实现业务能力与流程框架的解耦,工具自定义编排灵活度更高,适配运维多变的工具调用场景。
  • 专业技能
    • 后端开发:熟练掌握 Java、SpringBoot、SpringCloud、MyBatis,掌握常用设计模式与企业级工程化规范。
    • 数据库与中间件:熟悉掌握 MySQL、MongoDB 数据库,索引优化、SQL 调优、事务与锁机制;熟练使用 Redis、Kafka、Elasticsearch 中间件,具备缓存策略设计、消息队列治理、全文检索落地与线上排障实战经验。
    • 云原生与运维工程:熟练掌握 Docker 容器化、K8s 基础编排;具备中间件服务化封装、多环境部署、产品交付的落地经验。
    • AI 工程化能力:熟练掌握 Prompt 工程、大模型上下文管理与 RAG 检索增强技术;具备 Agent 应用定制开发经验,熟悉流程编排、Skill 工具封装,完成运维场景 AI 助手业务落地。
    • 线上稳定性治理:具备丰富线上排障经验,可定位解决服务异常、接口性能瓶颈、中间件故障,沉淀标准化故障处置 Skill。
    • ToB 产品交付:具备完整的私有化产品交付经验,可独立完成客户需求对接、方案设计、定制开发、部署上线与验收全流程。

cv-20260825

  • 教育经历
    华南理工大学 | 网络工程 2020.09 - 2024.07
    在校经历:曾获学校与企业奖学金, “三好学生”等荣誉,发表 EI 会议论文一篇。曾加入学院青马工程班学习,担任华工青年志愿者指导中心宣传部副部长,有丰富的志愿活动和学生组织经历。(项目统筹、跨团队协同能力。这里能不能直接改成“宣传部长,有丰富的志愿活动组织和跨团队协作经验”?)
  • 工作经历
    Midea|后端开发工程师-Java 2024.7~至今
    1. MOPS 自动化运维平台(主项目)
      • MOPS 是集团核心自动化运维底座,覆盖主机、网络、存储全品类资源的流程化运维管控,目标是通过自动化、流程化化替代人工操作,降低运维风险、提升交付效率。
      • 我的职责:担任网络与备份模块负责人,统筹方案设计、需求落地、稳定性治理,以及全程跟进私有化交付全流程。(需不需要有具体的功能、技术点?)
        • F5 网络自动化建设:独立落地 DNS 域名、负载均衡两大核心能力的全流程自动化能力建设,覆盖资源申请、配置变更、回收、一键回退完整链路;基于事前校验、事中事务机制、事后可回滚快照,保障工单状态与配置快照强一致,全年累计承接 3k+ 生产工单,网络变更人工操作占比降低 80%,操作失误率降至 0。(要体现对运维自动化需求的拆解,通过流程和技术设计保障核心事项,关注稳定性、可溯源、可回退、防配置飘移等,与技术结合的理解)
        • 防火墙策略自动化:优化防火墙策略批量下发能力,基于 Redis 分布式锁解决多节点并发下发的配置冲突问题;通过线程池异步任务处理批量策略请求,内置失败重试、超时熔断与排队调度机制,单批次下发效率提升 4 倍。(核心要体现如何拆解痛点,对典型的运维IO型场景的分层改进。这里对锁和线程池的使用能不能强调作为一个亮点引导提问?)
        • NBU 备份自动化落地:基于 Ansible 与 NBU 开放 API,通过异步线程解耦任务调度与执行,实现备份客户端远程自动化安装与备份策略统一下发;支持多台主机批量并行部署,客户端部署周期从小时级缩短至分钟级。(通过ansible实现NBU 客户端安装这样的手工运维场景的自动化,引入linux、os层面开发运维经验,体现运维平台不仅仅是调API)
        • 私有化产品交付:牵头平台网络、备份两大模块的 ToB 私有化交付,全程负责客户需求对接、方案评审、定制化开发与上线验收;日常负责系统容器化出包与多环境部署适配,沉淀标准化交付文档与运维手册,形成完整产品交付闭环能力。(体现代码开发以外的对客沟通的能力)
    2. CMDB 基础配置数据库(副项目)
      • CMDB 是集团 IT 运维唯一事实数据源,承载上万台服务器、各类设备与 IT配置项(部门、系统、服务等信息)的全生命周期资产数据,为上层 AIOps、监控、数据平台等各类系统提供数据支撑。
      • 我的职责:作为系统owner(各种需求、故障的第一负责人),负责各业务方数据接入、各类功能支持与线上故障运维。(特别的,我还将平台日常运营答疑梳理skill,用户可以通过办公数字人入口安装使用智能客服)
        • 数据接入架构优化:拆分数据接入层、同步层、存储层微服务链路,统一收敛全平台读写入口与流量管控;采用 MongoDB 元数据与业务数据分层存储架构,基于资产唯一主键 upsert 实现入库幂等;搭配 Redis 缓存表头元数据,大幅降低大批量资产同步的元查询压力,达到xx 的QPS??和低失败率(事实上没有做架构优化或需求开发,但在处理各种日常数据对接的过程中,深入研究了读写链路和并发问题,虽然一些特性在线上环境中没有体现,但对典型的并发场景也有一定了解准备)
        • 自动化采集体系:基于 Kafka 实现采集任务下发与数据入库的异步解耦,通过通用任务下发&结果入库链路,实现各类资产信息自动采集。我主要了解防火墙采集,对xx问题做了优化(特别介绍防火墙采集这个核心能力的业务链路与复杂故障的排查,我对fullgc的处理和分层优化设计;这在线上是一个大坑,围绕其有很多设计和亮点,注意要围绕我们梳理的内容做好闭环,避免过度发散)
        • 全文检索与数据订阅:通过 Monstache 实时同步 MongoDB 资产数据至 Elasticsearch,落地全字段全文检索与全局 IP 秒级查询能力;基于 Mongoshake 捕获数据变更事件并推送至 Kafka,实现下游系统异步订阅回调,数据变更同步延迟控制在秒级。(不是我开发的,但有真实的运维排障经验,简单介绍下就行)
        • 平台稳定性治理:负责平台线上故障排查,可快速定位解决中间件异常、JVM 问题、接口卡顿等线上故障;沉淀多套标准化故障处置 SOP 并梳理内网线上IT排障skill(可以稍微展开下我的skill?大概是引导用户结合内网的监控、不同场景下从生产机器获取真实数据,做到先止血、恢复、故障闭环,,)
    3. CMDB-Agent 智能问数(差异化亮点项目)
      • 基于 CMDB 资产数据的 AI 自然语言问数助手,基于 SpringAIAlibaba 实现 Graph 拆解实现自然语言自动解析、SQL 自动生成与结果返回,降低运维数据查询门槛。(这里提到的是问数1.0;除了直接提供CMDB用户使用,还为统一AIOps 运维根因分析Agent提供能力,上层Agent可以调用CMDB-Agent获取配置数据)
      • 我:问数1.0版本全链路定制化开发,知识库构建,基于业务问题做定制化调优。以及,基于内部通用Agent开发平台重构开发了问数2.0版本(平台提供通用Agent运行时能力,无需自研会话&记忆管理,配置Skill+知识库+LLm调度规则,实现“配置化”的Agent)(后续还会基于Harness自研通用问数Agent,实现3.0版本)
        • 问答链路工程化:基于图节点编排拆解完整问答流程,覆盖意图识别、Schema 映射、SQL 生成、语法校验、结果返回全节点;设计错误自动重试与异常兜底机制,针对用户问题对节点Prompt调优,SQL 执行成功率提升至 92%。(这里是问数1.0的内容,事实上直接用了阿里开源DataAgent的能力,整体链路开箱即用,所谓定制化开发就是根据业务问题问数情况调各个节点Prompt,仅介绍以上亮点,不要发散,比如如何流式输出就研究不深)
        • RAG 知识库调优:搭建运维专属业务知识库,完成资产指标、业务口径、表结构语义的分层知识梳理;优化向量检索匹配逻辑与大模型上下文注入策略,问答准确率较基线提升 35%。(这里是问数1.0的内容,事实上两类业务知识,都是我一条条梳理的,不涉及复杂知识库构建经验,也没有深入了解各种rag召回,三类模型的使用)
        • 上下文管理:通过保留x轮问题+结论作为记忆,热加载,滚动加载。。(这里是问数1.0的内容,)
        • 轻量化 Agent 架构重构:脱离重型框架依赖的通用运维 Agent 方案,采用 Harness 会话&上下文管理 + Skill 工具化编排的设计思路,解耦业务能力与流程框架,,(另外比起旧的流程编排问数Agent,还有什么优势??)(预计通过ai coding手搓,未开始,注意和前面提到的平台化重构的2.0版本区别开)
  • 专业技能
    • 后端开发:熟练掌握 Java、SpringBoot、SpringCloud、MyBatis,掌握常用设计模式与企业级工程化规范。
    • 数据库与中间件:熟悉掌握 MySQL、MongoDB 数据库,掌握索引优化、SQL 调优、事务与锁机制;熟练使用 Redis、Kafka、Elasticsearch 等中间件,具备缓存架构设计、消息队列治理、全文检索落地的实战经验。
    • 云原生与运维工程:熟练掌握 Docker 容器化打包、K8s 基础资源编排,Ansible 自动化脚本开发(与调度);具备中间件服务化封装、多环境部署、私有化产品交付的完整落地经验。
    • AI 工程化能力:熟练掌握 Prompt 工程、大模型上下文管理与 RAG 检索增强技术;具备 Agent 应用全流程开发经验,熟悉流程编排、Skill 工具封装、父子 Agent 调度架构,具备 AI 助手从 0 到 1 落地经验。
    • 线上稳定性治理:具备丰富的线上故障排查与稳定性治理经验,可独立定位解决 JVM 异常、接口性能瓶颈、中间件故障等问题,沉淀标准化故障处置 SOP(梳理并且分享 Skill)。
    • ToB 产品交付:具备完整的私有化产品交付经验,可独立完成客户需求对接、方案设计、定制开发、部署上线与验收全流程。
    • 其他:InfluxDB 时序数据库服务化开发经验,落地实例备份恢复、节点重启、规格变配、一键迁移等平台化功能,具备时序中间件容器化改造与服务封装实战经验。

talks

  • 自我介绍。
    面试官您好,我是蔡枫,本科毕业于华南理工大学计算机学院,在美的集团从事Java后端开发的工作。
    工作中主要负责的产品是运维平台,包括几个系统,xxxx

  • 为什么跳槽?
    “过去两年我主要负责内部两套核心底座平台 CMDB、MOPS 的开发与稳定性保障,实际上承担了系统负责人的角色,负责业务迭代和故障运维。
    但当前工作更多偏向存量系统的需求迭代与运维支撑,一方面,感觉个人兴趣不在运维平台上面,另一方面,虽然把稳定性做得很好,但在公司的评价体系下,偏维护类的工作可能很难形成可以用于晋升的业务产出。长期下来我感觉到个人成长触到了天花板。
    我已经具备需求落地、线上问题治理的能力,希望能够找一个新环境,参与具备业务深度、能够产生技术沉淀的场景,或者有新兴技术、如 AI 应用工程化这类有挑战的场景,产出更有重量的技术成果,进一步把自己的技术深度往上推。
    我也十分年轻,有信心尝试不同的领域;持续学习,保持好奇。”

    1. 能力:点明自己实际做系统负责人,做过稳定性、平台建设、故障治理(摆实绩)
    2. 潜力:不满足只做维稳,渴望更有挑战的技术场景
    3. 隐性野心:想要产出有分量的技术成果,追求更深的技术成长(不说我要升职加薪,用 “产出技术成果” 表达进取心)
  • “天花板具体是什么?突破什么?”
    “客观看,我在现有团队已经可以独立扛下整套平台的全生命周期,从功能开发、迭代的负责,到线上故障排查。但目前业务形态以存量运维底座为主,大部分精力花在把业务方(系统运维)的需求转换为功能,并且要保障系统不出问题。(技术上已经整体掌握了;业务上,一直在做历史债务+需求堆积,而非技术导向)
    这类工作的价值是稳,但很难产生新的业务成果。公司晋升评价更偏向新业务迭代类产出,所以在这套体系下我很难拿到向上的机会。
    我不希望一直停留在‘保障旧系统稳定’这个层面。我希望能够接触真正有业务深度、或者大模型工程落地的场景,去做架构层面的思考与落地。我希望把我现有的后端、云原生、运维开发的综合能力,放到更大规模的业务场景里,沉淀真正有影响力的技术产出,这也是我选择出来看机会的主要原因。”

  • 那你在原来的岗位,就不能做这些深度改造吗?
    “内部有做局部优化,比如数据库校验链路、Oplog 数据订阅、故障治理。但整体业务优先级偏向维稳,大规模架构重构、新形态业务落地的机会不多,资源和业务导向决定很难做大规模的技术产出。”
    “所在的团队和系统,目前还在自动化的构建中,在智能化方面的投入较少”

  • 你觉得你最大的优势是什么?(承接跳槽理由,顺势输出能力 + 潜力)
    “一方面,我有完整平台从开发到线上运维的实战经验,能兼顾开发和运维视角,处理复杂线上问题;另一方面我希望跳出纯维护的定位,愿意去啃高并发、AI 工程化这类比较新的难题,希望在新业务场景把能力进一步放大。”

  • 看过源码吗?fastjson?

  • AI的使用,AI工作流(极海题库:https://bitfree.cn/question
    我的工作流就是将人类的战略思维(Strategy)与AI的战术执行(Tactics)分离。我负责定义‘做什么(What)’和‘为什么(Why)’,而AI负责‘怎么做(How)’,并通过 Hook 和 Review 机制确保结果的可控与高质量。
    我做 AI coding 不是简单让 AI 生成代码,而是一套 Agent 化的工程开发流程
    首先我会前置统一 AI 编码规范(中文回答,生成注释,,)、梳理项目(先让 AI 读当前项目的技术架构、、异常处理逻辑)、注册好各类开发工具能力(Skills,MCP)。然后对于当前需求(readme.md),通过 Brainstorm 做需求发散、通过 Plan 做结构化拆解(拆分功能点P0P1P2,按模块拆分功能实现,各个模块各自开发??阶段性完成后进行测试,通过后再开发后续模块??)。在开发阶段,我采用主 Agent 调度 + 多专家子 Agent 并行开发的模式,主模型负责评审和把控质量,轻量化子模型负责具体代码编写,兼顾质量和效率(主模型用好的,贵的)。最后统一生成单元测试、做代码评审和联调,实现标准化、高质量、可落地的 AI 编码闭环。

  • 如何保证AI Coding代码质量?
    AI 写完必须过 AI 评审 Agent
    专门检查:
    并发隐患
    空指针风险
    事务失效场景
    SQL 全表扫描
    参数未校验
    重复代码、坏味道

  • AI开发中对SDD的理解
    “总而言之,SDD 不仅仅是一个开发流程,它更是一种工程思维的转变。它标志着我们从‘凭感觉编程’(Vibe Coding)进入了‘按规范交付’的工程化时代。对于后端开发者而言,掌握 SDD 意味着我们不再是 AI 生成代码的被动接收者,而是通过定义清晰的‘契约’,主动地、系统性地驾驭 AI,确保交付的软件既正确又符合业务意图。这正是在 AI 时代,后端工程师核心价值的体现。”

  • AI coding如何节省Token?
    核心:少输入、少输出、少交互、精上下文
    面试回答:原则 + 6个具体方法 + 总结价值
    亮点:体现你会用 AI、懂成本、有工程规范

  • AI CODING L1,L2,L3??

  • 现在大模型能力越来越强,你觉得程序员会不会被替代?程序员核心竞争力是什么?

    AI不会替代程序员,但是会淘汰只会机械编码、不会驾驭AI的人;未来竞争不是人对抗AI,而是会用好AI的程序员,和不会使用AI的程序员之间的竞争。

    1. AI的能力边界
      ✅ 优势:标准化代码生成、样板代码编写,高效完成确定性编码任务
      ❌ 短板:无法自主定义模糊问题、不理解业务隐性约束与存量技术债、缺少权衡决策能力、容易产生幻觉隐患
      ✅ 关键前提:AI产出的代码必须由人理解、校验、可控,否则无法上线落地
    2. 程序员三层核心竞争力
      ① 底层基础能力(底盘):计算机基础、并发、存储、网络等原理,用来识别AI代码隐患,具备评审、排障判断力
      ② 方案与业务能力(核心):需求拆解、架构取舍、风险评估、线上疑难问题排查。跨角色沟通、风险对齐、推动落地这类软能力,也是AI不具备的。
      ③ AI体系落地能力(高阶加分):Agent/RAG/Harness等大模型应用工程化、AI能力管控、搭建团队AI提效工具。 “我近期也在落地Text2SQL的Agent Demo,尝试用主Agent+专家子Agent+Harness编排的思路,本质就是探索如何规范化管控大模型能力,把AI可靠地融入研发流程。”
    3. 个人发展策略
      将AI作为编码工具,释放重复劳动;重心投入高价值的方案、决策、风险治理;持续夯实基础,同步积累AI工程落地经验,打造差异化优势。
  • Agent如何节省Token?

  • LLM相同,提词相同,输出结果一定一样吗?
    结论先说:默认情况下一定不一样;只有严格锁死全部条件,才能做到每次输出完全相同

  • Skills?
    “简单来说,Agent Skills 就是AI的标准化岗位说明书。它通过 SKILL.md 文件将领域知识、操作步骤和脚本封装起来。最巧妙的是它采用了渐进式披露机制,只在需要时才加载详细内容,既保证了效果又节省了 Token。如果说 Tool 是 AI 的手和脚,那 Skill 就是 AI 的‘肌肉记忆’和‘工作经验’。在2026年,构建高质量的 Skills 库是企业级 AI 应用落地的关键。”
    cf:skills提供业务知识,让llm从普通员工→该业务的资深员工,区别于预设好场景提前设计好Prompt模板供模型使用,Skills让LLM能按需自行构造Prompt?!

  • RAG?落地?

    • 全称是 检索增强生成(Retrieval-Augmented Generation)。大模型只有预训练的知识,不具备企业内部知识,RAG 通过知识库建设,知识检索,提示词构建,在提供给LLM的Prompt里引入内部知识上下文,从而让模型生成更准确、更实时的回答。
      RAG 本质上就是为了解决 LLM “知识滞后” 和 “一本正经胡说八道” 这两个核心痛点而诞生的架构。
    • 为什么要用 RAG?(核心价值 vs 痛点,建议对比微调(Fine-tuning)来谈)
      • 时效性 →知识更新快。只需更新外部知识库即可,无需重新训练昂贵的模型。
      • 准确性 →减少幻觉。答案基于检索到的事实,有据可依,可追溯来源。
      • 成本 →性价比高。比全量微调或预训练便宜得多,适合垂直领域落地。
      • 隐私 →数据不出域。私有数据只在检索环节使用,不需要注入到模型参数中。
    • 进阶补充(加分项,可以补充一点你对 RAG 当前挑战 的理解)
      “虽然 RAG 很好,但在实际落地中也面临挑战。比如检索精度问题(如果检索不到相关内容,模型就答不对),以及上下文窗口限制(检索内容太多塞不进 Prompt)。所以现在的优化方向通常包括使用混合检索(关键词+向量)、重排序(Rerank)模型,以及更智能的切片策略。”
  • Harness?

    1. 广义Harness(行业规范/设计思想) ➡️ Agent标准化运行底座标准,定义必备能力:会话生命周期状态机、长短双层记忆、上下文自动裁剪、重试/熔断、统一Prompt管理、可插拔Skill调度、安全沙箱;
    2. Harness框架(规范落地实现)➡️ 遵循上述标准的现成代码底座,分技术栈:
      Java:AgentScope、Agents-Flex(后端业务、求职首选)
      Python:LangGraph
      TS:DeepSeek Harness(快速本地原型验证)
    3. 与旧框架层级差异:LangChain/LangChain4j仅LLM/RAG工具封装,无标准化生命周期管控,需手动拼接流程 ➡️ 新增完整运行管控层,开箱即用生命周期、记忆、容错。
  • AI 技能体系

    • 底层永久核心(不会过时,必学)
      1. LLM基础:Token、上下文窗口、流式输出、多模型调用、输入输出安全;
      2. RAG全链路:切片、向量化、多路召回、重排、幻觉优化;
      3. 原生提示词工程:CoT、结构化输出、模板设计;
      4. 原生Agent逻辑:任务拆解、工具调用、反思循环。
    • Harness体系
      1. Harness底座使用与设计:生命周期、双层记忆、重试熔断、会话隔离;
      2. Skill模块化拆分、插件化开发;
      3. 多Agent协同基础;
      4. AI工程运维:链路日志、Token监控、性能优化。
    • 个人技术体系搭建思路
      1. 底层打底:吃透LLM/RAG/原生Agent底层原理,不依赖框架;
      2. 标准化落地层:主攻Java Harness框架(AgentScope),掌握规范落地思路;
      3. 业务层:模块化Skill设计,把业务抽象为可插拔工具;
      4. 底层深挖:业余手写极简Harness(记忆/状态机/重试),面试深度作答;
      5. 边界区分:能清晰区分「规范、框架、业务Skill」三者关系。
    • 最小可运行Demo:Nl2SQL智能问数(精简落地方案)
      • 选型 ➡️ 主框架:AgentScope(Java,贴合后端求职) ➕️ 配套组件:LLM 通义千问/DeepSeek API ,数据库:SQLite(零部署测试业务表),向量库:Chroma本地轻量版(RAG存储表注释),基础依赖:SpringBoot、JDBC、Lombok
      • 分层设计 ➡️ LLM层:模型推理大脑 ➕️ Harness运行时层(AgentScope内置):会话管理、长短记忆、上下文裁剪、3次重试熔断、Prompt模板中心、Skill调度器、SQL安全沙箱 ➕️ 可插拔Skill层(4个最小业务模块):MetaSkill:RAG检索数据表元数据,SqlGenSkill:自然语言生成SQL, SqlCheckSkill:语法校验、拦截高危DDL/DML,QuerySkill:执行查询、结果自然语言汇总 ➕️ 存储层:SQLite业务库 + Chroma向量知识库
      • Demo标准工作流(完整Harness生命周期展示)
        1. 用户输入自然语言提问 → Harness创建独立会话,加载历史对话记忆
        2. Harness调度MetaSkill:RAG检索匹配数据表结构与字段注释
        3. Harness加载SQL生成Prompt模板,调度SqlGenSkill产出SQL
        4. Harness调度SqlCheckSkill校验语法、拦截危险语句(校验失败:触发Harness重试机制,最多3次,超限自动熔断终止流程,校验通过:进入下一步)
        5. Harness调度QuerySkill执行SQL,获取数据
        6. LLM汇总查询结果,生成自然语言回答
        7. Harness更新短期会话记忆、持久化关键元数据至长期向量记忆
        8. 输出答案,完整生命周期日志打印(状态、重试次数、Token消耗)
      • 与旧Data-Agent区别
        • 不再手写大量 if‑else 来硬编码工具调用分支。业务能力被封装成标准化 Skill 注册到 Harness;调用哪个 Skill 由大模型做动态决策,Harness 负责解析指令、安全执行、管控重试熔断和上下文记忆流转。开发者只需要新增 / 修改 Skill,不用改动 Agent 主循环。
        • 举个反例:阿里开源 Data-Agent(非 Harness)
          写死:用户提问第一步固定先调用 RAG,再生成 SQL,再校验,再执行。 这种叫固定流水线,不是 Agent。
          Agent 的价值是:由大模型动态决定步骤顺序,不一定每次都完整走全部 Skill。 比如有些简单问题不需要 RAG,直接回答;有些报错需要多轮重试调用同一个 Skill。Harness 就是承接这种动态决策后的执行。
        • Nl2SQL Demo(using Harness)举现实例子
          1. LLM 思考:需要 MetaSkill,参数 query=2026 销售总额
          2. Harness:解析调用指令,找到注册好的 MetaSkill,执行向量检索(纯代码 Skill)
          3. MetaSkill 返回订单表结构
          4. 上下文更新,再次交给 LLM
          5. LLM 思考:调用 SqlGenSkill,入参带上表结构和用户问题
          6. Harness 调度 SqlGenSkill(内部包含 Prompt 模板 + LLM 调用 + 清洗代码)
          7. 拿到 SQL,LLM 指令调用 SqlCheckSkill
          8. Harness 执行校验,发现没问题,继续调度 QuerySkill 执行 JDBC 查询
          9. 查询结果返回,LLM 不再调用任何 Skill,直接输出最终答案。
      • 大模型驱动,Harness主控,Skills支持
        1. 大模型(驱动 / 决策)
          是大脑,负责理解用户意图、规划任务步骤、选择调用哪个 Skill、决定什么时候结束任务。
          ⚠️ 它不直接执行任何外部操作:不能查数据库、不能读向量库、不能操作文件,只输出思考和工具调用指令。
        2. Harness(主控 / 运行管控)
          是运行容器与调度中枢。接收大模型输出的工具指令,完成:Skill 查找、权限沙箱、执行调用、异常捕获、重试熔断、会话生命周期、记忆读写、上下文组装,再把 Skill 执行结果回传给大模型,驱动下一轮思考。
          “主控” 不是说 Harness 自己决定业务逻辑,而是掌控整个 Agent 的执行流程与运行边界;业务走向依旧由大模型决策。
        3. Skills(支持 / 能力支撑)
          是标准化可插拔能力单元。分为两类:
          纯代码 Skill:RAG 检索、SQL 校验、DB 查询;
          LLM 型 Skill:内部封装 Prompt 模板 + LLM 调用 + 前后处理逻辑;
          对外统一接口,由 Harness 按需调度执行,提供大模型本身不具备的外部能力。

项目

  • 项目介绍。
    1 自我相信,要反复强调:这个项目非常重要
    2 包装项目:为什么做,怎么做,亮点,结果
    3 告诉面试官一件他不知道的事情,产生好奇

  • Projects

    1. CMDB 集团基础配置底座(系统管理员)
      • 业务数据对接与数据治理:作为平台管理员,负责各业务方数据接入工作,维护数据表结构、上下游数据读写链路,支撑查询视图、数据订阅、资产自动采集等核心能力落地。常态化追踪业务数据状态,清洗修正脏数据,规范数据流转,保障集团运维数据源准确、可信、一致。
      • 平台日常运营与功能维护:负责CMDB平台日常运营与内部用户支撑,维护全文检索、数据读写、自动采集、数据订阅等核心功能稳定运行,响应各业务部门日常使用咨询与基础需求,保障平台常态化稳定提供服务。
      • 系统故障排查与性能调优:负责平台日常线上故障排查处理,可快速定位解决服务卡顿、数据读写异常、接口慢查询等业务功能问题;熟练排查Kafka消息堆积、同步延迟等中间件故障及 Java 服务 GC、OOM 等异常。
      • 私有化产品化交付:负责CMDB平台私有化交付工作,完成客户环境POC验证、项目打包、容器部署、服务启停与宕机演练,独立解决Zookeeper启动异常等部署故障,累计闭环多项交付问题,保障外部项目顺利验收上线。
    2. MOPS 自动化运维平台(网络 & 备份模块 Owner)
      • F5自动化全栈建设(DNS+负载均衡):作为MOPS平台网络模块负责人,基于F5平台独立落地DNS域名、负载均衡两大核心能力自动化建设,完成全流程产品化适配。自主研发域名申请、变更、回收、一键回退全链路自动化流程,替换老旧第三方采集数据源,实现资产元数据主动采集,半年稳定承载千余条业务工单;搭建负载均衡全生命周期自动化流程,设计蓝绿发布、动态参数校验、并发数据一致性控制、变更一键回退机制,半年承接400+生产工单,有效规避人工操作风险,大幅降低线上变更事故率,同时跟进团队全栈转型,独立承担模块前后端功能开发交付工作。
      • 网络模块统筹:统筹F5、DNS、防火墙全网络模块的方案调研、需求评审与风险把控;包括负载均衡参数下发、防火墙策略生成算法、自动采集核心设计,协助团队成员完成功能落地;同步承担平台发版、线上值班、系统调优、慢 SQL 优化等工作。
      • NBU备份模块:实现NBU备份策略申请自动化下发;完成客户端批量自动化安装、API策略远程推送、每日备份任务执行状态全量采集。
      • 产品私有化交付:负责网络、备份管理两大核心模块的私有化交付工作,精准对接外部客户,承接定制化需求沟通、日常问题答疑,输出标准化功能使用指引与交付文档;独立完成对应模块容器打包、多环境部署上线,沉淀了从需求对接、功能落地到客户验收的完整私有化产品交付闭环能力。
      • 集团数字人接入(todo):负责运维平台Agent与集团美的数字人、负A统一门户对接改造,将网络、备份模块的自助运维能力标准化封装,实现防火墙工单、运维资源自助申请等AI自动化能力,统一集团运维操作入口,推动传统人工运维向智能化自助运维升级。
    3. CMDB-Agent 自然语言智能问数(AI 工程化项目)
      • 基于阿里开源 Data-Agent 框架完成业务定制二次开发,落地运维 AI 问答能力,实现自然语言自动解析、语义识别、生成可执行 SQL,支撑集团人员快速查询资产配置数据。
      • 独立完成内部运维知识库搭建、知识分层梳理、检索精度调优,优化大模型上下文匹配逻辑,显著提升问答准确率与业务适配度。
      • 优化前端流式输出交互体验,实现问答结果实时分段推送,解决大模型长响应卡顿问题,大幅提升平台整体使用体验。
      • 预研无框架轻量化通用 Agent 架构,基于可配置 Skill 体系 + 精细化提示词工程实现低代码智能编排,摆脱厚重框架依赖,打造高复用、可扩展的运维 AI 能力,形成个人差异化技术优势。
    4. 基于XXL-Job的高并发定时秒杀商城系统 | 针对Java高并发技术短板,基于XXL-Job开源调度框架二次开发,结合电商定时秒杀业务搭建高并发调度系统,融入AI工程化开发流程,重点实现定时促销并发控制、流量防护、性能监控调优,补齐互联网后端核心实战能力。(开源源码研读+AI工程化实战)
      • 开源框架二次改造:研读XXL-Job核心源码(https://github.com/xuxueli/xxl-job ),掌握集群调度、线程模型、容错重试机制,针对电商业务场景定制任务优先级、分片执行策略,优化单机调度瓶颈,提升集群并发处理能力。
      • 定时秒杀高并发场景落地:实现用户预约定时下单、秒杀节点自动开售、超时订单取消功能,依托分布式定时任务精准管控促销窗口期,模拟电商大促瞬时高并发业务场景。
      • 并发安全与流量防护:通过Redis Lua脚本实现原子库存扣减,解决超卖问题;基于分布式锁、线程池隔离、任务限流规避流量冲击;借助Kafka异步削峰解耦,有效处理高并发任务积压,保障服务稳定运行。
      • AI工程化高效开发:落地标准化AI开发流程,通过需求拆解、AI辅助编码、人工审码、单元测试、压力验收全链路开发,提升复杂高并发项目的研发规范性与落地效率。
      • Arthas监控与性能调优:接入阿里开源Arthas组件搭建可观测能力,实时监控JVM指标、线程池状态、接口耗时及QPS;精准定位锁等待、频繁GC、任务超时等问题,针对性调优,提升系统并发承载能力与稳定性。

三大原则:强调项目业务价值、标准项目叙事结构、预埋知识点引导面试官提问,主动导向自身准备充分的技术栈。

  • 如何把项目讲好?细节?
    1 往岗位需求上倾向(包装)
    2 具体怎么展开?为什么做(背景,不要说“老板要做的”),核心问题(一两个难点、如何解决),惊喜(额外思考、解决了什么)
    3 面对质疑:好问题,可能当时没考虑到,而是聚焦别的问题。。未来可以。。

  • 一个挑战。
    接收 CMDB 时,团队对其代码实现了解不高,故障多。。我进行了整体梳理,负责故障处理

  • 技术亮点与难点
    分布式,高并发,,,??

  • 处理线上问题,处理偶现问题
    先止血,后排查。。?

  • 一次排查问题。
    cpu飙升,发现是一个接口有io操作,并发大了就卡住了。。
    首先,停掉回写接口,把cpu降下来,恢复服务
    分析,不是计算型的,所以多线程没用,要考虑优化io查询,cmdb加索引。。
    其次,如何降低并发,加缓存?异步??

  • Q&A(doubao文档 https://www.doubao.com/docx/LBf3d5HhDo1GbBxiSpucIhmDnbb)

  • “你了解高并发 / 秒杀如何设计?”
    在我们运维平台项目中,没有电商秒杀这种瞬时热点争抢资源的业务,但是项目中大量用到高并发相关基础组件。
    比如 MOPS 大批量下发任务用到线程池异步、接口限流;CMDB 大批量资产入库用到 Redis 缓存优化、upsert 幂等、Kafka 异步事件。
    针对秒杀这类场景,我学习过整套处理思路:
    前端请求层做限流削峰,Redis 预减库存挡大部分流量,MQ 异步解耦下单流程;通过请求 ID 做下单幂等;利用分布式锁防止库存超卖;同时设计熔断降级保障核心链路;最后数据库做最终一致性校验。
    我们运维场景没有资源抢占,更多是大批量平稳同步,不会出现热点 Key 争抢的情况。

🔗 CMDB基础配置数据库|项目口述

CMDB 是美的集团统一 IT 资产配置底座,作为全集团自动化运维体系唯一可信数据源。集团拥有上万台服务器,覆盖复杂制造业运维场景,上层 MOPS 运维平台、备份系统、监控平台全部依赖 CMDB 资产数据;一旦数据失真、服务读写卡顿,所有变更操作、线上故障根因定位都会失去依据,所以保障数据准确与服务稳定是核心目标。
我作为平台管理员,负责业务接入、核心功能运维、线上故障兜底与私有化交付。平台基于 MongoDB 存储资产信息,围绕数据同步、资产采集、快速检索三大核心能力建设:
1、数据订阅能力:为满足下游众多系统增量同步资产数据的需求,我们基于 MongoDB Oplog 实现变更监听,资产表新增、修改、删除事件自动触发回调接口,完成增量数据推送。依托 Oplog 天然的数据变更日志机制,规避轮询查询带来的数据库压力,我也深入研究了 MongoDB 复制集、Oplog 存储与过期清理机制。
2、资产自动采集:以防火墙策略采集链路举例,整体依靠 XXL‑Job 调度下发采集任务,任务投递至 Kafka;采集执行服务消费任务,通过 API 调用、Ansible 远程执行、FTP 拉取多种方式获取远端设备配置,采集结果再次投递 Kafka;采集代理消费数据,清洗入库 MongoDB。整条链路依赖 Kafka 实现异步解耦,日常处理过大量生产消费速率不匹配导致的消息堆积问题。
3、全文检索能力:借助 mongostash 组件,将 MongoDB 资产数据实时同步至 Elasticsearch,对外提供统一检索接口,解决 MongoDB 复杂模糊查询性能差的痛点,支撑业务方快速检索各类资产。
除功能迭代支撑外,我大量精力投入平台稳定性运维与故障闭环:经常遇到查询接口响应缓慢、视图查询超时问题,多数根源为缺失合适索引,通过优化索引策略持续优化查询性能;针对服务整体读写卡顿,沉淀标准化故障排查 SOP,依次排查容器资源、外部接口耗时、数据库慢查询逐层定位瓶颈;常态化处理 Kafka 消息堆积、Java 服务频繁 FullGC、OOM 内存溢出等线上问题。
同时负责平台私有化容器化交付,独立定位并修复 ZooKeeper 集群启动异常问题:原配置固化集群 IP,容器重建后 IP 变更导致集群失联;我在集群健康状态下执行 reconf,使用域名替换固定 IP,彻底根治该缺陷,保障私有化环境长期稳定运行。
【预埋引导话术,自然穿插】整套系统混合使用 MongoDB、ES、Kafka,在实践中能明显感受到文档数据库在强事务场景存在短板,这段时间我也系统深入学习 MySQL InnoDB 索引、事务、锁相关理论,对比两类数据库适用场景差异。

CMDB基础配置数据库|高频问答

  • Q0:CMDB 本质是一套资产数据读写服务,你在日常负责过程中,从架构稳定性角度,如何保障数据可信、接口稳定可靠?
    CMDB 作为集团运维领域的根数据源,它的核心本质并不是简单的增删改查接口,而是持续稳定输出可信资产数据。一旦写入脏数据、接口大面积超时不可用,上层整套自动化运维流程、故障根因分析都会完全失效。我在承担平台管理员职责期间,不只是做功能使用与问题救火,更多会从架构稳定性视角,围绕写入管控、访问防护、故障兜底、长期治理四个维度去做持续性优化与思考。

    • 第一,在数据写入链路上,基于责任链模式搭建分层校验体系,从源头降低脏数据产生概率。所有外部系统写入请求统一收口到写入 API,拆分为基础格式校验、资产关联规则校验、业务权限三层责任链。一方面实现校验逻辑解耦,新增业务规则不需要改动主业务流程;另一方面统一全集团数据写入标准,避免各个业务接入方各自实现校验逻辑,口径不一致。落地过程中也发现单纯堆叠校验节点会拉长接口耗时,我区分了强同步校验和弱业务规则校验,非关键校验后置为异步校验,在保证数据约束的前提下控制接口 RT,兼顾正确性与性能。同时写入接口层面统一增加幂等防护,依靠业务唯一标识做请求去重,解决上游系统网络抖动、服务重试带来的重复创建、重复更新问题,避免反复写入造成数据错乱。
    • 第二,在数据查询链路上,通过多重防护保障查询接口整体稳定性,防止单业务方拖垮整个平台。平台大量业务依赖多实体关联的资产视图接口,极易出现无限制条件查询、深度分页等高危请求。治理手段上:接口层面强制分页、设置单请求最大返回条数;对调用方做调用配额限流,做租户级访问隔离;持续监控慢查询,针对高频视图查询定制复合索引;通过监控提前发现异常查询流量,提前介入治理,而不是等故障发生再处理。这里我的思考是,单纯靠加索引无法永久解决查询压力,后续演进上需要做冷热资产区分,高频基础资产信息增加缓存层,把复杂视图查询流量与基础读写流量进行物理隔离,进一步提升整体可用性。
    • 第三,服务运行层面做好高可用与故障兜底设计。服务采用多实例容器部署,避免单点故障;沉淀完整的读写卡顿排查 SOP,从容器资源、外部依赖 RT、数据库慢查询、应用 GC 状态逐层定位瓶颈。在长期运维中意识到,高可用不能只依赖事后故障排查,需要前置建设:完善全链路埋点监控,对写入失败、查询超时、慢查询建立分级告警,由被动排障转向主动预警。
    • 第四,长期的数据可信治理思考。功能层面校验只能拦截实时写入错误,无法避免日积月累产生逻辑脏数据。参考业界 CMDB 产品的建设思路,我认为必须配套离线巡检任务,定时扫描全量资产,按照业务规则校验数据一致性,对异常数据产生告警,形成 “写入拦截‑实时监控‑离线巡检” 三层的数据可信保障体系。
    • 延伸总结(回答收尾拔高)
      整体来看,保障 CMDB 稳定不是一次性架构改造,而是持续性的治理工作。功能代码只解决 “能不能读写”,而稳定性设计、限流防护、校验约束、持续巡检解决 “能不能长期可信、稳定地读写”。这套架构基于 MongoDB 文档库实现,更适合资产字段灵活多变的运维场景,牺牲了原生强事务能力;如果是交易类业务系统,基于 MySQL 事务机制会是更稳妥的选择,我也针对性补充学习了 MySQL 索引、事务与锁相关原理,完善自身数据库层面的知识储备。
    • 衍生问题 1:责任链模式在校验场景相比 if‑else 有什么优势?有没有弊端?
      优势:规则解耦、易扩展、方便开关校验规则,适合持续迭代多条业务规范;
      弊端:链条过长带来性能损耗,调用链路调试复杂;
      落地取舍:核心强同步校验精简链路,次要校验后置异步执行。
    • 衍生问题 2:幂等具体怎么实现?有几种方案,你们为什么选择当前方案?
      可选方案梳理:唯一索引、token 机制、业务标识记录;
      结合你们业务:上游外部系统众多,统一 token 难以推进,选择业务主键幂等方案更易落地。
    • 衍生问题 3:如何防止业务方写出大量慢查询拖垮 CMDB 视图接口?
      四层防护:接口强制分页 + 查询参数校验 + 慢查询实时监控告警 + 业务访问限流 + 定期推动业务方优化查询语句。
    • 衍生问题 4:如果让你重构整套数据读写架构,优先增加哪些稳定性能力?
      1 新增完整变更审计日志,支持任何资产变更回溯,满足运维审计;
      2 引入缓存承载高频简单查询,隔离复杂视图查询压力;
      3 增加脏数据定时巡检任务,主动发现不合规资产信息;
      4 核心读写链路进行流量拆分,查询集群与写入集群物理隔离,互不影响。
    • 数据读写系统的关键点??TODO
      幂等
      事务
      限流
      **
  • Q1:你站在平台管理员视角,谈谈 CMDB 整体微服务架构体系、流量链路与服务拆分的设计思想?
    CMDB 整体基于 SpringCloud 微服务架构 搭建,是一套分层清晰、职责单一、能力解耦的企业级资产底座架构。我在日常运维、问题排查、功能支撑过程中,完整梳理了平台流量链路与各核心微服务的职责边界,也理解了团队架构拆分的核心设计思路。

    • 第一,完整的整体流量接入链路。
      外部所有访问流量,经过 F5 负载均衡 做第一层流量分发与四层负载,再经过 Nginx/Apache 做静态资源处理与反向代理,统一前置流量收口,最终全部进入SpringCloud 网关。由网关实现统一鉴权、路由分发、限流拦截、请求日志埋点,再精准调度转发至后端各个微服务,实现了流量统一入口、统一管控、统一防护。
    • 第二,核心微服务职责拆分与架构解耦设计(核心亮点)。
      平台按照「用户交互、数据变更、数据同步、对外服务、采集执行、检索赋能」的职责维度做垂直拆分,避免大单体臃肿、职责混杂,我日常接触最多的四个核心服务,分工非常清晰:
      1 用户交互服务:专门承接前端页面所有用户操作、资产查询、表单提交、视图展示等交互类接口。只负责请求接收、参数校验、页面逻辑组装,不直接操作数据库,保证交互层轻量化、稳定、专注用户体验。
      2 数据连接器服务:是 CMDB 对外暴露的标准化出入口,负责对外提供统一资产读写 API、资产视图查询、第三方系统数据对接、权限鉴权。所有下游系统、外部业务方调用 CMDB,全部统一走这一层,实现对内对外接口隔离,保护底层数据安全。
      3 数据入库服务(核心中台服务):这是我理解最深的架构亮点。无论是前端用户交互产生的数据变更,还是外部连接器接收的第三方数据变更,所有写库、数据落地、数据校验、幂等控制,全部收敛统一由数据入库服务完成。
      这个设计的优势非常关键:
      ✅ 实现读写分离、变更收口,所有脏数据拦截、校验规则、幂等逻辑只需要在这一层维护,不需要多服务重复开发;
      ✅ 彻底解耦「查询展示、对外接入、数据落地」能力,避免多服务直接写库导致的数据不一致、规则不统一、溯源困难;
      ✅ 所有变更单点可控、可审计、可兜底,从架构层面保障 CMDB 作为根数据源的权威性。
      4 配套能力服务集群:平台将非核心链路能力完全拆分独立部署,包括:
      • 数据同步服务:负责 Oplog 监听、增量数据推送下游;
      • 采集执行/采集代理服务:负责全量资产任务调度、远程采集、数据清洗;
      • 全文检索服务:负责对接 ES、模糊检索、资产搜索赋能。
      各服务各司其职,任务互不干扰,单个子服务异常不会拖垮主核心链路,极大提升平台整体可用性。
    • 第三,我对这套架构的个人设计思考与总结。
      我在长期运维中总结,这套微服务拆分完全符合单一职责、收口可控、分层隔离、高内聚低耦合的架构思想:
      1 入口统一:网关+连接器双层收口,内外流量隔离;
      2 变更唯一:全平台所有写操作统一收口入库服务,从架构根源保证数据可信;
      3 能力解耦:交互、查询、写入、同步、采集完全拆分,迭代互不影响,适合长期大型底座迭代;
      4 稳定性高:核心链路与辅助链路物理隔离,故障爆炸半径小。
    • 面试一句话亮点总结:
      CMDB 架构最大的精髓不在于微服务拆分本身,而在于所有数据变更的唯一收口设计,通过统一入库服务管控全平台写逻辑,从架构层面保证了集团资产数据源的唯一性、规范性与可信度,这也是企业级配置中心最核心的架构设计亮点。
    • 衍生追问1:为什么不让交互服务、连接器服务直接写库?
      如果多服务直接写库,会出现校验逻辑分散、幂等实现不统一、变更无统一日志、问题无法溯源,多人开发极易出现规则冲突、数据错乱。统一收口写入服务,可以做到规则统一、审计统一、兜底统一、治理统一,是底座类系统必备的架构设计。
    • 衍生追问2:F5、Nginx、网关三者的区别你怎么理解?
      三者是层层递进、各司其职的流量架构:
      1 F5:四层负载,负责最前置的 TCP 流量分发、负载均衡、高可用接入,不处理业务逻辑;
      2 Nginx/Apache:七层反向代理,处理静态资源、简单路由、解压、缓存,减轻后端服务压力;
      3 SpringCloud网关:业务层入口,负责鉴权、限流、路由、灰度、业务拦截,是微服务真正的流量管家。
      三者前置层层防护,让后端微服务只专注业务逻辑,是标准企业级流量架构。
  • Q2:介绍下基于 Oplog 实现的数据订阅方案,存在什么缺陷?如何处理?

    1. 实现原理:MongoDB 复制集生成 Oplog,完整记录库内所有 CRUD 变更;服务持续拉取解析日志,将资产变更事件推送下游系统;
    2. 方案优势:无业务代码埋点、低侵入;相比定时轮询,极大降低数据库查询压力;
    3. 现存痛点:Oplog 存在磁盘容量上限,日志滚动覆盖会造成变更丢失;消费进程宕机,窗口期变更无法捕获;
    4. 落地解决方案:持久化保存消费位点,增加消费中断告警;额外提供全量同步接口,支持下游进行数据补偿。
      延伸引导:如果是 MySQL 环境,一般依靠 Binlog 实现同等能力,我近期也对比研究过 Binlog 与 Oplog 的设计思路异同。
  • Q3:详细描述防火墙自动采集整条链路,线上遇到过哪些典型问题?
    链路流程:XXL‑Job 下发采集任务 → 消息投递 Kafka → 采集执行服务消费任务 → 通过 API/Ansible/FTP 拉取设备配置 → 结果推送 Kafka → 采集代理消费数据,清洗入库 MongoDB。
    典型故障:外部设备接口响应缓慢阻塞消费、生产速率大于消费速率引发 Kafka 消息堆积;
    优化手段:消费端开启批量处理、限制单节点任务并发、异常消息转入死信队列,避免任务持续重试堵塞链路。

  • Q4:mongostash 同步 MongoDB 数据到 ES,出现两边数据不一致,如何排查修复?

    1. 第一步排查同步组件运行状态,定位中断诱因:网络波动、文档字段结构不兼容、超大文档同步失败;
    2. 区分故障类型:增量同步链路断层、存量数据长期存在差异;
    3. 解决方案:恢复同步链路;开发定时数据校验任务,对比 MongoDB 与 ES 数据,自动触发缺失数据补偿。
  • Q5:平台查询接口响应很慢,你的标准化排查 SOP 是什么?

    1. 检查容器资源指标:CPU、内存、网络负载,确认是否资源瓶颈;
    2. 观测依赖外部接口 RT,排除第三方调用耗时拖累整体链路;
    3. 抓取数据库慢查询,核查索引是否合理;
    4. 分析应用 GC 日志,判断是否存在内存压力、频繁 GC 拖慢服务。
      实战案例:多次资产联合视图查询由于缺少复合索引导致超时,优化索引后查询耗时大幅下降。
  • Q6:线上 Java 服务频繁 FullGC、出现 OOM,你的完整排查流程?

    1. 故障发生优先保留现场,导出堆快照;利用 jstack、jmap、MAT 工具分析;
    2. 定位内存占用大户:超大集合、长期无法释放的对象、线程长时间持有资源;
      实战优化:批量资产采集任务一次性加载全量数据,改造为分页分批加载,缩短对象生命周期,解决内存溢出。
  • Q7:Kafka 消息堆积一般怎么分析、如何解决?

    1. 先监控 lag 指标,区分根因:消费处理能力不足、消费逻辑阻塞、消息重试形成死循环;
    2. 处理方案:优化消费业务逻辑、合理提升消费并发、阻塞消息隔离至死信队列;结合采集链路中真实堆积案例阐述。
  • Q8:syn-task服务FullGC失控、Kafka消费停滞的P1故障完整复盘
    我处理过采集代理服务严重FullGC失控故障,导致Kafka消费停滞、批量采集入库失败,通过线程栈、堆dump、JVM监控全方位定位根因,完成代码架构优化与JVM调优,是我Java工程稳定性的核心实战亮点。

    • 一、故障核心现象
      服务Old区使用率99.97%,每秒多次FullGC且无法回收内存,STW耗时数百毫秒,导致Kafka心跳超时、消费者反复重连,采集任务入库全部失败,故障持续80分钟,定级P1重大故障。
    • 二、多层级根因定位(面试高分逻辑)
      1. 核心代码根因:全局 synchronized 实例锁,锁内执行MB级JSON反序列化、批量入库等超耗时IO操作,持锁时间长达数十秒;
      2. 内存堆积诱因:多条Kafka消息新建裸线程消费,大量线程BLOCKED等锁,线程持有对象无法GC回收,持续积压内存;
      3. JVM配置短板:堆内存仅1G,配置严重不足,无冗余缓冲空间,轻微内存积压直接触发OOM;
      4. 恶性死循环:FullGC长时间STW导致业务处理更慢、持锁更久、线程积压更多,形成闭环故障,系统无法自愈。
    • 三、紧急止血方案
      临时扩容堆内存至4G并重启服务,快速恢复Kafka消费与采集入库能力,止损线上故障。
    • 四、四大根治优化方案(核心工程亮点)
      1. 锁粒度精细化重构(核心优化):废弃全局大锁,基于instanceId实现细粒度分布式锁,仅将毫秒级Redis原子操作留在锁内,耗时的反序列化、入库逻辑全部移至锁外,持锁时间压缩至毫秒级;
      2. 消费线程池规范化:废弃无上限裸new Thread(),改用有界线程池+CallerRunsPolicy拒绝策略,实现消费背压,避免线程无限创建堆积;
      3. Kafka消费机制优化:关闭自动offset提交,改为业务处理完成后手动提交,避免重启丢失未入库消息,保证数据一致性;
      4. JVM参数标准化:固化4G堆内存、OOM自动dump、GC日志持久化、OOM自动退出参数,提升服务稳定性与可排查性。
    • 面试拔高总结:这次故障让我彻底掌握了「锁设计优化、线程池治理、JVM调优、消息队列消费机制」的综合工程能力,理解了代码细节缺陷如何引发线上P1级重大故障,具备从代码层、架构层、运维层全方位根治稳定性问题的能力。
  • Q9:私有化交付遇到 ZooKeeper 集群无法启动,完整说下排查过程和解决方案
    现象:容器环境重启后 ZooKeeper 集群启动失败,节点无法互相通信;
    根因:集群配置文件硬编码节点物理 IP,容器重建之后 IP 发生变动;
    手工处理:创建版本化配置文件
    解决方案:选择集群健康运行窗口期执行 reconf 动态更新集群配置,用内部域名替换固定 IP;
    亮点:方案无需停机重建集群,不存在业务中断;
    拓展思考:云原生容器环境部署中间件,应当尽量规避写死 IP,优先使用域名、服务发现机制。

  • Q11:讲讲你线上Java大日志文件清理的核心踩坑、Linux文件底层原理与零中断清理方案
    我在运维CMDB微服务过程中,深度踩坑Java服务活跃日志清理场景,吃透了Linux文件inode、文件句柄FD、幽灵文件核心原理,沉淀两套零业务中断的日志清理最优方案,规避致命操作风险。

    • 一、核心底层原理(面试高频)
      1. 核心三要素:文件名仅为目录索引标签,inode是文件真实数据存储载体,文件句柄FD是进程读写文件的内核通道;
      2. Linux文件删除判定规则:文件真正释放的前提是「文件名解绑+所有进程FD关闭」,仅执行rm删除文件名,进程持有FD仍会持续写入inode,产生deleted幽灵文件,磁盘空间无法释放;
      3. 日志缓存特性:Java服务持有活跃日志文件句柄时,ll/ls读取的是目录缓存大小,无法实时同步真实文件大小,易造成操作误判。
    • 二、线上致命踩坑复盘(避坑亮点)
      此前处理60G超大活跃日志时,因ll缓存未刷新误判清理失败,错误执行rm删除活跃日志,导致产生大量幽灵文件,服务持续向废弃inode写入数据,磁盘空间无法释放,最终只能重启服务恢复,造成短暂服务不可用。通过这次故障,我总结出严禁直接rm清理Java活跃日志的运维准则。
    • 三、两套零中断最优清理方案(适配不同场景)
      1. 轻量安全方案:cp备份+cat /dev/null > 清空,无需重启服务、无需发送信号,Java进程无感知,日志链路不中断,适配中小体积日志;缺点是cp过程短暂占用双倍磁盘空间;
      2. 超大日志最优方案:mv重命名+touch新建+kill -HUP重载日志,mv瞬间完成无磁盘翻倍风险,适配100G+超大日志文件,通过发送信号让Logback/Log4j2重新绑定新inode,实现无缝切换;
      3. 缓存刷新方案:掌握kill -USR1进程信号,不重启服务即可刷新目录缓存,让ll实时展示真实文件大小。
        面试拔高总结:日志清理看似基础,实则涉及Linux内核底层机制与Java日志框架联动,我通过实战踩坑形成了「懂原理、避风险、分场景、零中断」的标准化运维能力,规避线上低级致命故障。
  • Q12:详细说说CMDB内存Swap抖动OOM重大故障的完整排查、根因与整改思路
    我处理过CMDB data-center服务P1级内存故障,核心是Linux Swap抖动引发的服务OOM崩溃,全程沉淀了系统内存调优、JVM参数配置、线上故障止血的高阶实战能力。

    • 一、故障核心现象
      清空50G超大日志后,服务器Load飙升至15+,Swap使用率从14%暴涨至100%,持续si/so双向交换,系统陷入震荡,最终Java服务频繁FullGC、OOM崩溃,服务中断40分钟。
    • 二、核心根因(分层拆解,面试亮点)
      1. 直接诱因:错误使用cat /dev/null > 清空大日志,内核触发文件截断、刷盘操作,临时占用大量PageCache,瞬间耗尽物理内存;
      2. 系统底层问题:内核LRU算法误判,将Java、ilogtail的4GB活跃热数据换出至Swap,而非仅清理冷数据;
      3. 稳态架构隐患:服务器物理内存配置临界,长期依赖Swap存放冷数据,无冗余缓冲空间,一旦出现临时内存占用,直接打破内存平衡;
      4. 恶性死循环:热数据常驻Swap,业务频繁访问触发持续Swap In/Out,GC暂停时间从毫秒级拉长至秒级,最终OOM Killer杀死进程。
    • 三、紧急止血方案(零扩停最优操作)
      1. 精准止损:优先停止非核心日志采集服务ilogtail,快速释放5.8GB内存,满足Swap清空的物理条件;
      2. 强制恢复:执行swapoff -a && swapon -a,强制将Swap中8GB数据回迁物理内存,彻底终结内存震荡;
      3. 服务恢复:重启崩溃的data-center服务,校验日志写入、接口访问正常后恢复非核心服务。
    • 四、长效整改优化
      1. 操作标准化:统一使用truncate -s 0命令清空大日志,杜绝cat截断引发的内存震荡问题;
      2. 日志架构优化:配置Logback按天、按大小双维度日志切割,避免单日志文件超大;部署自动备份清理定时脚本,实现日志常态化轮转;
      3. JVM参数固化:统一配置4G堆内存、OOM自动dump、GC日志持久化、OOM自动退出参数,提升服务容错性与可观测性;
      4. 资源扩容优化:规划服务器内存扩容至32G,拆分非核心服务,降低核心服务内存占用压力。
    • 面试拔高总结:这次故障让我跳出单纯的JVM调优,掌握了「应用层+JVM层+Linux系统层」三层联动的故障排查思维,理解了内存、PageCache、Swap的底层联动机制,具备处理服务器级重大稳定性故障的能力。
  • Q13:讲讲CMDB MongoDB实例异常重启故障的排查思路与索引优化落地
    我处理过MongoDB节点无故崩溃重启的线上故障,通过日志溯源、慢查询分析、索引优化,彻底解决数据库实例稳定性问题,沉淀了MongoDB性能调优与故障排查能力。

    • 一、故障排查标准化SOP
      1. 定性崩溃类型:通过日志关键词Got signal区分主动崩溃(Aborted)与外部kill(Terminated),锁定为服务内部异常崩溃;
      2. 定位故障时间窗口:截取崩溃前后日志,精准筛选异常时段的数据库操作记录;
      3. 挖掘核心元凶:过滤COLLSCAN全表扫描日志,发现单条查询扫描20万+数据、仅返回1条结果,无复合索引导致超高IO消耗,是实例崩溃核心诱因;
      4. 集群状态校验:通过MongoDB Compass确认副本集主从节点状态,区分仲裁节点与数据节点,保证优化操作在主节点执行;
      5. 索引落地验证:后台创建业务复合索引,通过Explain执行计划验证,实现全表扫描(COLLSCAN)转为索引扫描(IXSCAN),查询耗时从5s+降至毫秒级。
    • 二、核心技术亮点
      1. 精准问题定位:通过日志关键词筛选、执行计划分析,定位「大扫描量、低返回量」的低效查询,解决MongoDB隐性性能瓶颈;
      2. 生产无损优化:采用background后台建索引,避免锁表影响线上读写业务;
      3. 稳定性治理:建立MongoDB慢查询巡检机制,常态化监控全表扫描、超耗时查询,提前规避实例过载、重启故障。
  • Q14:CMDB Kafka偶发性消息堆积的根因分析与治理方案
    线上出现每周定时Kafka消息堆积的规律性故障,我通过流量分析、消费能力校验,定位瓶颈并完成轻量化治理,保障采集链路稳定运行。

    • 一、故障现象与根因
      每周日凌晨定时出现Topic消息堆积,1小时后自动恢复,核心根因是:定时采集任务集中触发,消息生产流量激增,但对应Topic 6个分区仅配置1个消费实例,消费能力无法匹配瞬时生产流量,形成生产>消费的堆积缺口。
    • 二、治理方案与优化思路
      1. 风险评估:校验Broker磁盘容量、消息堆积自愈能力,确认堆积为瞬时流量峰值导致,无数据丢失、无业务阻塞,无需扩容消费实例;
      2. 告警阈值优化:适配业务流量规律,上调该时段消息堆积告警阈值,避免无效告警干扰运维;
      3. 长效优化规划:后续可根据流量峰值,扩容消费实例、匹配分区数量,提升峰值消费能力,彻底消除堆积现象。
  • Q15:CMDB微服务视图卡顿、Tomcat线程打满故障排查与治理
    针对CMDB对外数据访问服务接口卡顿、500报错问题,我沉淀了Tomcat线程池监控、服务链路排查、上下游定位的标准化排查能力。

    • 一、故障核心现象
      ops-data-access对外查询、写入接口大面积500报错,接口响应卡顿,核心原因为Tomcat工作线程被批量写入请求打满,查询请求无线程可用、直接被拒绝。
    • 二、分层排查思路
      1. 线程监控定位:通过Tomcat线程指标判定,线程数打满100阈值、accept-count=0,新请求无排队队列直接拒绝;
      2. 链路分层校验:区分写入卡顿、查询卡顿不同场景,精准判断瓶颈在当前服务还是下游入库服务;
      3. 溯源流量源头:通过MongoDB审计表,按数据量排序筛选大批量写入请求,定位高频、大流量调用方,完成业务侧限流治理。
    • 三、核心优化思路
      1. 流量隔离:拆分批量写入与普通查询流量,避免大批量任务占用核心线程池,拖垮查询业务;
      2. 阈值管控:优化Tomcat线程池参数、请求队列长度,适配业务批量场景;
      3. 业务治理:对接调用方,规范批量写入频次与单次数据量,从源头降低流量峰值压力。
  • Q17:如果让你重新设计整套 CMDB 底座,你会做哪些优化?

    1. 对资产进行冷热分层存储,降低全量检索压力;
    2. 核心强一致性业务资产引入 MySQL 混合存储,弥补 MongoDB 事务能力短板;
    3. 在采集、数据同步全链路添加埋点监控,完善告警体系;
    4. 完善数据订阅能力,增加消息幂等、死信队列、自动补偿机制,提升下游同步可靠性。
  • 18:CMDB 大量选用 MongoDB,为什么初期没有选择 MySQL?你如何对比两种数据库?
    选型原因:平台资产类型繁杂,不同设备属性差异大,需要频繁新增扩展字段;MySQL 频繁执行 DDL 修改表成本很高,MongoDB 文档模型可以灵活扩展字段,适配业务场景。
    客观短板:MongoDB 不适合强事务、多表复杂关联的交易场景。
    主动过渡话术:如果是订单类核心业务系统,MySQL 会更加合适,事实上我对 Mysql的学习更加深入(有服务化数据库开发经验),我近期系统学习 InnoDB MVCC、锁机制、索引优化等内容,能够胜任关系型数据库相关的开发与调优工作。

🛠️ MOPS 自动化运维平台|项目口述

项目定位:集团一站式自动化运维平台,承接全集团网络设备、备份服务的日常运维工单,核心替代人工高频手工操作,实现运维流程标准化、自动化、可回滚、可交付,支撑集团海量日常运维需求与外部私有化定制交付。

标准文稿:
MOPS 是美的集团核心自动化运维平台,主要承载全集团网络 F5 设备调度、NBU 备份服务两大核心模块的日常运维工作,替代传统人工登录设备、手工执行指令、手工配置策略的低效模式,实现运维工作标准化、自动化、闭环化。我作为网络与备份模块核心负责人,全程负责需求对接、方案设计、功能迭代、落地推进、团队协同及外部私有化定制交付全流程工作。
项目核心业务价值非常明确:集团日常网络变更、资源回收、备份策略下发、设备巡检工单量极大,传统人工操作效率低、误操作率高、无统一流程规范、故障难以回滚。我的核心工作,就是把高频、重复、高危的手工运维动作,通过Java 接口调用 + Ansible 远程调度 的方式固化为平台自动化能力,实现全流程线上化、一键化执行。
我主导落地了两大核心业务场景的自动化改造。第一是 F5 网络运维全流程自动化,覆盖负载均衡、域名解析资源的申请、变更、回收全生命周期,同时创新性落地了运维操作一键回退能力,解决了传统人工变更无兜底、出错需手动排查回滚、耗时久风险高的行业痛点,极大降低了网络变更的运维风险。第二是 NBU 备份服务自动化,实现客户端批量安装、备份策略批量下发、任务调度、结果校验全流程自动化,彻底替代人工远程操作。
在功能落地和工程优化层面,我结合业务场景做了针对性的性能与体验优化。针对批量多 IP 域名解析、批量设备巡检等多任务并行场景,手动串行执行耗时极长,我通过自定义线程池实现并行异步执行,大幅缩短批量任务执行耗时;针对备份客户端安装、策略下发等耗时超长的阻塞任务,我采用异步线程解耦,避免接口长时间阻塞超时,提升平台整体响应速度与用户体验。
除核心功能迭代外,我重点承担业务需求把控、团队方案落地、私有化对外交付三大核心工作。对内,我日常对齐各业务方运维需求,梳理标准化迭代方案,统一团队开发口径,协助团队成员拆解任务、落地功能,保障平台迭代高效有序;对外支撑私有化项目交付,承接客户定制化运维需求,全程对接客户沟通、需求确认、问题闭环、版本输出,积累了完整的 ToB 产品交付与客户闭环经验。
同时我主动对接部门 AI 智能化运维建设,完成运维助手与公司统一数字人平台的接入,实现办公端一键提报运维工单、自助查询运维进度;并且在团队根因定位 Agent 建设中,主动暴露平台工单标准化接口为外部 MCP 服务,支撑 AI Agent 自动拉取工单数据、辅助故障根因分析,完成传统运维平台向智能化运维的能力升级。
整体而言,这个项目让我跳出单纯的功能开发,完整沉淀了业务抽象、需求治理、工程优化、团队协同、客户交付、AI 能力融合的综合落地能力,擅长把繁琐的业务流程转化为标准化、自动化、可兜底、可交付的工程能力。

MOPS 自动化运维平台|高频问答

  • Q0:你在 MOPS 平台主要负责什么?核心解决了什么业务问题?
    我主要负责 MOPS 平台F5 网络自动化、NBU 备份自动化两大核心模块的全栈落地与运维交付,同时承担对内需求治理、团队方案推进、对外私有化定制交付工作。
    核心解决两大业务痛点:第一,集团传统网络、备份运维高度依赖人工,高频工单重复操作多、效率低、人工误操作风险高;第二,人工变更无标准化流程、无记录、无回滚机制,一旦出错排查成本极高;第三,传统运维能力无法标准化对外输出,私有化项目交付成本高、难以闭环。
    我通过将手工运维动作固化为平台自动化能力,实现了运维流程标准化、执行自动化、风险可兜底、能力可交付,大幅提升集团运维效率,降低线上变更风险,同时支撑外部客户定制化交付。
    基本实现是工单对应变更流程,记录映射到实际的生产配置,,
    自动化,具体来说是把系统运维同事的描述转换为代码实现,关键是要理清需求,不能达成自动化的同时引入新的隐患?!如根据工单从A变更到B,A状态实际在生成环境中的一些属性可能没有完全被平台工单的字段覆盖,导致变更后如果是创建新的B覆盖A(蓝绿发布),可能B中一些属性赋值为默认值,没有从A带过来,,网络变更涉及到的影响会很大,比如F5Dns变更解析IP后丢失了解析方法的原值,于是平台要引入LoadBalanceMethod字段,并且要处理好存量数据。。

  • Q1:讲讲你 F5 自动化和一键回退功能的设计思路,亮点在哪里?
    传统 F5 网络变更、资源回收都需要运维人员手动登录设备、逐条配置指令,不仅效率低,一旦配置出错,需要人工逐条比对、手动回滚,恢复时间长、风险不可控。
    我的设计思路是:平台统一接收运维工单,标准化校验参数后,通过 Java 调用设备 API + Ansible 远程执行实现自动化配置下发;同时新增变更快照机制,每次正式变更前自动留存设备原有配置快照。
    若业务需要,例如变更后验证有误,或只是临时测试,可通过平台一键触发回退逻辑,基于前置快照自动恢复原有配置,无需人工干预。这里要注意实现回退,要在变更时记录“元数据”,替代不可信的平台数据的“变更前”值。。
    这个功能的核心亮点不只是自动化执行,而是补齐了运维变更的风险兜底能力,让高危网络变更从“人工赌风险”变成“可控可回滚”,是传统运维自动化的核心价值升级。

  • Q2:项目中用到了线程池、异步优化,具体是什么场景?解决了什么问题?
    MOPS 整体业务并发压力不高,属于典型的多任务批量耗时型业务场景,核心痛点不是高并发击穿,而是串行执行效率极低、接口阻塞超时,我针对性做了几处工程优化:

    1. 批量域名解析场景:业务经常需要对几十上百个 IP 执行 nslookup 校验,串行逐个执行耗时极高。我通过自定义核心线程池,实现多 IP 并行解析,批量任务耗时缩短 60% 以上,同时通过线程池参数管控,避免无限制创建线程导致资源溢出。
    2. 备份策略下发场景:NBU 客户端批量安装、远程策略下发属于超长耗时任务,同步执行会导致接口阻塞、前端超时。我采用异步线程单独执行耗时任务,接口即时返回受理结果,后台异步完成执行、记录日志、同步结果入库,极大优化了平台使用体验与接口稳定性。
    3. 防火墙策略下发:分布式 + 线程池!?
      核心思路:针对批量、耗时、IO 密集型运维场景,通过并行+异步的工程思维,解决效率与接口稳定性问题,适配运维类业务的核心场景痛点。
  • Q3:你说你负责需求把控和团队方案落地,具体做了什么工作?
    MOPS 承载全集团运维工单,需求零散、场景琐碎、业务方诉求不统一,如果盲目迭代会导致功能冗余、逻辑混乱、难以维护。我在团队中主要承担需求收敛、方案标准化、落地推进的工作:
    第一,日常对接各业务运维团队,收集零散工单需求,梳理共性场景,过滤无效需求,将个性化诉求沉淀为标准化通用功能,避免重复造轮子;
    第二,针对复杂运维场景,牵头拆解技术方案,统一团队开发规范,协助新人拆解任务、梳理执行逻辑,保障团队迭代节奏统一;
    第三,把控迭代优先级,区分紧急故障修复、日常功能迭代、长期能力建设,保障平台稳定优先、迭代有序。
    这让我具备了从「纯开发编码」上升到「业务理解+需求治理+团队协同落地」的综合能力,能够站在业务视角做技术建设。

  • Q4:讲讲你私有化对外交付的工作内容,遇到过什么难点,怎么解决的?
    我负责 MOPS 平台私有化项目的定制化需求交付、客户对接、问题全闭环工作。和对内迭代不同,私有化交付需要完全贴合客户现场环境、适配客户个性化运维流程,同时需要全程对接客户、答疑、处理适配问题,交付闭环要求极高。
    核心难点:客户现场运维流程、设备环境和集团内部不一致,存在大量定制化适配需求,且客户对平台稳定性、操作安全性要求极高。
    我的落地做法:前期充分对齐客户流程,梳理定制化适配清单,区分通用能力和私有化专属能力;中期迭代过程中同步适配环境差异,完成功能定制、兼容性测试;后期全程跟进交付验收、问题答疑、故障兜底,保障项目顺利交付。
    这份工作让我积累了成熟的 ToB 产品交付思维,懂得平衡通用化与定制化,保证产品可落地、可验收、可运维。

  • Q5:你在项目中怎么结合 AI 做能力升级?具体落地了什么?
    我主要从用户使用智能化、故障运维智能化两个维度,完成传统运维平台的 AI 能力赋能:

    1. 前端智能化提报:将 MOPS 运维工单体系接入部门统一数字人平台,员工可直接在办公软件端通过智能入口提报网络、备份运维工单、查询进度,降低运维操作门槛,提升工单流转效率;
    2. 底层 AI 赋能对接:在团队建设运维根因定位 Agent 时,我负责梳理平台标准化工单接口,封装为通用 MCP 服务对外暴露,支撑 AI Agent 自动调取工单全量数据,用于故障复盘、根因分析、异常工单统计,为智能化运维排查提供标准化数据底座。
      整体属于传统业务平台向智能化运维体系的能力延伸,贴合当前运维+AI的行业发展趋势。
  • Q6:MOPS 并发不高,你觉得你的技术亮点在哪里?怎么体现你的工程能力?
    虽然平台线上高并发场景少,但运维类平台的核心难点是场景复杂、流程琐碎、IO 密集、批量耗时、风险极高、交付要求高,我的工程能力主要体现在场景化落地和风险治理上:

    1. 场景化工程优化:针对批量 IO、超长耗时任务,落地线程池并行、异步解耦,解决效率与接口稳定性问题,具备典型的 IO 密集型业务优化思维;
    2. 风险工程化兜底:独创变更快照+一键回退机制,把人工不可控的运维风险,通过技术方案标准化兜底,体现风险前置、稳定优先的工程思维;
    3. 业务架构抽象:将零散、无序的人工运维操作,抽象为标准化、模块化、可复用的自动化流程,体现业务抽象与架构沉淀能力;
    4. 全链路交付工程化:实现对内标准化迭代、对外私有化可交付,具备完整的产品化、工程化落地思维。
      高并发只是工程能力的一部分,而场景适配、风险兜底、业务沉淀、稳定交付、团队落地,是我在这个项目沉淀的核心工程素养。
  • Q7:如果让你优化现在的 MOPS 平台,你会从哪些方向入手?

    1. 任务调度体系升级:目前批量异步任务依赖原生线程池管理,后续可以接入分布式调度框架,实现任务持久化、失败重试、断点续跑、任务监控告警,提升批量运维任务的可靠性;
    2. 运维数据可视化:沉淀工单执行数据、设备变更数据、故障数据,搭建运维数据大盘,实现运维状态可观测、风险可预警;
    3. AI 能力深度落地:基于已开放的 MCP 接口,深化 Agent 应用,实现异常工单自动识别、常规运维操作智能执行、故障自动根因定位;
    4. 权限与流程精细化:针对私有化多客户场景,细化租户隔离、权限分级、操作审计,进一步提升平台安全性与通用性。
    5. 性能监控体系完善:开启Tomcat JMX监控,完善线程池、请求链路指标观测,提前预警线程打满、接口阻塞等隐性故障。
  • Q8:Ansible+API 自动化运维的优势是什么?为什么不用纯自研脚本?

    1. 兼容性更强:Ansible 支持多设备、多系统远程调度,适配不同版本 F5、备份设备,无需针对不同环境单独自研脚本;
    2. 轻量化易维护:基于成熟开源组件,减少自研代码量,降低维护成本和 bug 风险;
    3. 流程标准化:可以统一封装运维剧本,将高频操作固化为标准化模板,方便批量复用;
    4. 可观测性更好:Ansible 自带执行日志、结果回显,方便平台统一记录日志、排查问题,适配工单审计需求。
  • Q9:讲讲你在云原生部署、容器化交付方面的落地能力与思考?
    我今年重点深耕平台私有化产品化交付体系建设,全程吃透了Java服务容器化打包、K8S云原生环境部署、私有化项目标准化交付全流程能力,完成了MOPS从“集团内部业务系统”到“可对外输出的标准化云原生产品”的能力沉淀,补齐了后端开发云原生交付、工程化落地的核心短板。

    • 第一,熟练掌握Java服务容器化全流程打包规范。针对MOPS多模块架构特点,统一编写优化Dockerfile,基于JDK轻量基础镜像,完成服务分层打包,精简镜像体积、减少冗余依赖;同时规范打包流程、统一配置文件环境隔离,区分开发、测试、私有化生产环境配置,避免不同环境配置混淆导致的部署异常,实现服务一键构建、可移植、可快速部署。
    • 第二,精通K8S云原生环境私有化部署与适配。熟练运用K8S核心资源对象,通过Deployment实现服务无状态弹性部署、保证副本高可用;配合Service实现服务内部负载均衡、端口统一暴露;通过ConfigMap、Secret挂载私有化定制配置和私密参数,适配不同客户现场的个性化环境需求,无需改动业务代码即可快速适配多场景交付。同时掌握容器日常运维能力,可快速排查容器启动异常、端口冲突、资源限制、配置挂载失效等私有化部署高频问题,保障交付稳定性。
    • 第三,建立了完整的产品化、标准化私有化交付思维。内部迭代侧重业务功能落地,而私有化交付核心是标准化、通用性、可适配、可维护。我在跟进交付过程中,重点沉淀了适配外部客户的交付规范:一是环境标准化,统一私有化部署依赖、中间件版本、资源配额,降低现场适配成本;二是配置解耦,将所有环境差异化配置外置,实现一套镜像适配多客户现场;三是交付闭环,梳理私有化部署SOP、问题排查手册,实现快速交付、快速兜底、故障快速复盘。
    • 第四,输出运维手册时,补充了架构梳理、配置清单(xCxG)??
      核心能力亮点拔高:很多业务开发只专注业务CRUD,缺乏上线部署和产品化交付思维,而我通过私有化工作,打通了「代码开发→容器打包→云原生部署→客户交付闭环」的完整链路。不仅具备业务开发能力,更懂云原生工程化落地,能够适配企业级私有化产品交付场景,具备独立支撑项目对外商业化交付的综合能力。
    • 衍生追问:私有化容器化部署相比传统服务器部署,最大优势是什么?
      1. 环境一致性:容器打包屏蔽系统环境差异,解决“本地正常、线上报错”的环境不一致问题,适配不同客户现场复杂环境;
      2. 部署高效化:摒弃传统服务器繁琐的环境搭建、依赖安装流程,实现一键部署、快速迁移,大幅提升私有化交付效率;
      3. 高可用可扩缩:依托K8s实现服务副本冗余、故障自动重启、弹性扩缩容,相比单机部署稳定性更强;
      4. 产品化程度高:配置与代码解耦、环境隔离,真正实现一套产物多环境复用,符合商业化产品交付标准。
  • Q10:讲讲MOPS线程池满导致接口pending的故障排查、根因与优化方案
    我处理过MOPS线上P2级线程池耗尽故障,核心是线程池设计不合理导致业务接口饿死,沉淀了Java线程池架构设计、故障排查、性能优化的工程实战能力。

    • 一、故障现象
      validatePorts核心接口持续pending、客户端超时报错,影响用户正常使用,故障持续2小时,属于典型的线上线程池资源耗尽故障。
    • 二、核心根因
      1. 架构设计缺陷:多个不同业务模块共享同一线程池queryCmdbValidIpExecutor,违反线程池隔离原则;
      2. 任务特性冲突:高峰期其他模块长耗时任务占满全部15个核心线程,快速的端口校验任务无线程可用,只能排队阻塞;
      3. 调用链路短板:上层接口使用Future.join()无超时阻塞等待,任务排队后直接导致API线程永久pending,无容错机制。
    • 三、原有线程池参数问题复盘
      原有线程池核心参数不合理:核心线程数15偏小、无任务超时机制、2000有界队列过大,导致慢任务长期占用线程、任务大量堆积、快速业务被饿死。
    • 四、完整优化方案
      1. 业务线程池隔离:拆分共享线程池,为高优、快响应的端口校验业务独立配置线程池,与耗时后台任务物理隔离,避免相互干扰;
      2. 线程池参数调优:调高核心线程数、缩减队列长度,适配IO密集型运维业务场景,避免任务无限堆积;
      3. 增加超时容错:替换无超时的join()方法,配置任务超时时间,避免接口永久阻塞,提升服务容错性;
      4. 流量均衡优化:配合集群部署,规避大量请求集中单节点的问题,分散线程池压力。
    • 面试拔高总结:这次故障让我深刻理解,线程池不仅是简单的多线程工具,更是服务资源隔离、SLA保障的核心架构手段,不同优先级、不同耗时的业务必须物理隔离,才能避免业务雪崩、接口饿死。
  • Q11:为什么需要手动开启Tomcat MBean监控?有什么工程意义?
    线上曾出现Tomcat线程池打满、接口阻塞但无监控告警的问题,核心原因是Spring Boot内嵌Tomcat默认关闭MBean注册,无法暴露线程池、连接数、请求耗时等核心指标,导致服务可观测性缺失。
    一、问题根因
    Spring Boot约定优于配置,内嵌Tomcat默认不向JMX注册MBean监控节点,监控探针无法抓取Tomcat运行指标,线上线程耗尽、连接溢出等故障无法提前预警,只能被动等待用户反馈故障。
    二、解决方案与落地链路
    在application.yml开启tomcat.mbeanregistry.enabled=true,触发自动装配机制,让TomcatServletWebServerFactory自动注册MBean到JVM,监控平台通过JMX Exporter定时抓取指标,实现线程活跃数、连接数、请求异常率、耗时等数据可视化。
    三、工程核心价值
    1 完善可观测体系:补齐Web容器层监控,实现应用层、容器层、业务层全维度监控;
    2 故障前置预警:提前感知线程池满载、连接堆积、请求超时等隐患,实现从被动救火到主动预警;
    3 性能调优依据:通过监控指标精准优化线程池参数、限流阈值,让调优有据可依。

  • Q12:讲讲MOPS慢SQL排查与索引优化实战(35万数据量级)
    我针对MOPS备份任务查询接口慢查询问题,完成全链路SQL优化,解决35万数据量级下的全表扫描性能瓶颈,沉淀了MySQL索引优化、查询语句重构的实战能力。

    • 一、故障核心问题
      备份任务记录表累积35万数据,接口多条件模糊查询+大批量IN权限过滤,导致MySQL放弃索引、全表扫描,接口响应缓慢,用户体验极差。
    • 二、核心性能瓶颈
      1. 双侧%模糊查询:所有业务字段使用LIKE ‘%值%’,完全失效B-Tree索引;
      2. 大IN列表叠加查询:权限过滤数千条系统名称IN列表,叠加模糊查询后,优化器直接放弃索引走全表扫;
      3. 索引覆盖不全:仅单字段索引,无针对性业务联合索引,无法适配多条件查询场景。
    • 三、场景化优化方案(面试核心亮点)
      • 查询语句精细化改造:
        1 备份类型改为精确等值查询,前端下拉枚举选择,废弃模糊匹配,新增专属索引;
        2 IP地址智能适配:完整IP走精确查询、IP段走前缀模糊查询,适配索引机制;
        3 文件路径优化:备份集路径废弃左侧%,改为前缀匹配likeRight,可正常命中索引;
      • 索引精准新增:针对高频查询字段backup_type、ip_address、backup_set建立专属业务索引,缩小查询候选集;
      • 查询逻辑优化:规避大IN列表低效查询,优化权限过滤逻辑,减少数据库查询压力。
    • 四、优化效果
      彻底解决全表扫描问题,接口响应耗时从秒级降至毫秒级,35万数据量级下查询性能大幅提升,同时规避了后续数据增量带来的性能恶化风险。
  • Q13:介绍下 MOPS 防火墙策略定时下发的调度方案,线上遇到过什么核心问题?如何解决的?!
    防火墙策略定时下发是 MOPS 平台高优先级的运维场景,整体依托 XXL-Job 实现定时任务调度,周期性扫描待执行防火墙策略,完成批量策略自动化下发、配置同步与状态校验。这个场景也是平台为数不多的高并发、批量 IO 密集型核心场景,线上曾出现过任务重复执行、单节点调度瓶颈、批量下发效率低下三大核心问题,我针对性做了三层架构优化,彻底解决调度缺陷,同时最大化提升批量任务执行效率。

    • 一、核心线上事故:定时任务重复执行问题
      初始调度逻辑仅依靠 XXL-Job 定时扫描任务,无状态管控机制,导致严重线上问题:前一次调度的批量策略任务尚未执行完毕,下一轮定时调度触发后,会再次扫描到同一份未完成的工单,造成同一防火墙策略重复下发、重复配置覆盖,极易引发设备配置冲突、运维工单异常、设备会话震荡等高危问题。
      根治解决方案:工单全状态流转管控
      我为防火墙策略工单设计闭环状态流转机制,从根源杜绝重复执行:所有待下发的防火墙策略工单,初始状态为「待执行」,调度任务扫描到待执行工单后,立即将工单状态原子更新为「执行中」,锁定当前任务;待当前批次策略全部下发完成、配置校验通过后,再统一更新为「执行完成」。后续所有定时调度扫描,仅筛选「待执行」状态的工单,「执行中、执行完成」的工单全部跳过,彻底规避了任务未结束、新一轮调度重复执行的线上问题,实现工单执行唯一性保障。
    • 二、集群多节点调度优化:基于 Redis 分布式锁实现节点负载均衡
      解决重复执行问题后,发现单节点执行任务资源利用率低,无法发挥集群部署优势。平台后端部署了三个服务节点,原生 XXL-Job 调度存在单点执行缺陷,大概率只会固定调度到某一个节点,其余节点资源闲置,大批量防火墙策略下发时执行耗时过长,存在性能瓶颈。
      为充分利用三节点集群调度能力,我引入Redis 分布式锁实现集群节点负载均衡调度,核心设计:拆分批量策略任务粒度,通过分布式锁抢占机制,让三个后端服务节点可以竞争式承接调度任务,避免任务单点扎堆执行;同时依托分布式锁保证同一工单、同一策略仅被一个节点抢占执行,在实现集群资源充分利用、提升调度吞吐量的同时,二次兜底杜绝跨节点重复执行问题,实现高可用、高负载的集群调度能力。
    • 三、批量任务并发优化:自定义线程池实现策略并行下发
      在单节点任务执行层面,初始为串行下发逻辑,单次调度批量 N 条防火墙策略时,逐条串行执行,IO 等待时间长、整体耗时极高,批量运维效率低下。
      针对该 IO 密集型场景,我做了并发工程优化:自定义业务线程池,设置线程池核心线程数大于单次调度批量任务数 N,保障所有待下发策略可同时进入并行执行状态;通过线程池统一管控任务生命周期、拒绝策略,规避无限制创建线程导致的系统资源溢出、任务堆积问题。通过线程池并行改造,大批量防火墙策略下发耗时大幅缩短,极致提升批量运维任务的执行效率。
    • 面试拔高总结
      这套调度优化方案,是我在 MOPS 平台核心的并发工程实战亮点,区别于常规简单 CRUD 开发。我从数据状态兜底、集群负载调度、单机并发提速三个维度,层层解决定时批量运维场景的核心痛点:通过状态流转解决任务重复执行的稳定性问题,通过 Redis 分布式锁实现集群资源最大化利用,通过自定义线程池并行优化解决批量 IO 任务效率瓶颈,完整沉淀了定时任务调度、分布式并发控制、IO 密集型场景性能优化的实战工程能力。
    • 衍生追问1:为什么线程池核心线程数要大于单次批量任务数 N?
      防火墙策略下发是典型的 IO 密集型场景,大量耗时消耗在设备接口调用、配置同步等待上,CPU 资源基本处于空闲状态。线程池线程数大于任务批量数,能够保证本轮所有批量任务全部并行执行,无任务排队阻塞,最大化压榨 IO 等待间隙的系统资源,彻底释放并发执行性能;同时通过有界队列+合理拒绝策略,规避瞬时超大批量任务导致的线程资源耗尽、任务堆积问题,兼顾性能与稳定性。
    • 衍生追问2:状态更新和分布式锁是如何配合保证绝对不重复执行的?
      形成「状态前置锁定+分布式锁兜底」的双重防护体系:工单状态更新是业务层前置拦截,优先过滤已执行、执行中任务,减少无效锁竞争;Redis 分布式锁是集群层兜底防护,防止多节点同时扫描到同一待执行工单、同时触发状态更新竞争,解决并发更新状态的线程安全问题,两层机制配合彻底杜绝单节点、跨节点的任务重复执行问题。

CMDB-Agent 自然语言问数项目|AI工程化实战(核心加分项)

项目定位:基于阿里开源 Spring AI Alibaba DataAgent 二次开发,面向运维资产场景打造的轻量化智能问数 Agent。解决传统 CMDB 查数门槛高、需写SQL、依赖运维经验的痛点,支持用户通过自然语言直接查询、分析、统计运维资产数据,实现「自然语言→智能解析→精准查数→结果反馈」的端到端智能化能力,是传统运维平台向 AI 智能化升级的核心落地项目。我全程负责Java后端工程改造、Agent工作流架构优化、RAG检索定制、流式交互、模型热更新等核心AI工程化落地工作,重点深耕Java与AI结合的工程实战,而非单纯算法调优。

  • 3分钟标准项目口述文稿(可直接背诵)
    CMDB-Agent 智能问数项目,是我基于 Spring AI Alibaba 开源 DataAgent 二次迭代的运维领域智能化项目,核心目标是降低集团CMDB资产数据的查询门槛,让无SQL基础、无运维经验的员工,可通过自然语言直接查询、统计、分析上万台设备的资产数据,实现运维查数智能化、轻量化。
    项目核心难点在于,大模型原生输出具有概率性、不可控性,直接端到端调用极易出现 SQL 错误、语义偏差、脱离运维业务场景的问题;同时通用RAG检索无法适配运维资产枚举类、术语类专属场景,存在漏召回、语义稀释的痛点。针对这些问题,我聚焦AI工程化落地、Java架构适配、场景化能力优化,完成了五大核心技术改造,全程规避纯算法短板,突出后端工程落地能力。
    第一,我基于 Spring AI Graph 搭建DAG 原子化工作流架构,彻底解决大模型输出不可控问题。将原本端到端的问答流程,拆解为意图识别、证据召回、查询增强、SQL规划、SQL生成、语义校验、数据执行、报告生成等独立原子节点;每个节点通过结构化Prompt、固定输出格式、Java代码约束压缩模型自由度,同时配置Dispatcher动态路由,实现SQL失败重生成、规划不合理重试的容错闭环,极大提升问答准确率与系统可控性。同时支持分支、循环、故障重试,相比传统线性流程稳定性大幅提升。
    第二,实现全链路流式交互架构,优化用户体验与实时反馈能力。基于 Spring WebFlux 响应式编程,贯通从LLM流式响应、Graph节点流式执行到SSE前端推送的完整链路,所有节点均以Flux为输出载体;同时自定义文本类型状态机,区分SQL、Markdown、推理日志、表格数据四类流式内容,前端差异化渲染展示。并且实现线程安全的主动断连、资源释放机制,避免流式请求堆积造成的内存泄漏。
    第三,落地模型懒加载+无重启热切换工程能力,解决传统模型配置更新需重启服务的痛点。通过Spring AOP动态代理、自定义TargetSource实现Embedding模型动态委托,结合FactoryBean模式封装流式/阻塞式模型调用策略;通过比对数据库配置更新时间,结合ConcurrentHashMap缓存实现ChatClient懒加载、自动淘汰,支持DeepSeek、Qwen等多模型动态切换,无需重启服务,大幅提升运维迭代效率。
    第四,定制运维场景专属混合RAG检索架构,解决通用检索适配性差、漏召回问题。采用向量检索+关键词检索并行执行、RRF算法融合的混合检索方案,同时针对运维业务特性,将知识库拆分为实体枚举类、规则映射类两类数据,差异化配置召回参数与检索策略;通过LLM提取实体关键词,联动数据库精准模糊匹配,解决向量语义稀释导致的枚举值漏召回、术语不匹配问题,大幅提升查询语义匹配度。
    第五,设计轻量级多轮对话管理机制,实现会话无缝续接与低损耗交互。采用内存滑动窗口机制缓存核心对话摘要,不冗余存储完整上下文,降低Token消耗;冷启动时自动从数据库恢复历史会话状态,将上下文信息注入各节点Prompt,保障多轮问答的语义连贯性,同时规避大模型上下文过长导致的性能衰减与成本问题。
    整体项目完全聚焦后端AI工程化落地,不依赖算法调优,通过Java架构优化、工作流约束、检索策略定制、流式能力改造,实现了大模型在企业运维场景的稳定、可控、可用落地,补齐了我Java高阶工程、AI应用开发、响应式编程、动态代理架构的实战经验,也是我区别于普通CRUD开发的核心技术亮点。

  • Q1:你为什么要基于Graph图流程拆解问答链路?核心解决大模型什么问题?
    核心解决大模型概率性输出不可控、黑盒难容错、错误无法定位的行业通用痛点。传统端到端问答,模型一次性输出所有内容,极易出现SQL语法错误、语义偏离业务、逻辑漏洞等问题,且无法精准定位出错环节、无法自动修复、难以迭代优化。
    我通过DAG有向无环图将复杂问答拆解为多个单一职责的原子节点,每个节点只负责一项任务,通过Java代码+结构化Prompt严格约束输出格式:意图识别仅做二分类、规划节点仅输出规范JSON、SQL节点仅负责语句生成、专属节点做SQL审计校验。
    同时搭配Dispatcher动态路由,形成完整容错闭环:SQL校验失败自动重生成、规划不合理触发重试、参数异常自动兜底,将大模型的不确定性,通过工程架构手段转化为可控、可观测、可修复的稳定能力,这也是企业级AI应用落地的核心关键。

  • Q2:讲讲你全链路流式输出的技术实现,用到了哪些核心技术?
    整套流式架构基于Spring WebFlux + Reactor + SSE实现,是典型的响应式编程工程落地,贯穿整个Graph工作流:

    1. 底层载体:所有Graph节点的执行结果均以Flux流式数据流返回,替代传统阻塞式返回,支持逐Chunk输出;
    2. 中转推送:通过Sinks.Many背压感知管道订阅全链路流式输出,统一封装SSE协议推送前端,实现实时流式反馈;
    3. 内容差异化渲染:自定义TextType状态机,在流中嵌入内容类型标记,区分SQL代码、推理日志、Markdown报告、表格数据,前端针对性做高亮、折叠、展示处理;
    4. 资源安全管控:基于AtomicBoolean原子标记实现线程安全的流中止机制,前端断开连接时,自动释放Disposable资源、终止任务执行,彻底解决流式请求堆积、内存泄漏问题。
      整套方案的亮点是全链路流式贯通,并非单一接口流式,而是每个AI执行节点都支持实时输出,用户体验和系统实时性远超普通单次返回的AI应用。
  • Q3:模型热切换、懒加载的核心实现思路是什么?解决了什么业务痛点?
    传统AI项目模型Bean均为容器启动时初始化,模型配置修改、模型切换必须重启服务,迭代效率低、可用性差。我通过Spring高阶特性实现无重启热更新、按需懒加载:

    1. Embedding模型动态代理:使用Spring AOP ProxyFactory + 动态TargetSource(非静态代理),容器中的代理Bean每次被调用,都会动态委托给注册器获取最新模型实例,实现无感热切换;
    2. ChatClient懒加载缓存:基于「模型名称+思考模式」组合键,通过ConcurrentHashMap实现懒加载,首次调用动态构建实例,后续复用;同时比对数据库配置updateTime,自动淘汰过期缓存,无需重启;
    3. 多模式统一封装:通过FactoryBean工厂模式,统一封装流式、阻塞式两种调用策略,业务层无需感知底层实现,按需切换。
      核心价值:实现模型配置、模型版本、调用模式的线上无重启更新,大幅提升AI应用的迭代效率与生产环境可用性。
  • Q4:你的RAG混合检索架构和普通向量检索有什么区别?解决了什么独有场景问题?
    通用单向量检索存在严重的语义稀释、枚举值漏召回、业务术语不匹配问题,完全无法适配运维资产场景,我针对性设计了分层混合检索架构:

    1. 并行融合检索:采用模板方法模式,通过CompletableFuture实现向量检索、关键词检索并行执行,再通过RRF算法融合排序结果,兼顾语义匹配和精准词条匹配;
    2. 场景化知识分层:将运维知识库拆分为两类核心数据,差异化适配:一是实体枚举类(系统名、设备类型),调低相似度阈值、增大TopK,避免精准枚举被语义结果挤压;二是规则映射类(术语翻译、概念规范),精准匹配语义,优化查询规范性;
    3. 兜底增强检索:通过LLM提取用户问题中的实体关键词,联动数据库模糊匹配,解决向量检索无法精准匹配枚举值的短板;
    4. 多后端适配:基于条件注解实现Milvus、本地向量存储的动态切换,适配开发、生产不同环境。
      最终实现用户自然语言,精准转化为CMDB运维标准查询口径,从根源提升SQL生成准确率。
  • Q5:多轮对话你是怎么设计的?为什么不直接用大模型原生上下文?
    大模型原生上下文直接累加对话,会导致Token消耗激增、推理速度变慢、冗余信息干扰查询精度,我设计了轻量级低损耗会话管理方案:

    1. 精简存储策略:不存储完整对话内容,仅持久化「用户问题+AI规划摘要」核心数据,极大降低存储与Token消耗;
    2. 滑动窗口机制:内存中仅保留最近N轮有效会话,自动淘汰过期上下文,避免对话无限累积;
    3. 冷启动恢复:客户端重新连接时,自动从数据库加载历史会话摘要,重建上下文状态,实现跨请求无缝续聊;
    4. 精准上下文注入:仅将有效会话信息注入各节点Prompt,辅助模型理解多轮语义关联,兼顾准确率与性能。
      这套方案相比原生上下文,更低成本、更高性能、更适配企业级长期会话问答场景。
  • Q6:讲讲你项目中的容错、降级、修复机制,体现工程稳定性思维
    我在项目中搭建了多层级容错自愈体系,彻底解决AI问答随机性高、易出错的问题:

    1. 节点级重试:SQL生成失败、语义校验不通过时,自动触发节点重试,更新Prompt约束重新生成,最多支持3次迭代修复;
    2. 人工介入兜底:内置Human-in-the-Loop可中断节点,复杂场景自动暂停流程,等待用户审核规划方案,支持人工修正后恢复执行,避免错误扩散;
    3. 链路级降级:模型调用超时、网络异常时,自动降级为基础查询模式,保障核心查数功能可用;
    4. 上下文回滚:人工拒绝方案或重试失败时,通过上下文管理器回滚上一轮会话状态,避免脏数据影响后续问答。
      整套容错机制让AI应用从“偶尔可用”变成“企业级稳定可用”,是核心工程亮点。
  • Q7:这个项目最大的技术难点是什么?你是怎么攻克的?
    最大难点不是调用AI接口,而是如何通过Java工程手段,约束大模型概率性输出,适配严苛的企业运维场景。通用AI模型自由度过高,极易输出错误SQL、脱离业务口径、生成不符合CMDB资产规范的查询语句,随机性强、生产可用性极低,无法直接落地企业真实业务场景。
    我的解决方案是「架构限流+代码约束+数据增强」三层工程闭环方案,完全靠后端架构手段压制模型不确定性:

    1. 架构层拆解限流:通过Spring AI Graph把端到端黑盒流程拆分为单一职责原子节点,每一步只允许模型做一件事,从流程维度大幅降低出错概率;
    2. 代码&Prompt强约束:每个节点定义严格的结构化输出规范,通过Java实体类、转换器强制校验输出格式,非法结果直接拦截重跑,杜绝不规范输出;
    3. 业务数据前置增强:通过定制混合RAG、运维术语映射、实体精准匹配,给模型注入标准业务口径,让模型“懂运维、懂CMDB资产规则”,从输入源头减少语义偏差;
    4. 容错自愈兜底:搭配节点重试、SQL语义审计、上下文回滚机制,模型出错可自动修复,形成完整工程闭环。
      简单来说,我没有依赖模型本身的能力提升,而是通过Java工程架构治理,把不可控的大模型能力,改造为企业可落地、可审计、可自愈的标准化业务能力,这也是AI应用工程落地的核心难点与核心价值。
  • Q8:你项目中用到了Spring AI Graph,讲讲DAG工作流的核心优势,和普通串行调用有什么区别?
    传统AI应用基本都是串行线性调用,流程固化、无容错、无分支、不可控,一旦某一步出错直接整体失败,且无法针对性优化单节点能力。而我基于Spring AI Graph实现的DAG有向无环工作流,是企业级Agent落地的核心架构升级,核心优势有四点:

    1. 职责原子化、可迭代优化:将复杂问答链路拆分为独立节点,意图识别、检索、规划、SQL生成、校验各司其职,可单独优化某一个节点逻辑、Prompt、策略,不影响整体流程,迭代性极强;
    2. 动态路由、支持分支与循环:通过Dispatcher分发器实现智能路由,支持条件分支、失败重试、循环修复,比如SQL校验失败自动回退重生成、规划不合理重新召回知识,串行流程完全无法实现这类自愈能力;
    3. 全链路可观测、可定位:每个节点独立执行、独立日志、独立状态,出错可以精准定位是召回问题、规划问题还是SQL生成问题,彻底解决大模型黑盒问题;
    4. 多模式适配、灵活性极高:基于同一套DAG图,通过路由配置切换纯SQL生成、完整分析、人工审核三种模式,适配不同业务场景,一套架构支撑多类问答需求。
      总结:串行调用是“一次性黑盒调用”,而Graph DAG是可管控、可自愈、可迭代、可扩展的工程化AI工作流。
  • Q9:谈谈你对AI Agent工程化的理解,你的项目和普通简单调用LLM的AIdemo有什么本质区别?
    市面上绝大多数AIdemo只是简单封装LLM接口,单次调用、无约束、无容错、无业务适配,只能做演示无法落地生产。而我的CMDB-Agent是完整企业级AI工程化落地,核心区别体现在工程稳定性、业务适配性、可运维性三个维度:

    1. 从“黑盒调用”升级为“可控工作流”:通过DAG原子化拆解+结构化输出约束,解决大模型概率不可控问题,让AI输出可标准化、可校验、可修复;
    2. 从“通用能力”升级为“场景定制能力”:针对运维资产场景定制混合RAG、术语翻译、实体补全、枚举精准匹配,解决通用模型不懂业务、答非所问的问题;
    3. 从“一次性调用”升级为“可运维可迭代系统”:具备模型热切换、懒加载缓存、全链路流式、多轮会话管理、故障重试降级、人工兜底回路等工程能力,支持线上持续迭代、稳定运行;
    4. 纯后端工程落地、贴合企业技术栈:全程基于Java/Spring生态改造,不依赖算法调优,通过架构设计、代码约束、工程优化实现AI落地,贴合后端开发的技术深耕方向,适配企业生产环境部署标准。
      一句话总结:Demo解决“能不能跑”,我的项目解决能不能长期、稳定、准确、可运维地在企业生产环境落地。
  • Q10:项目还有哪些未深入、可优化的点?体现你的复盘思考能力
    我目前核心深耕的是AI工程落地、Java架构改造、工作流约束、检索优化、流式交互,还有部分AI进阶能力尚未深入落地,也是后续优化方向,面试可坦诚说明、体现复盘思维:

    1. MCP协议能力未深度落地:目前仅了解MCP工具调用规范,尚未实现Agent主动调用外部工具、主动拉取工单数据、主动校验资产状态的能力,后续可以基于MCP对接MOPS运维工单、监控接口,实现AI主动运维排查;
    2. 精细化文本切割与RAG优化未深挖:当前知识库以整体分类召回为主,未深入做细粒度切片、重叠度优化、召回权重精细化调优,后续可以通过自适应切片、语义分块进一步提升精准度;
    3. 人工反馈回路未完全闭环落地:目前仅实现人工暂停、审核、重规划的基础能力,尚未将人工反馈持续沉淀为知识库、自动优化Prompt与检索策略,缺少数据闭环迭代能力;
    4. 多级缓存体系待完善:可以新增热门问答、高频SQL缓存,减少重复模型调用与检索开销,进一步提升响应速度、降低推理成本。
      整体来说,项目核心工程化能力已经落地成熟,进阶AI智能迭代能力是我后续重点深耕的方向。
  • Q11:如何评价问数效果?
    ??

  • Q12:抛开Spring AI等框架,只用原生LLM能力+自定义Skills,你如何从零自研实现一套自然语言问数系统?
    如果剥离所有AI开发框架,不依赖Graph工作流、封装好的Agent组件,我可以基于原生LLM调用 + 自定义技能组件 + 纯Java工程编排,从零自研实现可落地、可控、稳定的自然语言问数系统,核心是用通用编程能力复刻框架核心逻辑,同时规避原生LLM的随机性问题,整体分为四层自研架构,完全脱离框架依赖:

    • 第一层:基础通信层(原生LLM能力封装)
      不使用框架封装的ChatClient,我会基于HTTP请求原生对接LLM模型接口,统一封装阻塞、流式两种调用工具类,自主处理请求签名、参数组装、超时重试、异常捕获、响应解析逻辑。同时通过自定义Prompt模板引擎,实现提示词的动态拼接、参数注入、版本管理,替代框架的Prompt封装能力,保证基础模型调用可管控、可复用。
    • 第二层:核心技能层(自定义Skills能力落地)
      拆解问数系统所需的核心能力,封装为独立、可复用的Java Skill技能组件,完全自主实现,核心包含四大技能:
      1. 意图识别Skill:封装原生LLM调用,传入固定分类Prompt,仅让模型输出标准化意图结果(查数/统计/分析/无效提问),通过代码枚举校验返回结果,非法输出直接拦截兜底;
      2. 知识库检索Skill:自主实现向量检索、关键词检索混合逻辑,通过Java代码完成文本向量化调用、结果排序、RRF融合、实体匹配,复刻RAG核心能力,适配运维资产场景;
      3. SQL生成&校验Skill:注入数据库表结构、资产规范、枚举口径,让模型根据用户问题和检索证据生成SQL,同时自研SQL语法校验、语义合规校验、高危语句拦截逻辑,杜绝错误SQL、删改类高危语句执行;
      4. 结果整理Skill:将数据库查询结果、原始问答数据,通过LLM二次加工为通俗易懂的自然语言报告,适配用户阅读习惯。
    • 第三层:流程编排层(纯Java自研工作流)
      替代Spring AI Graph DAG框架能力,通过责任链+状态机+条件分支纯原生代码编排完整问答链路,规避线性串行调用的缺陷。自定义全局上下文对象,统一存储用户问题、检索证据、SQL语句、会话状态、异常信息,贯穿全流程。通过代码逻辑实现核心机制:意图识别失败直接终止、SQL校验失败自动重试、证据不足触发二次检索、异常场景触发降级兜底,复刻框架的分支、重试、容错能力,实现流程可控可观测。
    • 第四层:工程稳定层(自研容错与优化能力)
      基于通用Java工程能力,补齐原生LLM的生产短板,完全复刻框架工程能力:
      1. 会话管理:自主实现内存滑动窗口+数据库持久化,管控多轮对话上下文,精简Token消耗;
      2. 重试与容错:自定义重试注解和重试策略,针对模型超时、输出不规范、SQL错误实现分级重试,搭配全局异常兜底;
      3. 流式交互:基于SSE+响应式HTTP原生实现流式推送,自主管控流资源、处理连接断连、内存释放,避免资源泄漏;
      4. 模型热更新:通过配置文件动态加载、Bean动态注册,无需重启服务切换LLM模型与参数。
    • 核心总结与面试拔高
      框架的本质是封装通用工程能力、简化开发,而底层核心逻辑完全可以通过原生LLM调用+自定义技能拆解+纯Java流程编排实现。不用框架的核心难点,不再是模型调用,而是通过手动工程约束压制大模型随机性、标准化流程、保障系统稳定性。
      这套自研方案能落地,也恰恰证明我不是只会套用AI框架的开发者,而是真正理解Agent工作流、RAG原理、AI工程化核心本质,具备透过框架底层、自主实现AI业务系统的通用编程与架构能力。

⚡项目四:基于XXL-Job的高并发定时秒杀商城系统

  • 为什么自己实现秒杀系统?
    日常工作以内部运维平台开发为主,业务偏向资产配置、自动化流程,较少接触高并发、流量削峰场景。因此自主搭建极简秒杀系统,一方面实践 Redis、消息队列、并发控制等技术;另一方面持续探索 AI Coding 工作流,借助大模型完成需求拆解、接口设计、单元测试、问题调试,沉淀一套标准化开发流程,同时学习优秀开源项目架构思想,补齐高并发系统工程落地经验。

🛢️ 项目五:InfluxDB数据库服务化能力建设

  • 我主导了InfluxDB数据库的服务化改造,在数据库管控平台(DataMars)中,建设InfluxDB的“数据库即服务”(DBaaS)能力,解决手动运维数据库效率低、易出错的问题。基于开源Influxdb Cluster,从0到1负责InfluxDB实例的备份恢复、在线变配、节点迁移/重搭等核心功能的调研、设计和开发。
  • 具体工作与关键技术细节
    1. 核心场景实现:
      备份恢复:调研并采用 influx_inspect export 逻辑备份与OSS对象存储结合方案,实现了数据库级和实例级的备份恢复,并集成到平台的工作流引擎中。
      在线变配:通过修改Kubernetes StatefulSet配置,实现了CPU、内存的原地变配,以及通过PVC(持久化存储声明)扩容实现存储空间的在线扩展。
      节点迁移与数据一致性保障:这是项目最大挑战。我设计并实现了 “热分片截断-冷副本恢复”双阶段迁移方案。在数据持续高速写入(2000 points/s)的场景下,通过先截断热分片引导新数据写入新节点,再从健康节点同步历史数据,有效解决了开源工具在迁移过程中可能丢失增量数据的问题,确保了数据一致性。
    2. 技术架构:功能深度集成到平台的apiserver、bakserver和agent组件中,通过K8S Operator模式对InfluxDB集群的生命周期进行管理。
  • 市场竞品对比与成果衡量
    • 对标产品:这类数据库服务化平台在业界类似云和恩墨zCloud、腾讯云DBbridge等数据库管理平台,核心是实现数据库资源的池化、按需供给和全生命周期智能化管理。
    • 我的成果:事实上基本没有用户,随着发现开源数据库的功能不完善,开发也中断了。
  • 在面试中介绍时,你可以遵循“背景-行动-结果-对比”的结构:
    • 开头总结:用一句话点明项目核心价值。
    • 具体阐述:重点突出你如何解决关键问题,特别是高可用、数据一致性、自动化闭环方面的设计。
    • 量化成果:用数据(如效率提升百分比、可靠性指标)证明你的贡献。
    • 展现视野:通过提及竞品,表明你不仅埋头编码,更抬头看路,了解行业最佳实践。

hr

  • 谈薪
    1. 背景:当前现金总包 18W(12.1K×14 薪 + 月度餐补 400 + 年度旅游补贴 5000),员工宿舍属于公司后勤福利,不计入总包基数,只可口头顺带提,不能算进年收入。尽量让对方先开薪资;如果要你先说,直接输出总包目标,不说单月底薪。
    2. 目标:目标总包28.8W(较现有总包上涨 60%),对应期望月薪约 20K;
    3. 口径原则:优先聊总包,不聊底薪涨幅,规避底薪从 12K 涨到 20K 看上去 67% 的巨大涨幅;HR 审批看总包,薪资可以拆分:底薪不暴涨,用绩效 / 年终补齐总包。如果底薪达不到 20K,但总包接近目标,是可接受的(HR 拆分薪资结构,底薪涨幅被控制,靠年终绩效拉总包)。
    4. 背景客观情况:上家是制造业,行业薪酬基线低,初始薪资低于同背景同经验;可以用来解绑 “现有底薪低 = 能力低”,不能卖惨、不提倒挂、不提晋升失败、不吐槽老公司。我项目经验丰富,希望互相尊重,达到一个合理的薪资。(20k是很多大厂的校招生水平,对比同工作年限的水平也偏低)
    5. HR 压价反复揪底薪 ➡️ “前公司行业薪资水位偏低,同背景人员和互联网赛道存在明显差距,希望可以结合我的实际交付能力综合定薪,不只参考过往底薪。”

2026

Maven

1. Maven是什么?

  • 基本概念
    Maven是Apache基金会的开源项目管理和构建自动化工具,使用POM(Project Object Model) 文件描述项目结构、依赖关系和构建配置。

  • 是否专门用于Java?
    主要定位:主要用于Java项目
    可扩展性:通过插件可支持其他语言(Scala、Groovy、Kotlin等)
    生态系统:虽然不限于Java,但在Java生态中最为成熟

  • 核心功能

    1
    2
    3
    4
    5
    ├── 依赖管理(Dependency Management)
    ├── 项目构建(Build Lifecycle)
    ├── 统一项目结构(Standard Directory Layout)
    ├── 插件体系(Plugin Architecture)
    └── 仓库管理(Repository)
  • Maven依赖的本质

    • 通过pom.xml引入的依赖,默认下载的是已编译的JAR包,主要包含:
      内容 说明 是否必须
      .class文件 已编译的字节码(人类可读的.java源代码文件编译而来) ✅ 必须,用于运行
      pom.xml 依赖的元数据 ✅ 必须,用于传递依赖解析
      源代码(.java) 可选,用于调试 ❌ 可选,IDE可单独下载
      javadoc API文档 ❌ 可选,IDE可单独下载
    • 依赖获取过程
      1
      2
      3
      4
      5
      6
      // pom.xml中的依赖声明
      <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>31.1-jre</version>
      </dependency>
    • 实际下载的文件
      1
      2
      3
      4
      5
      6
      7
      ~/.m2/repository/com/google/guava/guava/31.1-jre/
      ├── guava-31.1-jre.jar // 主要JAR,包含.class文件
      ├── guava-31.1-jre.pom // 该依赖的pom文件
      ├── _remote.repositories // 仓库信息
      └── 可能还有:
      ├── guava-31.1-jre-sources.jar // 源代码
      └── guava-31.1-jre-javadoc.jar // 文档

2. Maven基本命令

常用命令速查表

命令 功能 说明
mvn clean 清理项目 删除target目录
mvn compile 编译源码 生成target/classes
mvn test 运行测试 执行src/test下的测试
mvn package 打包项目 生成jar/war包
mvn install 安装到本地仓库 mvn package + 将jar安装到本地 ~/.m2/repository
mvn deploy 部署到远程仓库 发布到Nexus/Artifactory
mvn clean install 清理并安装 常用组合命令
mvn dependency:tree 查看依赖树 分析依赖关系
mvn spring-boot:run 运行Spring Boot 需要对应插件

Maven生命周期阶段

A[clean] –> B[validate] –> C[compile] –> D[test] –> E[package] –> F[verify] –> G[install] –> H[deploy]

1. mvn compile - 编译阶段

相当于:执行 javac 编译源代码

1
2
3
4
5
6
7
8
9
10
11
12
# 实际操作
# 1. 编译主代码
javac -cp "所有依赖的jar" -d target/classes src/main/java/**/*.java
# 2. 处理资源文件
cp src/main/resources/** target/classes/

# 生成目录结构
target/
├── classes/ # 编译输出的.class文件
│ ├── com/example/Main.class
│ └── application.properties
└── generated-sources/ # 生成的代码(如有)

当您运行 mvn compile时,Maven 会:
解析依赖:读取 pom.xml 中的
下载依赖:从远程仓库下载到本地 ~/.m2/repository/
构建依赖图:处理传递依赖
关键点:Maven 管理的这些依赖是已经编译好的 .class 文件(JAR 包),不是 .java 文件。

然后 Maven 会:
编译您的代码:src/main/java/下的 .java 文件
使用依赖:编译时将下载的依赖 JAR 加入 classpath
打包:将您的 .class 文件 + 资源文件打包

todo:那么打包的时候到底保护包含依赖?我自己的源代码编译 .class + 依赖包的 .class ??

2. mvn package - 打包阶段

相当于根据packaging类型不同,将编译结果打包成可分发的格式(注意:普通mvn package生成的jar不包含依赖!依赖需要单独配置)

1
2
3
4
5
6
<!-- pom.xml中指定打包类型 -->
<packaging>jar</packaging> <!-- 默认值 -->
<!-- 或 -->
<packaging>war</packaging>
<!-- 或 -->
<packaging>pom</packaging>

打包过程

1
2
3
4
5
6
7
# 对于jar包:
jar cvf target/myapp-1.0.jar -C target/classes .

# 生成的jar内容:
# META-INF/MANIFEST.MF # 清单文件
# com/example/Main.class # 你的类
# application.properties # 资源文件

3. mvn install - 安装阶段

mvn install 做了两件事:
1 执行之前所有阶段:validate → compile → test → package → verify → install
2 将当前项目的构建结果安装到本地仓库,安装位置 →

1
2
3
4
~/.m2/repository/com/yourcompany/yourapp/1.0.0/
├── yourapp-1.0.0.jar # 打包的jar
├── yourapp-1.0.0.pom # 当前项目的pom.xml
└── yourapp-1.0.0-sources.jar # 源代码(如果生成)

命令组合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 标准开发流程
mvn clean compile
mvn test
mvn package

# 跳过测试
mvn clean install -DskipTests

# 指定配置文件
mvn clean install -Pprod

# 查看帮助
mvn help:effective-pom
mvn help:describe -Dcmd=package

3. 打包Java服务为可运行JAR

三种打包方式对比

方式 插件 特点 适用场景
普通JAR maven-jar-plugin 不包含依赖 库项目
Fat JAR maven-assembly-plugin 包含所有依赖 简单应用
可执行JAR spring-boot-maven-plugin Spring Boot专用 Spring Boot应用
Shadow JAR maven-shade-plugin 重命名包解决冲突 复杂依赖

Spring Boot项目(最常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- pom.xml -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.MainApplication</mainClass>
<!-- 包含依赖 -->
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
1
2
3
4
# 打包命令
mvn clean package
# 运行
java -jar target/your-app-1.0.0.jar

完整构建流程示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
my-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/
│ │ │ └── App.java
│ │ └── resources/
│ │ └── config.properties
│ └── test/
│ └── java/
│ └── com/example/
│ └── AppTest.java
├── target/ # 构建输出目录
└── pom.xml

执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 清理 + 编译
mvn clean compile
# 生成: target/classes/ 目录

# 2. 运行测试
mvn test
# 生成: target/test-classes/ 和 target/surefire-reports/

# 3. 打包
mvn package
# 生成: target/my-project-1.0.jar

# 4. 安装到本地仓库
mvn install
# 生成: ~/.m2/repository/com/example/my-project/1.0/

4. IDEA中使用Maven与问题解决

IDEA Maven配置位置

1
File → Settings → Build, Execution, Deployment → Build Tools → Maven

常用操作

  1. 刷新依赖:Maven工具窗口的刷新按钮 🔄
  2. 执行命令:双击生命周期中的阶段
  3. 查看依赖图:右键项目 → Show Dependencies
  4. 排除冲突:在依赖树上右键排除

问题分类

  1. Import报红
    本地仓库缺失
    版本冲突
    网络问题
  2. 编译失败
    依赖下载不全
    编译版本不匹配
  3. 运行时异常
    依赖冲突
    ClassNotFound

解决方案

  1. 点击刷新按钮
  2. 仍未解决,检查网络–> 清除本地仓库–> 清理IDEA缓存 –> 检查依赖冲突 –> 重新导入项目

具体问题解决步骤

问题1:刷新按钮无效

症状:点击刷新后,依赖仍然报红

解决方案

  1. 强制刷新

    1
    2
    # 命令行执行
    mvn clean install -U

    -U 参数强制更新快照版本

  2. 删除本地仓库

    1
    2
    3
    4
    5
    # Windows
    rd /s /q %USERPROFILE%\.m2\repository

    # Mac/Linux
    rm -rf ~/.m2/repository
  3. 清除IDEA缓存

    1
    File → Invalidate Caches and Restart

问题2:依赖下载慢/失败

解决方案

  1. 配置阿里云镜像settings.xml):

    1
    2
    3
    4
    5
    6
    7
    8
    <mirrors>
    <mirror>
    <id>aliyun</id>
    <name>aliyun maven</name>
    <url>https://maven.aliyun.com/repository/public</url>
    <mirrorOf>central</mirrorOf>
    </mirror>
    </mirrors>
  2. IDEA配置镜像

    1
    2
    File → Settings → Build Tools → Maven
    修改 User settings file 为包含镜像的settings.xml

问题3:依赖冲突

症状:NoSuchMethodError, ClassNotFoundException

排查命令

1
2
3
4
5
6
7
8
# 查看依赖树
mvn dependency:tree

# 查看冲突
mvn dependency:tree -Dverbose

# 分析依赖
mvn dependency:analyze

IDEA可视化排查

  1. 右键项目 → Analyze → Analyze Dependencies
  2. 查看冲突标记(红色)

解决冲突示例

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.example</groupId>
<artifactId>problematic-lib</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>conflict-group</groupId>
<artifactId>conflict-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>

问题4:版本不兼容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 统一管理版本 -->
<properties>
<spring.version>5.3.23</spring.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

IDEA Maven工具窗口使用技巧

1. 快速操作

图标 功能 快捷键
🔄 重新导入所有Maven项目 Ctrl+Shift+O
🏃 运行Maven目标 右键→Run Maven
📊 显示依赖图 右键→Show Dependencies
🧹 执行clean 双击Lifecycle→clean

2. 配置文件切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- pom.xml 中定义不同环境 -->
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>

在IDEA Maven工具窗口的Profiles中勾选激活。

终极解决方案清单

当遇到顽固依赖问题时,按顺序执行:

  1. 第一步:清理缓存

    1
    mvn clean install -U
  2. 第二步:删除本地仓库对应目录

    1
    2
    # 删除有问题依赖的目录
    rm -rf ~/.m2/repository/com/example/problem-dependency
  3. 第三步:IDEA完全清理

    • File → Invalidate Caches and Restart
    • 删除项目下的 .idea 目录和 *.iml 文件
    • 重新导入
  4. 第四步:检查Maven配置

    1
    2
    3
    4
    5
    # 查看有效POM
    mvn help:effective-pom

    # 查看有效设置
    mvn help:effective-settings
  5. 第五步:网络代理检查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- settings.xml 配置代理 -->
    <proxies>
    <proxy>
    <id>optional</id>
    <active>true</active>
    <protocol>http</protocol>
    <host>proxy.company.com</host>
    <port>8080</port>
    </proxy>
    </proxies>

预防措施

  1. 使用依赖锁定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.2.1</version>
    <executions>
    <execution>
    <id>enforce</id>
    <configuration>
    <rules>
    <dependencyConvergence/>
    </rules>
    </configuration>
    <goals>
    <goal>enforce</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
  2. 定期清理本地仓库

    1
    2
    # 清理失败下载
    find ~/.m2/repository -name "*.lastUpdated" -delete
  3. 使用CI/CD环境验证
    在Jenkins/GitHub Actions中设置定期构建,提前发现问题。

常见错误代码与解决

错误代码 含义 解决方案
501 HTTPS Required 仓库需要HTTPS 更新settings.xml,使用https仓库
401 Unauthorized 认证失败 配置正确的server认证
Could not transfer artifact 网络/权限 检查网络,清理.lastUpdated文件
No compiler is provided JDK配置错误 配置JAVA_HOME,或在pom中指定javac

高级技巧

  1. 离线模式(网络不稳定时)

    1
    mvn clean install -o
  2. 跳过测试

    1
    2
    3
    mvn clean install -DskipTests
    # 或
    mvn clean install -Dmaven.test.skip=true
  3. 多线程构建

    1
    mvn clean install -T 4
  4. 调试模式

    1
    mvn -X clean install

Milvus

📚 文档链接: 快速入门 | Milvus 文档

什么是向量嵌入?

向量嵌入是从机器学习模型中提取的数值表示,捕捉非结构化数据的语义含义。这些嵌入通过神经网络或变压器架构对数据中的复杂相关性进行分析,创建一个密集的向量空间,其中每个点对应于数据对象(如文档中的词)的“含义”。

这个过程将文本或其他非结构化数据转换为反映语义相似性的向量——在这个多维空间中,意义相关的词被放置得更近,从而实现一种称为“密集向量搜索”的搜索方式。这与依赖精确匹配和使用稀疏向量的传统关键词搜索形成对比。向量嵌入的发展,通常源于大型科技公司广泛训练的基础模型,使得搜索能够捕捉数据的本质,超越词汇或稀疏向量搜索方法的局限性。

向量:可以理解为在多维空间中的一个点,它由一组数字(坐标)表示。在 AI 中,无论是文本、图像还是声音,都可以通过特定模型(如 qwen3-embedding-4b-torch)转换为一个向量。这个向量捕捉了原始数据的深层特征。
维度:就是指这个向量有多少个数字。例如,一个 [0.12, 0.45, -0.23, …, 0.78]的向量,如果它有 1024 个数字,我们就说它是一个 1024 维​ 的向量。维度的选择通常由您选用的嵌入模型决定,并且在创建 Milvus 集合时必须正确定义,因为所有存入的向量都必须有相同的维度。

非结构化数据、Embeddings 和 Milvus

非结构化数据(如文本、图像和音频)格式各异,蕴含丰富的潜在语义,因此分析起来极具挑战性。Embeddings 被用来将非结构化数据转换成能够捕捉其基本特征的数字向量。然后将这些向量存储在向量数据库中,从而实现快速、可扩展的搜索和分析。

Milvus 提供强大的数据建模功能,使您能够将非结构化或多模式数据组织成结构化的 Collections。它支持多种数据类型,适用于不同的属性模型,包括常见的数字和字符类型、各种向量类型、数组、集合和 JSON,为您节省了维护多个数据库系统的精力。
与处理结构化数据并执行精确搜索操作的传统关系数据库不同,向量数据库擅长使用 Approximate Nearest Neighbor(ANN)算法等技术进行语义相似性搜索。这种能力对于开发推荐系统、聊天机器人和多媒体内容搜索工具等各种领域的应用程序,以及解决 ChatGPT 等大型语言模型和 AI 带来的挑战(如理解上下文和细微差别以及 AI 幻觉)至关重要。

Milvus 概念 类比关系型数据库概念 说明
Collection(集合) Table(表) 存储向量和关联元数据(如文本内容、来源)的基本单位。
Entity(实体) Row(行) 一条完整的记录,包含主键、向量字段和标量字段(元数据)。
Field(字段) Column(列) 包括向量字段(存储向量)和标量字段(存储 ID、标签等结构化数据)。
Index(索引) Index(索引) 为向量字段建立的索引(如 HNSW、IVF_FLAT),是高速检索的关键。
  1. 写入数据(以 Python 为例)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType

    # 1. 连接到您的 Milvus 服务
    connections.connect("default", host='localhost', port='19530')

    # 2. 定义集合结构(假设向量维度为1024)
    fields = [
    FieldSchema(name="id", dtype=DataType.VARCHAR, is_primary=True, max_length=100),
    FieldSchema(name="text_content", dtype=DataType.VARCHAR, max_length=65535),
    FieldSchema(name="embedding_vector", dtype=DataType.FLOAT_VECTOR, dim=1024)
    ]
    schema = CollectionSchema(fields, description="问答助手知识库")
    collection = Collection("qa_knowledge_base", schema)

    # 3. 插入数据(假设您已使用 embedding 模型将文本转换为向量)
    data = [
    ["doc_001", "CMDB是配置管理数据库,用于...", [0.12, 0.34, ...]], # 1024维向量
    ["doc_002", "MongoDB的聚合管道用于...", [0.56, 0.78, ...]],
    ]
    collection.insert(data)
    collection.flush() # 确保数据持久化

    # 4. 创建索引(以HNSW为例)
    index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE", # 使用余弦相似度
    "params": {"M": 16, "efConstruction": 200}
    }
    collection.create_index("embedding_vector", index_params)
    collection.load() # 将集合加载到内存以提供服务
  2. 读取与搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 用户提出问题,并转换为向量
    question = "如何查询CMDB中的主机信息?"
    question_vector = embed(question) # 使用相同的模型生成1024维向量

    # 在 Milvus 中搜索最相似的 Top K 个向量
    search_params = {"metric_type": "COSINE", "params": {"ef": 50}}
    results = collection.search(
    data=[question_vector],
    anns_field="embedding_vector",
    param=search_params,
    limit=3, # 返回最相似的3条结果
    output_fields=["id", "text_content"] # 指定需要返回的元数据字段
    )

    # 处理结果
    for hits in results:
    for hit in hits:
    print(f"相似度得分: {hit.score}, 关联知识: {hit.entity.get('text_content')}")
  3. Milvus 支持的搜索类型
    ANN 搜索:查找最接近查询向量的前 K 个向量。
    过滤搜索:在指定的过滤条件下执行 ANN 搜索。
    范围搜索:查找查询向量指定半径范围内的向量。
    混合搜索:基于多个向量场进行 ANN 搜索。
    全文搜索:基于 BM25 的全文搜索。
    获取:根据主键检索数据。
    查询使用特定表达式检索数据。

  4. Rerankers
    在基础的 RAG 流程中,系统通过 Embedding 模型​ 将用户查询和知识库文档转换为向量,然后利用向量数据库进行快速的相似性搜索,找出最相关的几个文档片段作为上下文提供给 LLM 。然而,这种基于向量相似度的检索有时无法完美捕捉深层次的语义关联,可能返回一些看似相关实则无关的“噪声”文档 。
    Reranker 模型的作用就在于此。它通常在向量检索之后介入,像一个专业的裁判,对初步检索到的候选文档(例如 Top 20 或 Top 50)进行二次精排 。其核心工作原理是:
    1、精细化语义理解:不同于 Embedding 模型分别处理查询和文档,典型的 Cross-encoder Reranker​ 会将查询和一个候选文档同时输入模型,让模型直接分析两者之间的交互信息,从而给出一个更精确的相关性分数 。
    2、结果重排序:Reranker 为所有候选文档重新打分并排序,最终只将排名最靠前的、真正相关的少量文档(如 Top 3 或 Top 5)传递给 LLM 。

Milvus 4 RAG

在 RAG 流程中,Milvus 扮演着 知识库与高速检索引擎 的角色。

  1. 检索(Retrieval):当用户提问时,系统首先使用嵌入模型将问题转换为查询向量。随后,Milvus 在该查询向量与知识库中所有文档向量之间进行高速的相似性比较(不同于结构化数据库的精确查询,是支持相似语义的模糊查询),找出最相关的几个知识片段。
  2. 增强(Augmentation):将这些检索到的知识片段(元数据中的原始文本)作为上下文,与用户的问题组合成一个更丰富的 Prompt
  3. 生成(Generation):将这个富含上下文的提示词发送给大语言模型(如 qwen3-235b-a22b),由它来生成精准、有据可依的最终答案。

人工智能集成:

  1. Embeddings 模型集成:将非结构化数据转换为其在高维数据空间中的数字表示,以便您可以将其存储在 Milvus 中。目前,PyMilvus(Python SDK)集成了多个嵌入模型,因此您可以快速将数据准备成向量嵌入。
  2. Reranker 模型集成:在信息检索和生成式人工智能领域,Reranker 是优化初始搜索结果顺序的重要工具。PyMilvus 也集成了几种 Rerankers 模型,以优化初始搜索返回结果的顺序。
  3. LangChain 和其他人工智能工具集成:在 GenAI 时代,LangChain 等工具受到了应用程序开发人员的广泛关注。作为核心组件,Milvus 通常在此类工具中充当向量存储。要了解如何将 Milvus 集成到您喜爱的人工智能工具中,请参阅我们的集成和教程。

Agentic RAG(智能体驱动的 RAG,Agentic RAG = RAG + Agent:基础的 RAG 中,检索-增强-生成是一次性完成的。而Agentic RAG则引入了“智能体”的概念,智能体可以根据对大模型初步生成内容的理解,主动地、多次地 与向量数据库(Milvus)进行交互。这使 RAG 系统从静态的“文档查找”升级为动态的、具有决策能力的“研究助手”。例如:

  • 判断是否需要进一步检索:如果首次检索到的信息不足以回答问题,智能体可以重新生成搜索 query,多轮检索,直到信息足够。
  • 分解复杂问题:对于复杂问题,智能体可以将其拆解成多个子问题,然后针对每个子问题去 Milvus 中检索相关信息,最后综合所有信息生成答案。
  • 工具调用(Tool Use):智能体可以将 Milvus 的检索功能视为一个可调用的工具,根据对话进程决定何时以及如何调用这个工具。

Agent

AI Agent的本质是能够感知环境、自主决策并执行行动的智能实体。与传统AI系统最大的区别在于Agent具有自主性、反应性、目标导向和学习能力,它不再是简单的工具,而是能够主动规划和完成复杂任务的智能体。从1997年击败国际象棋世界冠军的IBM“深蓝”,到2011年苹果推出的个人助理Siri,都展示了Agent在特定领域的强大能力,关键的转折点发生在2023年左右,大语言模型LLM的出现为Agent提供了强大的通用理解和推理能力,使其不再局限于单一任务。

Agent 架构模式

  1. 组成组件
    Agent:智能代理,它是一个能够感知环境、做出决策并执行动作的软件实体(比起LLM只能“回答问题”,Agent可以真正地“执行任务”)。在LLM语境中,Agent通常利用LLM作为大脑,来理解用户输入、决定需要执行的动作(比如调用工具)、处理工具返回的结果并生成响应。
    LLM(大语言模型):如GPT系列,是Agent的核心,负责理解自然语言、进行推理和生成文本。
    Tool:工具,是Agent可以调用的函数,用于执行特定任务,比如查询数据库、调用API。Tool扩展了Agent的能力,使其能够获取实时信息或执行具体操作。
    Function Calling:函数调用,是LLM的一种能力,允许模型根据用户输入决定何时以及如何调用哪个函数(Tool),并以结构化格式(如JSON)输出函数调用参数。然后由外部系统执行该函数。
    Prompt:提示词,是引导LLM生成期望输出的文本。在Agent中,Prompt通常包含系统指令(定义Agent的角色和能力)、用户输入、对话历史以及工具描述等。
    RAG(检索增强生成):通过从外部知识库检索相关信息,并将其作为上下文提供给LLM,从而生成更准确、更相关的回答。
    MCP(模型上下文协议):MCP可以包含本地tools和外部API,但通常 MCP更强调于将工具(无论是本地tools还是外部API)以标准化的上下文协议暴露给AI模型。MCP可以看作是一个中间层,它将工具抽象成标准的接口,使得AI Agent可以通过统一的协议来调用这些工具,从而实现模型与工具之间的解耦。
  2. 一个典型的工作流程如下:
    用户输入自然语言请求。
    Agent将用户输入、对话历史、可用的工具描述(通过Function Calling定义)以及系统提示词组合成一个Prompt,发送给LLM。
    LLM根据Prompt判断是否需要调用工具。如果需要,LLM会返回一个函数调用请求(包括函数名和参数)。
    Agent解析LLM返回的函数调用请求,并执行对应的工具(Tool)。
    工具执行后返回结果,Agent将工具执行结果作为上下文,再次调用LLM,生成最终的自然语言响应。
    Agent将响应返回给用户。
    在这个过程中,RAG可以在第一步之前或之中介入,通过检索外部知识库来增强Prompt的上下文。
  3. a demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #### main.py
    from pydantic_ai.models.gemini import GeminiModel
    from pydantic_ai import Agent
    from dotenv import load_dotenv
    import tools

    # 模型API Key等可以写到.env文件 并加载到环境变量
    load_dotenv()
    model = GeminiModel("gemini-2.5-flash-preview-04-17")

    # 将llm模型、系统prompt、工具tools “注册” 到 Agent
    agent = Agent(model,
    system_prompt="You are an experienced programmer",
    tools=[tools.read_file, tools.list_files])

    def main():
    history = []
    while True:
    # eg. Input: list and read file. base on your konwledge tell me what lang each file use.
    user_input = input("Input: ")
    # 将用户输入、历史对话(上下文)发给 agent 并记录本次对话(短期记忆)
    resp = agent.run_sync(user_input,
    message_history=history)
    history = list(resp.all_messages())
    print(resp.output)

    if __name__ == "__main__":
    main()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #### tools.py
    from pathlib import Path
    import os

    base_dir = Path("./test")

    def read_file(name: str) -> str:
    # docstring 帮助模型理解 tools
    """Return file content. If not exist, return error message.
    """
    print(f"(read_file {name})")
    try:
    with open(base_dir / name, "r") as f:
    content = f.read()
    return content
    except Exception as e:
    return f"An error occurred: {e}"

    def list_files() -> list[str]:
    print("(list_file)")
    file_list = []
    for item in base_dir.rglob("*"):
    if item.is_file():
    file_list.append(str(item.relative_to(base_dir)))
    return file_list

MCP

MCP 可以看作是一个中间层,它将工具抽象成标准的接口,使得AI Agent可以通过统一的协议来调用这些工具,从而实现模型与工具之间的解耦。MCP 可以包含本地tools和外部API,但通常 MCP更强调于将工具(无论是本地tools还是外部API)以标准化的上下文协议暴露给AI模型。

  1. Agent通过集成的MCP Client向MCP Server发送请求,调用工具。因此,Agent不再直接调用具体的工具函数,而是可以使用任何符合MCP协议的工具,无论工具是本地还是远程。MCP模式下,我们不再直接注册这些Tools到Agent(如传统框架LangChain),而是注册MCP Client(可以看作是Tools的抽象层),然后Agent通过MCP Client来动态获取MCP Server提供的Tools列表,并在需要时调用它们。
    示例流程:
    步骤1:启动一个MCP Server,它提供了一些工具(例如,天气查询、计算器等)。
    步骤2:在Agent框架中,配置MCP Client,并连接到该MCP Server。
    步骤3:Agent在需要时,通过MCP Client查询MCP Server提供了哪些工具。
    步骤4:当用户提问时,Agent决定调用哪个工具,然后通过MCP Client发送调用请求给MCP Server。
    步骤5:MCP Server执行工具,并将结果返回给MCP Client,然后Agent接收结果并生成最终回答。?
  2. MCP代表了AI应用架构的演进方向,从紧耦合的工具注册转向松耦合的工具治理,特别适合企业级AI应用的复杂集成场景。

RAG

RAG​ 的核心思想是:在让大模型回答问题之前,先从一个外部的、可随时更新的知识库中检索相关信息,然后将这些“新鲜”信息作为上下文和问题一起交给模型,从而引导模型生成更准确、及时且可追溯的答案。它能有效减少模型“幻觉”(即胡编乱造),让AI应用在知识密集型任务中变得真正可靠。以及,它能帮助构建专业领域专家对话机器人。

一个典型的RAG系统工作流程包含三个关键阶段:

  1. 知识库索引构建(Indexing)
    这是准备阶段,目的是将您的私有知识库“灌输”进向量数据库。
    文档解析与分块:首先,系统会解析各种格式的文档(如PDF、Word、HTML),将其转换为纯文本。然后,根据语义或结构将长文本切分成大小适宜的“文本块”(Chunks),以适应模型上下文长度限制。
    向量化(Embedding):使用嵌入模型将每个文本块转换为一个高维向量(一组数字)。这个向量就像是文本在数学空间中的“坐标”,语义相近的文本,其向量在空间中的距离也更近。
    向量存储:将这些向量及其对应的原始文本块、元数据(如来源、标题)一并存入向量数据库。
  2. 实时检索(Retrieval)
    当用户提出问题时,系统执行以下操作:
    使用相同的嵌入模型将用户问题也转换为一个向量。
    将这个“问题向量”送入向量数据库,进行相似性搜索。数据库会快速找出与问题向量最相似的K个“文档向量”。
    为了提高检索质量,常采用混合检索策略,结合语义向量搜索和传统关键词搜索,并利用重排序模型对初步结果进行精细排序,以提升准确性。
  3. 增强生成(Generation)
    提示词增强:将检索到的最相关的文本块作为上下文,与用户的原始问题一起组合成一个结构化的提示词(Prompt)。
    智能生成:将这个富含信息的提示词发送给LLM,基于您提供的权威上下文,而非仅凭其内部可能过时的知识,来生成最终答案

Agent流程编排

业务逻辑

LangChain

LangChain 是一个功能强大的大语言模型应用开发框架。与Spring在Java后端开发中的定位类似。
https://python.langchain.com/docs/concepts/
https://arxiv.org/abs/2302.07842

LangChain 包:通过 pip 命令(类似于Java的Maven或Gradle)安装到项目中,避免重复造轮子。

LangGraph

LangGraph 核心认知:用“有向图”构建智能体的新一代框架,是 用于构建有状态、多步骤、多分支智能体(Agent) 的框架,核心是将 Agent 的行为拆解为节点(Nodes)边(Edges),用有向图(Directed Graph) 替代老版本的“线性循环” 以支持更复杂的流程编排。
LangGraph 可以理解为:给 Agent 设计“流程图”的工具,而老版本 ReAct Agent 只是这个流程图中最基础的“单循环分支”。
https://docs.langchain.com/oss/python/langgraph/overview

  • LangGraph 核心概念解析(多节点、边是关键)
    1. 核心基础:状态(State)
      • 作用:整个图的“共享内存”,所有节点都能读写这个状态(比如用户问题、LLM 思考结果、工具调用结果、最终回答等)。
      • 举例:在你之前的 CMDB Agent 中,状态里会包含:
        1
        2
        3
        4
        5
        {
        "messages": [{"role": "user", "content": "显示运行中的Linux服务器"},
        {"role": "ai", "content": "思考过程+工具调用指令"},
        {"role": "tool", "content": "工具调用结果"}]
        }
      • 特点:状态是持久化的(通过 Checkpoint 如 InMemorySaver),支持中断、恢复、回溯(比如多轮对话的上下文保留)。
    2. 最小执行单元:节点(Node)
      • 定义:图的最小操作单元,对应一个具体的功能(比如调用 LLM、调用工具、格式化回答、清洗用户问题)。
      • 类型(实际开发中常见):
      • LLM 节点:调用大模型生成思考结果或工具调用指令(比如你之前看到的“分析问题+工具选择”)。
      • 工具调用节点:执行具体的工具函数(比如 cmdb_searchcmdb_query)。
      • 预处理/后处理节点:清洗用户问题、格式化最终回答。
      • 判断节点:根据状态判断流程走向(比如“是否需要继续调用工具?”)。
      • 多节点的价值:你可以把 Agent 的复杂行为拆分成多个独立节点,比如:
        用户输入清洗节点LLM 思考节点CMDB 工具节点日志工具节点回答格式化节点
    3. 流程的灵魂:边(Edge)
      • 定义:节点之间的连接关系,定义流程的走向。LangGraph 支持三种边,这也是“多分支”的核心:
      • 普通边(Direct Edge):直接从 A 节点跳转到 B 节点(比如“清洗节点”执行完,直接到“LLM 思考节点”)。
      • 条件边(Conditional Edge):根据状态的内容判断跳转方向(最核心的“多分支”能力)。
        • 举例:LLM 输出“需要调用 CMDB 工具”,则跳转到 cmdb_search 节点;如果输出“需要调用日志工具”,则跳转到 log_search 节点;如果输出“无需调用工具”,则跳转到“回答节点”。
      • 入口/出口边:定义图的开始节点(比如用户输入节点)和结束节点(比如回答节点)。
      • 多边的价值:打破了老版本“单一循环”的限制,支持分支、并行、循环等复杂流程。
    4. 关键能力:检查点(Checkpoint)
      • 作用:记录图的执行状态,支持中断、恢复、回溯(比如你之前用的 InMemorySaver 就是内存级的检查点,还可以用 Redis、SQL 实现持久化)。
      • 场景:多轮对话中,用户可以“回退”到上一步,或 Agent 崩溃后恢复执行。
  • LangGraph 中 Agent 与 Tools 的工作机制(结合你的 CMDB 例子)
    以你之前的“显示运行中的 Linux 服务器”为例,LangGraph 的执行流程可以拆解为一个极简的有向图
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [入口节点:用户输入] 
    ↓(普通边)
    [LLM 思考节点]:分析问题,生成工具调用指令(cmdb_search + 入参)
    ↓(条件边:判断需要调用工具)
    [工具调用节点]:执行 cmdb_search,将结果写回状态
    ↓(条件边:判断无需继续调用工具)
    [LLM 回答节点]:基于工具结果生成最终回答
    ↓(普通边)
    [出口节点:输出回答]
    核心细节:
    1. 状态驱动:所有节点都通过状态交互,没有直接的函数调用——LLM 节点从状态中读用户问题,工具节点从状态中读工具调用指令,执行后写回结果。
    2. 工具的解耦:工具本身是独立的函数(如 cmdb_search),LangGraph 只通过工具调用节点统一管理,支持多工具的动态选择和调用。
    3. 循环执行:通过条件边实现“思考→工具→再思考→再工具”的循环(比如用户问“查生产主机并统计数量”,可能需要先调用 cmdb_search,再调用 cmdb_query 统计)。
  • LangGraph vs 老版本 ReAct Agent(核心区别)
    老版本 ReAct Agent(LangChain v0.x 的 AgentExecutor)是 LangGraph 的简化版,两者的核心差异在于流程的编排能力
    维度 老版本 ReAct Agent(AgentExecutor) LangGraph
    架构基础 线性循环(Single Loop) 有向图(Directed Graph)
    节点/边 无物理节点拆分,只有“思考→工具”的逻辑两步;无多分支边,只有单一循环。 支持多节点、多类型边(条件边、普通边),可编排复杂流程。
    状态管理 临时状态(每次循环重新生成,无持久化) 统一的状态对象,支持持久化、回溯(Checkpoint)。
    流程灵活性 只能“思考→工具→思考”的单一循环,多工具需按顺序调用(一次一个)。 支持多分支(比如不同工具的并行调用)、条件跳转、中断恢复。
    扩展性 扩展复杂流程(如多工具并行)需要大量自定义代码。 通过图的编排即可实现,无需修改核心逻辑。
    老版本 ReAct Agent 的核心是 AgentExecutor单一循环
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    while True:
    # 1. 调用 LLM 生成思考结果和工具调用指令
    llm_output = llm.invoke(状态)
    # 2. 判断是否需要调用工具
    if 需要调用工具:
    tool_result = tool.invoke(llm_output 中的指令)
    # 将工具结果加入状态,继续循环
    else:
    # 生成最终回答,退出循环
    break
    它的“节点”只是逻辑上的两步(思考、工具),没有物理上的节点拆分,也没有多分支的边——即使有多个工具,也只能按顺序单次调用(一次调一个),无法实现并行或多分支跳转。
    比如:
    老版本要实现“查生产主机+查今日告警”,只能先调用 cmdb_search,再调用 alert_search,通过多次循环实现。
    LangGraph 可以通过多节点和条件边,甚至并行执行这两个工具(比如同时调用 CMDB 和告警工具,提升效率)。
  • 五、总结:为什么需要 LangGraph?
    1. 从“单一循环”到“复杂图”:老版本 Agent 适合简单的单工具循环场景,而 LangGraph 适合需要复杂流程编排的场景(比如多工具、多分支、有状态的智能体)。
    2. 可视化与可维护性:将 Agent 的行为拆解为节点和边,流程更清晰,便于调试和维护(比如你之前看到的“两轮 LLM 调用”,在 LangGraph 中就是两个节点的执行)。
    3. 生产级能力:Checkpoint、状态持久化、中断恢复等能力,让 LangGraph 更适合生产环境的部署。

Spring AI

集成到Java

MongoDB


MongoDB 是一个基于文档的开源 NoSQL 数据库,使用类似 JSON 的文档模型灵活存储数据,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
https://www.mongodb.com/zh-cn/docs/manual/crud/

MongoDB 概念 类比 MySQL 概念 类比 Elasticsearch 概念 核心解析
文档 (Document) 行 (Row) 文档 (Document) 数据的基本单元,是一个键值对的有序集合。它类似于一条完整的记录。
集合 (Collection) 表 (Table) 索引 (Index) 一组文档的容器。它类似于一张数据表。
数据库 (Database) 数据库 (Database) 无直接对应(可类比为索引的逻辑分组) 最高层的命名空间,用于组织和管理多个集合。一个 MongoDB 实例可以运行多个数据库,每个数据库在文件系统上有独立的文件。
字段 (Field) 列 (Column) 字段 (Field) 文档中的键值对,代表一个数据属性。
  • 关键特性与对比
    1. BSON:MongoDB 的“增强版JSON”
    BSON(Binary JSON)是 MongoDB 用来存储文档和数据交换的二进制格式。您可以把它理解为 JSON 的超级版,它支持 JSON 的所有数据类型,但更强大: 二进制编码:比 JSON 更紧凑,存储和扫描效率更高。更丰富的数据类型:除了字符串、数字、数组、对象外,还支持日期、对象ID(ObjectId)、二进制数据等特定类型。
    2. 灵活的模式(Schema)
    这是 MongoDB 与 MySQL 最根本的区别之一。同一个集合内的文档不需要具有相同的字段集。您可以随时为文档添加新字段,而无需像关系型数据库那样进行复杂的 ALTER TABLE 操作。
  • 如何根据场景选择
    特性 MongoDB MySQL Elasticsearch
    数据模型 文档模型,动态模式 关系模型,固定模式 文档模型,可定义映射
    查询语言 面向对象的 API 方法 标准 SQL 语句 JSON-based DSL
    核心优势 敏捷开发、水平扩展、存储复杂数据结构 复杂查询、事务一致性、数据完整性 全文搜索、日志分析、复杂聚合
    典型场景 内容管理系统、用户画像、实时分析、物联网 金融交易系统、ERP、CRM等需要严格事务的系统 搜索引擎、日志和指标分析、应用内搜索
  • Mongo CRUD汇总
    操作类型 方法示例 功能说明
    创建 (Create) db.hosts.insertOne({hostname: "web-01", ip: "192.168.1.100"}) 向集合中插入一条新文档。若集合不存在,会自动创建。
    批量创建 db.hosts.insertMany([{hostname: "web-01"}, {hostname: "db-01"}]) 批量插入多条文档。
    查询 (Read) db.hosts.find({status: "running"}) 查询所有符合条件的文档。不传参数则返回所有文档。
    条件查询 db.hosts.find({"specs.memory": {$gte: 8}}, {hostname: 1, _id: 0}) 使用查询操作符进行过滤,并使用投影选择返回的字段。
    更新 (Update) db.hosts.updateOne({hostname: "web-01"}, {$set: {"specs.memory": 32}}) 更新一条匹配的文档。$set 操作符用于修改特定字段的值。
    批量更新 db.hosts.updateMany({environment: "production"}, {$inc: {visits: 1}}) 更新所有匹配的文档。$inc 操作符用于将字段的值增加指定数额。
    删除 (Delete) db.hosts.deleteOne({hostname: "web-01"}) 删除一条匹配的文档。
    批量删除 db.hosts.deleteMany({status: "terminated"}) 删除所有匹配的文档。

读写

  • 写入确认:默认情况下,MongoDB 的写入操作是异步的。如果你的应用需要确保数据已经安全写入,可以设置 写关注(Write Concern)来控制确认级别。
  • 单文档原子性:所有 CRUD 操作在 单个文档 级别是原子的。例如,一条文档的更新操作要么完全成功,要么完全失败,不会出现只更新一半字段的情况。需要注意的是,insertMany 这样的多文档操作,默认情况下并非一个整体事务。
  • 比较操作符:$gt (大于), $gte (大于等于), $lt (小于), $lte (小于等于), $ne (不等于)。
    逻辑操作符:$and, $or, $in (匹配数组中任意值), $nin (不匹配数组中任何值)。
  • 关键操作:
    1
    2
    3
    4
    5
    6
    7
    // 创建
    db.hosts.insertOne({
    hostname: "web-01",
    ip: "192.168.1.100",
    specs: { cpu: 8, memory: 16 }, // 嵌入式文档
    tags: ["web", "production"]
    })
    1
    2
    3
    4
    5
    // 查询与投影
    db.hosts.find(
    { "specs.memory": { $gte: 8 }, "status": "running" },
    { hostname: 1, ip: 1, _id: 0 } // 投影:只返回指定字段
    )
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 更新操作符
    db.hosts.updateOne(
    { hostname: "web-01" },
    {
    $set: { "specs.memory": 32 }, // 设置字段
    $push: { tags: "upgraded" }, // 数组追加
    $inc: { "stats.visits": 1 } // 数值递增
    }
    )
  • 复杂查询模式:
    1
    2
    3
    4
    5
    6
    7
    // 逻辑组合查询
    db.hosts.find({
    $or: [ // $or逻辑操作符用于筛选出符合任意一组给定条件的文档
    { "environment": "production", "status": "running" }, // 每个条件组内部使用隐式的AND逻辑,需同时满足
    { "criticality": "high", "last_backup": { $gte: yesterday } } // $gte是“大于或等于”的比较操作符
    ]
    })
    1
    2
    3
    4
    5
    // 数组查询
    db.hosts.find({
    "tags": { $all: ["web", "load_balancer"] }, // 包含所有指定标签
    "ips": { $size: 3 } // 数组长度匹配
    })
    1
    2
    3
    4
    // 正则表达式与文本搜索
    db.hosts.find({
    "hostname": { $regex: "^web-.*", $options: "i" }
    })

聚合框架

  • 聚合和管道是 MongoDB 最强大、最独特的特性之一。核心概念用工厂流水线来理解:想象一下数据处理就像一条工厂装配线
    • 普通查询:像是在成品仓库里筛选符合条件的产品。例如:“给我找出所有红色的汽车。” 你得到的是原始产品列表。
    • 聚合管道:像是把原材料送进一条多阶段的加工流水线,每个工位(阶段)都对数据进行特定处理,最终产出全新的、经过深度加工的产品。例如:原材料 → 切割 → 焊接 → 喷漆 → 组装 → 全新的汽车模型+统计报告。
  • 目的:对数据进行多阶段转换、分组、计算,生成全新的数据结构。
  • 详细对比解析
    1. 普通查询:检索和筛选文档。你得到的是原始数据。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 场景:找出所有生产环境的Linux主机
      db.hosts.find({
      environment: "production",
      "os.type": "Linux"
      })

      // 返回结果:原始文档数组
      [
      {hostname: "web-01", environment: "production", os: {type: "Linux"}, memory_gb: 16, ...},
      {hostname: "db-01", environment: "production", os: {type: "Linux"}, memory_gb: 32, ...},
      // ... 直接返回所有匹配的文档
      ]
    2. 复杂查询:在检索时进行一些简单计算或逻辑处理。
      1
      2
      3
      4
      5
      6
      7
      // 场景:找出内存大于8G的生产环境主机,按内存降序排列,只显示主机名和内存
      db.hosts.find(
      { environment: "production", memory_gb: { $gt: 8 } },
      { hostname: 1, memory_gb: 1, _id: 0 } // 投影:选择字段
      ).sort({ memory_gb: -1 }).limit(10)

      // 返回结果:经过筛选和排序的文档,但结构基本不变
    3. 聚合管道 (Aggregation Pipeline) :对数据进行多阶段转换、分组、计算,生成全新的数据结构
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      // 目标:查询生产环境的应用,并获取其运行主机的详细信息
      db.applications.aggregate([ // 在'applications'集合上执行聚合管道
      // 阶段 1: $match - 筛选文档
      {
      $match: { // 过滤条件:只处理符合以下条件的应用文档
      "environment": "production", // 条件1: 环境为'production'
      "status": "active" // 条件2: 状态为'active'
      }
      },

      // 阶段 2: $lookup - 关联查询(连表查询)
      {
      $lookup: { // 从左集合(applications)关联到右集合(hosts)
      from: "hosts", // 要关联的集合名称:'hosts'
      localField: "app_name", // 左集合(applications)中的关联字段:应用名称
      foreignField: "running_apps", // 右集合(hosts)中的关联字段:运行的应用列表
      as: "host_details" // 将关联匹配到的主机文档存入此新字段(是一个数组)
      }
      },

      // 阶段 3: $unwind - 展开数组字段
      {
      $unwind: "$host_details" // 将`host_details`数组拆分为多条独立的文档,每条包含一个应用和一个主机
      },

      // 阶段 4: $project - 重塑文档结构
      {
      $project: { // 定义输出文档的字段
      "_id": 0, // 不显示原始的_id字段(0为排除)
      "application": "$app_name", // 将app_name重命名为application
      "team": "$owner_team", // 显示负责团队
      "criticality": 1, // 显示应用关键程度(1为包含)
      // 从关联的主机文档中提取信息
      "hostname": "$host_details.hostname", // 显示主机名
      "ip_address": "$host_details.ip_address", // 显示IP地址
      "cpu_cores": "$host_details.specs.cpu_cores", // 显示CPU核心数
      "memory_gb": "$host_details.specs.memory_gb", // 显示内存大小
      "disk_gb": "$host_details.specs.disk_gb" // 显示磁盘大小
      }
      },

      // 阶段 5: $sort - 结果排序
      {
      $sort: { // 排序条件
      "team": 1, // 首先按团队名称升序排列 (1: 升序, -1: 降序)
      "criticality": -1, // 其次按应用关键程度降序排列 (high比medium优先级高)
      "memory_gb": -1 // 关键程度相同时,按主机内存降序排列
      }
      }
      ])
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      // 应用文档示例
      {
      "_id": ObjectId("67a1b2c3d4e5f6a1b2c3d4e5"),
      "app_name": "订单服务",
      "environment": "production", // 环境: production, staging, development
      "owner_team": "order-team",
      "criticality": "high"
      }
      // 主机文档示例
      {
      "_id": ObjectId("77a1b2c3d4e5f6a1b2c3d4e5"),
      "hostname": "prod-order-01",
      "ip_address": "192.168.1.100",
      "environment": "production",
      "specs": {
      "cpu_cores": 8,
      "memory_gb": 32,
      "disk_gb": 500
      },
      "running_apps": ["订单服务", "用户服务"] // 在此主机上运行的应用名称数组
      }
      // 返回结果:完全重新组织的数据
      [
      {
      "application": "订单服务",
      "team": "order-team",
      "criticality": "high",
      "hostname": "prod-order-01",
      "ip_address": "192.168.1.100",
      "cpu_cores": 8,
      "memory_gb": 32,
      "disk_gb": 500
      },
      {
      "application": "支付网关",
      "team": "payment-team",
      "criticality": "critical",
      "hostname": "prod-payment-01",
      "ip_address": "192.168.1.101",
      "cpu_cores": 16,
      "memory_gb": 64,
      "disk_gb": 1000
      }
      // ... 其他结果
      ]
    4. CMDB 应用场景
      问题1:”显示每个团队负责的主机数量,并按环境分别统计”
      1
      2
      3
      4
      5
      6
      7
      8
      db.hosts.aggregate([
      { $match: { status: "running" } },
      { $group: {
      _id: { team: "$owner_team", env: "$environment" },
      host_count: { $sum: 1 }
      }},
      { $sort: { host_count: -1 } }
      ])
      问题2:”找出生产环境中,哪些应用依赖的主机平均内存使用率超过80%”
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      db.applications.aggregate([
      { $match: { environment: "production" } },
      { $lookup: {
      from: "hosts",
      localField: "dependent_hosts",
      foreignField: "hostname",
      as: "host_details"
      }},
      { $project: {
      app_name: 1,
      avg_memory_usage: { $avg: "$host_details.memory_usage_percent" }
      }},
      { $match: { avg_memory_usage: { $gt: 80 } } }
      ])

索引策略

  1. 索引类型与应用场景:
  • 单字段索引:db.hosts.createIndex({ hostname: 1 })
  • 复合索引:db.hosts.createIndex({ environment: 1, status: 1 })
  • 多键索引:数组字段索引 db.hosts.createIndex({ tags: 1 })
  • 文本索引:全文搜索 db.hosts.createIndex({ description: "text" })
  • TTL索引:自动过期数据 db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
  1. 索引优化原则:
  • ESR规则:Equality(等值) → Sort(排序) → Range(范围)
  • 覆盖查询:索引包含所有查询字段,避免回表
  • 索引交集:MongoDB可自动组合多个索引

高可用与扩展

1. 复制集(Replica Set)深度

  • 架构组成
    1
    2
    3
    4
    5
    Primary(主节点)← 读写操作
    ↓ 数据同步
    Secondary-1(从节点)← 只读查询
    Secondary-2(从节点)← 只读查询
    Arbiter(仲裁节点)← 仅参与选举
  • 配置示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 复制集配置
    cfg = {
    _id: "cmdb-rs",
    members: [
    {_id: 0, host: "mongo1:27017", priority: 2},
    {_id: 1, host: "mongo2:27017", priority: 1},
    {_id: 2, host: "mongo3:27017", priority: 1, arbiterOnly: true}
    ]
    }
    rs.initiate(cfg)
  • 读写关注
    1
    2
    3
    4
    5
    6
    7
    // 强一致性写操作
    db.hosts.insertOne(doc, {
    writeConcern: { w: "majority", wtimeout: 5000 }
    })

    // 从节点读取(最终一致性)
    db.hosts.find().readPref("secondary")

2. 分片集群(Sharded Cluster)

  • 分片策略选择
    • 基于范围分片:适合范围查询,可能产生热点
    • 基于哈希分片:数据分布均匀,范围查询效率低
    • 区域分片:基于标签的智能数据分布
  • 分片键选择考量
    1
    2
    3
    4
    5
    // 好的分片键:基数高、分布均匀
    sh.shardCollection("cmdb.hosts", { "tenant_id": 1, "hostname": 1 })

    // 避免的选择:低基数、单调递增
    // sh.shardCollection("cmdb.hosts", { "created_date": 1 }) // 不好!

CMDB助手查询工具设计(基于MCP)

  • MCP工具架构设计
    1
    自然语言查询 → MCP Server → 查询解析器 → MongoDB查询生成器 → 结果格式化
  • MCP Server 核心代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    from mcp import MCPServer, Context
    import pymongo
    from typing import Dict, List, Any

    class CMDBAgentMcpServer:
    def __init__(self, mongo_uri: str):
    self.client = pymongo.MongoClient(mongo_uri)
    self.db = self.client.cmdb
    self.setup_tools()

    @mcp_tool(
    name="query_hosts",
    description="根据条件查询主机信息",
    args_schema={
    "type": "object",
    "properties": {
    "environment": {"type": "string", "enum": ["production", "staging", "development"]},
    "os_type": {"type": "string"},
    "min_memory": {"type": "integer"},
    "status": {"type": "string", "enum": ["running", "stopped", "maintenance"]},
    "tags": {"type": "array", "items": {"type": "string"}},
    "limit": {"type": "integer", "default": 10}
    }
    }
    )
    async def query_hosts(self, ctx: Context, **filters) -> List[Dict]:
    """智能主机查询工具"""
    # 构建MongoDB查询
    query = self._build_host_query(filters)

    # 执行查询
    cursor = self.db.hosts.find(query["filter"], query["projection"])
    cursor = cursor.sort(query["sort"]).limit(query["limit"])

    results = list(cursor)
    return self._format_host_results(results)

    def _build_host_query(self, filters: Dict) -> Dict:
    """将自然语言参数转换为MongoDB查询"""
    query_filter = {}

    # 环境过滤
    if filters.get("environment"):
    query_filter["environment"] = filters["environment"]

    # 操作系统过滤(支持模糊匹配)
    if filters.get("os_type"):
    query_filter["os.type"] = {"$regex": filters["os_type"], "$options": "i"}

    # 内存条件
    if filters.get("min_memory"):
    query_filter["specs.memory_gb"] = {"$gte": filters["min_memory"]}

    # 状态过滤
    if filters.get("status"):
    query_filter["status"] = filters["status"]

    # 标签查询
    if filters.get("tags"):
    query_filter["tags"] = {"$all": filters["tags"]}

    return {
    "filter": query_filter,
    "projection": {"_id": 0, "hostname": 1, "ip": 1, "environment": 1, "os": 1, "specs": 1},
    "sort": [("last_seen", -1)],
    "limit": filters.get("limit", 10)
    }

    @mcp_tool(
    name="analyze_infrastructure",
    description="基础设施统计分析",
    args_schema={
    "type": "object",
    "properties": {
    "analysis_type": {"type": "string", "enum": ["environment", "os", "team"]},
    "metric": {"type": "string", "enum": ["count", "memory", "cpu"]}
    }
    }
    )
    async def analyze_infrastructure(self, ctx: Context, analysis_type: str, metric: str = "count"):
    """聚合分析工具"""
    pipeline = self._build_analysis_pipeline(analysis_type, metric)
    results = list(self.db.hosts.aggregate(pipeline))
    return self._format_analysis_results(results, analysis_type, metric)

    # 使用示例
    server = CMDBAgentMcpServer("mongodb://localhost:27017/cmdb")
  • Agent提示词设计
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    你是一个CMDB专家,可以查询和分析基础设施信息。

    可用工具:
    1. query_hosts - 查询主机信息,支持按环境、操作系统、内存等条件过滤
    2. analyze_infrastructure - 统计分析基础设施资源分布

    示例交互:
    用户:列出生产环境中所有运行CentOS的主机
    AI:我将使用query_hosts工具查询...
    工具调用:query_hosts(environment="production", os_type="CentOS")

    用户:按环境统计主机数量
    AI:使用analyze_infrastructure进行分析...
    工具调用:analyze_infrastructure(analysis_type="environment", metric="count")
  • 高级查询特性支持
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    # 支持复杂查询的扩展工具
    @mcp_tool(
    name="advanced_host_search",
    description="高级主机搜索,支持复杂条件组合"
    )
    async def advanced_search(self, ctx: Context, search_expression: str):
    """支持自然语言复杂查询"""
    # 使用LLM解析复杂查询条件
    parsed_query = await self.llm_parse_search(search_expression)
    return await self.execute_advanced_query(parsed_query)

    # 关系查询工具
    @mcp_tool(
    name="find_related_assets",
    description="查找相关资产和依赖关系"
    )
    async def find_related_assets(self, ctx: Context, hostname: str, relation_type: str):
    """查询资产关系"""
    pipeline = [
    {"$match": {"hostname": hostname}},
    {"$graphLookup": { # 使用图查询查找关系
    "from": "asset_relations",
    "startWith": "$_id",
    "connectFromField": "from_asset",
    "connectToField": "to_asset",
    "as": "relationships"
    }}
    ]
    return list(self.db.hosts.aggregate(pipeline))

Kubernetes

2023-07-31 10:25:07 Docker /

Docker 是一种开源平台,一种快速构建、运行和管理应用的工具。它使用容器化技术,使得应用程序及其依赖性可以打包到一个容器中,并在任何支持 Docker 的环境中运行。
1 MobarXterm 通过 SSH 连接 linux虚拟机,操作虚拟机上的 Docker。
2 Windows本地:通过wsl安装Linux发行版本,安装docker desktop(将自动在WSL中配置Docker环境,借助linux内核运行)

容器(Container)

  • 容器是一个轻量级的、可移植的、自包含的单元,包括应用程序和其所有依赖项。
  • Docker 利用容器技术,将应用程序及其依赖项打包成一个容器,确保在不同环境中的一致性运行。
  • Docker 位于容器 和 服务器-操作系统/硬件 之间,是运行容器的引擎。
  • 隔离网络、文件、进程等环境。一个容器是一个沙盒隔离环境。
  • 相对于虚拟机技术,docker 启动更快、更清量。但容器共用宿主机的内存、CPU物理资源,多容器可能存在互相抢占资源的情况。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    docker run -d \                  # 创建并运行一个容器,-d 是让容器在后台运行;同一个镜像可创建多个容器
    --name mysgl \ # 给容器起名字,必须唯一
    -p 3306:3306 \ # 设置 宿主机端口:容器端口 映射
    -e TZ=Asia/Shanghai \ # 设置环境变量
    -e MYSQL_ROOT_PASSWORD=123 \ # 指定运行的镜像名,一般为 [镜像名]:[镜像版本]
    mysql
    docker ps # 查看本地容器(运行中的)
    docker ps a # 查看所有容器 (包括未运行)
    docker start <容器ID> # 启动容器
    docker stop <容器ID> # 停止容器中的进程,容器未删除
    docker rm <容器ID> # 删除容器
    docker inspect <容器ID> # 查看容器配置信息
    docker log <容器ID> # 查看容器日志
    docker exec -it <容器ID> bash # 进入容器内部,命令行模式(容器内部模拟出一个操作系统)
    容器打包成镜像: docker commit -a "作者信息" -m "log信息" <容器ID><目标镜像名称:tag版本>
    拷贝文件到容器: docker cp <文件目录> <容器ID>:<目标目录>
    拷贝容器文件到宿主机:docker cp <容器ID>:<文件目录><宿主机目标目录>
    更新容器设置:docker update <容器ID><相关设置>

镜像(Image)

  • 镜像是一个只读的模板,包含运行应用程序所需的所有信息,包括代码、运行时、库、环境变量和配置文件。
  • 容器是通过运行镜像创建的(像光盘),本地容器是真正运行的实例。镜像是容器的模板,是从容器打包来的,可以在不同操作系统,不同服务器之间传播。
  • 1
    2
    3
    4
    5
    6
    7
    docker images                                # 查看本地镜像
    docker search <名称关键字> # 搜索镜像仓库
    docker pull <镜像名:tag版本> # 下载镜像
    docker push <镜像名:tag版本> # 上传镜像
    docker rmi <镜像名:tag版本> # 删除镜像
    docker save -o <输出文件路径><镜像名:tag版本> # 打包本地镜像文件
    docker load -i <加载文件路径 # 导入本地镜像文件

仓库(Registry)

  • 仓库是存储和组织 Docker 镜像的地方。Docker Hub 是一个常见的公共仓库,你也可以搭建私有仓库。
  • Docker 镜像可以从仓库中拉取,也可以推送到仓库。

沙箱

  • 沙箱是一种安全机制,用于隔离和限制程序或应用程序的运行环境,以防止其对系统或其他程序产生潜在的危害。沙箱技术旨在创建一个受控制的环境,使得运行在其中的代码无法直接影响到系统的其他部分。这种隔离有助于确保安全性、防止恶意软件传播,同时提供一定程度的控制和监控。
  • 容器化平台(如 Docker)使用沙箱技术来隔离容器中的应用程序,确保它们互相独立运行。

容器创建

镜像结构:入口,层,基础镜像。分层的好处是可复用,,

  • 通过命令直接创建,需要完整镜像,几个G常有,稳定。
  • 通过dockerfile创建,不需要完整镜像,更灵活。
    Dockerfile 是一个包含构建镜像步骤的文本文件,包含一个个的指令。通过编写 Dockerfile,你可以定义如何构建镜像,包括基础镜像、安装依赖、复制文件等步骤。
  • 使用 Docker 的基本步骤
    1. 安装 Docker: 根据操作系统的不同,安装适合的 Docker 版本。
    2. 创建 Dockerfile: 编写包含应用程序构建步骤的 Dockerfile。
    3. 构建镜像: 在包含 Dockerfile 的目录中运行 docker build 命令构建镜像。(如java项目还需要jar包)
    4. 运行容器: 使用 docker run 命令基于构建的镜像创建和运行容器。
    5. 发布镜像: 将构建的镜像推送到 Docker 仓库,以便其他人可以拉取使用。

数据卷

  • 数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁(两边文件同时修改)。
    (容器一般只包括支持运行的最少文件,一般无vi或其他编辑器,所以无法进入容器直接对容器中的文件进行修改)
  • 如何挂载数据卷?在创建容器时,利用-v 数据卷名:容器内目录完成挂载。创建时如果发现挂载的数据卷不存在,会自动创建。
    1
    2
    3
    4
    docker volumels        # 查看数据卷
    docker volume rm # 删除数据卷
    docker volume inspect # 查看数据卷详情
    docker volume prune # 删除未使用的数据卷

容器编排(Orchestration):运维人员

容器编排是指在生产环境中管理和协调多个容器的过程。Docker 提供了 Docker Compose 工具,用于定义和运行多容器的应用。


Kubernetes

什么是Kubernetes

Kubernetes是一个开源的容器编排引擎,可以用来管理容器化的应用,包括容器的自动化的部署、扩容、缩容、升级、回滚等等;
它是Google在2014年开源的一个项目,它的前身是Google内部的Borg系统。

Kubernetes出现之前,我们一般都是使用Docker来管理容器化的应用,但是Docker只是一个单机的容器管理工具,它只能管理单个节点上的容器,当我们的应用程序需要运行在多个节点上的时候,就需要使用一些其他的工具来管理这些节点,比如Docker Swarm、Mesos、Kubernetes等等;
这些工具都是容器编排引擎,它们可以用来管理多个节点上的容器,但是它们之间也有一些区别,比如Docker Swarm是Docker官方提供的一个容器编排引擎,它的功能比较简单,适合于一些小型的、简单的场景,而Mesos和Kubernetes则是比较复杂的容器编排引擎;
Mesos是Apache基金会的一个开源项目,而Kubernetes是Google在2014年开源的,目前已经成为了CNCF(Cloud Native Computing Foundation)的一个顶级项目,基本上已经成为了容器编排引擎的事实标准了。

Kubernetes 资源对象

Node:k8s集群节点,可以是物理机/虚拟机
Pod:k8s最小调度单元,容器(运行app/数据库/..镜像)的抽象,可以是一/多个容器的组合,但除非高度耦合,一个pod只运行一个容器
Service:将一组pod封装成一个服务并且提供统一访问入口(解决了一组数据库pod中一个重建后ip变化的问题,类似于“服务发现”)
Ingress:为了对外提供服务,将外部请求路由转发到内部集群的service上

ConfigMap:封装配置信息
Secret:封装敏感信息
其他安全机制:网络安全,访问控制,身份认证
Volumn:将数据挂在到本地磁盘或远程存储上,实现持久化存储

Deployment:部署无状态应用程序,将一/多个Pod组合到一起;冗余备份,相当于对Pod的抽象;具有副本控制、滚动更新、自动扩缩容等功能,实现应用程序的高可用
Statefulset:部署有状态应用程序,如DB、MQ、缓存以及保留会话状态的应用程序

Kubernetes 架构

分为Master和Worker节点
apiserver:位于master节点上,是k8s集群的API接口;交互方式包括 kubectl 命令行、Dashboard界面或API接口

k8s 存储

pv
pvc
lvm

k8s 调度

service
ingress

集群间dns
外部访问

kubectl常用命令

6.1 基础使用

1
2
3
4
5
6
7
8
# 查看帮助
kubectl --help

# 查看API版本
kubectl api-versions

# 查看集群信息
kubectl cluster-info

6.2 资源的创建和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建并运行一个指定的镜像
kubectl run NAME --image=image [params...]
# e.g. 创建并运行一个名字为nginx的Pod
kubectl run nginx --image=nginx

# 根据YAML配置文件或者标准输入创建资源
kubectl create RESOURCE
# e.g.
# 根据nginx.yaml配置文件创建资源
kubectl create -f nginx.yaml
# 根据URL创建资源
kubectl create -f https://k8s.io/examples/application/deployment.yaml
# 根据目录下的所有配置文件创建资源
kubectl create -f ./dir

# 通过文件名或标准输入配置资源
kubectl apply -f (-k DIRECTORY | -f FILENAME | stdin)
# e.g.
# 根据nginx.yaml配置文件创建资源
kubectl apply -f nginx.yaml

6.3 查看资源信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看集群中某一类型的资源
kubectl get RESOURCE
# 其中,RESOURCE可以是以下类型:
kubectl get pods / po # 查看Pod
kubectl get svc # 查看Service
kubectl get deploy # 查看Deployment
kubectl get rs # 查看ReplicaSet
kubectl get cm # 查看ConfigMap
kubectl get secret # 查看Secret
kubectl get ing # 查看Ingress
kubectl get pv # 查看PersistentVolume
kubectl get pvc # 查看PersistentVolumeClaim
kubectl get ns # 查看Namespace
kubectl get node # 查看Node
kubectl get all # 查看所有资源

# 后面还可以加上 -o wide 参数来查看更多信息
kubectl get pods -o wide

# 查看某一类型资源的详细信息
kubectl describe RESOURCE NAME
# e.g. 查看名字为nginx的Pod的详细信息
kubectl describe pod nginx

6.4 资源的修改、删除和清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 更新某个资源的标签
kubectl label RESOURCE NAME KEY_1=VALUE_1 ... KEY_N=VALUE_N
# e.g. 更新名字为nginx的Pod的标签
kubectl label pod nginx app=nginx

# 删除某个资源
kubectl delete RESOURCE NAME
# e.g. 删除名字为nginx的Pod
kubectl delete pod nginx

# 删除某个资源的所有实例
kubectl delete RESOURCE --all
# e.g. 删除所有Pod
kubectl delete pod --all

# 根据YAML配置文件删除资源
kubectl delete -f FILENAME
# e.g. 根据nginx.yaml配置文件删除资源
kubectl delete -f nginx.yaml

# 设置某个资源的副本数
kubectl scale --replicas=COUNT RESOURCE NAME
# e.g. 设置名字为nginx的Deployment的副本数为3
kubectl scale --replicas=3 deployment/nginx

# 根据配置文件或者标准输入替换某个资源
kubectl replace -f FILENAME
# e.g. 根据nginx.yaml配置文件替换名字为nginx的Deployment
kubectl replace -f nginx.yaml

6.5 调试和交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 进入某个Pod的容器中
kubectl exec [-it] POD [-c CONTAINER] -- COMMAND [args...]
# e.g. 进入名字为nginx的Pod的容器中,并执行/bin/bash命令
kubectl exec -it nginx -- /bin/bash

# 查看某个Pod的日志
kubectl logs [-f] [-p] [-c CONTAINER] POD [-n NAMESPACE]
# e.g. 查看名字为nginx的Pod的日志
kubectl logs nginx

# 将某个Pod的端口转发到本地
kubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]
# e.g. 将名字为nginx的Pod的80端口转发到本地的8080端口
kubectl port-forward nginx 8080:80

# 连接到现有的某个Pod(将某个Pod的标准输入输出转发到本地)
kubectl attach POD -c CONTAINER
# e.g. 将名字为nginx的Pod的标准输入输出转发到本地
kubectl attach nginx

# 运行某个Pod的命令
kubectl run NAME --image=image -- COMMAND [args...]
# e.g. 运行名字为nginx的Pod
kubectl run nginx --image=nginx -- /bin/bash

7. Portainer的安装和使用

Portainer 是一个轻量级的容器管理工具,可以用来管理Docker和Kubernetes,它提供了一个Web界面来方便我们管理容器
官方网址: https://www.portainer.io/

8. Helm的安装和使用

Helm 是一个Kubernetes的包管理工具,可以用来管理Kubernetes的应用,它提供了一个命令行工具来方便我们管理Kubernetes的应用
官方网址: https://helm.sh/

集群存储

https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/

InfluxDB

InfluxDB 是一个由InfluxData开发的开源时序型数据库,专注于海量时序数据的高性能读、高性能写、高效存储与实时分析等;
在DB-Engines Ranking时序型数据库排行榜上排名第一,广泛应用于DevOps监控、IoT监控、实时分析等场景。
https://jasper-zhang1.gitbooks.io/influxdb/content/Introduction/getting_start.html

influxdb-cluster 是InfluxDB的集群版本,InfluxDB Enterprise 的开源替代方案,设计用于大规模数据存储和高可用性需求。
可以实现数据的分片和复制,从而提高系统的可用性和扩展性。数据安全。operator缺失
https://github.com/chengshiwen/influxdb-cluster/wiki

集群体系结构

InfluxDB Enterprise由两组软件进程组成: Data 数据节点 和 Meta 元节点。集群内的通信是这样的:

influxdb使用的默认端口号为分别为用于meta集群内部服务的8091端口,meta节点通信的8089端口,data集群内部服务的8088端口,以及data节点对外提供http服务的8086端

  • Meta 节点通过 TCP 协议和 Raft 共识协议相互通信,默认都使用端口 8089,此端口必须在 Meta 节点之间是可访问的。默认 Meta 节点还将公开绑定到端口 8091 的 HTTP API,influxd-ctl 命令使用该 API。
  • Data 节点通过绑定到端口 8088 的 TCP 协议相互通信。Data 节点通过绑定到 8091 的 HTTP API 与 Meta 节点通信。这些端口必须在 Meta 节点和 Data 节点之间是可访问的。
  • 在集群内,所有 Meta 节点都必须与所有其它 Meta 节点通信。所有 Data 节点必须与所有其它 Data 节点和所有 Meta 节点通信。

Where data lives

InfluxDB 集群中,一个节点要么是专门用于存储和查询时间序列数据的数据节点,要么是专门用于存储集群元数据的元节点。数据节点负责存储实际的数据和处理查询请求,而元节点则负责管理集群的元数据,包括节点信息、数据库和保留策略等。

Meta

元节点保存以下所有元数据:

  • 集群中的所有节点及其角色
  • 集群中存在的所有数据库和保留策略
  • 所有分片和分片组,以及它们存在于哪些节点上
  • 集群用户及其权限
  • 所有连续查询

元节点将这些数据保存在磁盘上的Raft数据库中,由BoltDB提供支持。默认情况下,Raft数据库是/var/lib/influxdb/meta/raft.db。
注意:Meta节点需要/ Meta目录。

  • influxd-meta 元数据服务
    1
    2
    3
    4
    5
    # 配置文件示例(meta节点)
    [meta]
    dir = "/var/lib/influxdb/meta" # 元数据存储路径
    bind-address = ":8089" # Raft协议通信端口
    http-bind-address = ":8091" # 管理API端口
  • influxd-ctl 集群管理
    1
    2
    3
    4
    5
    6
    7
    8
    # 查看分片分布
    kubectl exec influxdb-meta-0 -- influxd-ctl show-shards
    # 强制同步分片到新节点
    kubectl exec influxdb-meta-0 -- influxd-ctl copy-shard 3 influxdb-data-0:8088
    # 节点维护操作
    influxd-ctl remove-data influxdb-data-0:8088 # 下线节点
    # 检查Meta节点Raft状态
    kubectl exec influxdb-meta-0 -- influxd-ctl raft-state

Data

数据节点保存所有原始时间序列数据和元数据,包括:

  • measurements
  • tag keys and values
  • field keys and values

在磁盘上,数据总是按照//组织。默认父目录为/var/lib/influxdb/data。注意:数据节点需要/var/lib/influxdb/的所有四个子目录,包括/meta(specifically, the clients.json file)、/data、/wal和/hh。

  • influx CLI工具
    1
    2
    3
    4
    5
    6
    7
    # 进入容器执行CLI
    kubectl exec -it influxdb-data-0 -n influxdb -- influx -username admin -password 'xxx'

    # 常用命令
    SHOW DATABASES; # 显示所有数据库
    SELECT * FROM cpu; # 查询数据
    CREATE RETENTION POLICY "1d" ON db3 DURATION 1d REPLICATION 2; # 创建保留策略
  • influxd 数据节点服务
    1
    2
    3
    4
    5
    6
    # 查看运行状态
    kubectl exec influxdb-data-0 -- ps aux | grep influxd

    # 关键参数
    -data-dir /var/lib/influxdb/data # 数据存储目录
    -wal-dir /var/lib/influxdb/wal # WAL日志目录
  • influx_inspect 数据工具
    1
    2
    3
    4
    5
    6
    7
    8
    # 导出TSM文件(需进入容器)
    kubectl exec -it influxdb-data-0 -- influx_inspect export \
    -datadir /var/lib/influxdb/data \
    -waldir /var/lib/influxdb/wal \
    -out backup.gz -compress

    # 验证数据完整性
    influx_inspect verify -dir /var/lib/influxdb/data/db3

Data 与 Meta节点交互机制

  1. 通信协议

    组件 端口 用途 协议
    Meta节点间 8089 Raft协议同步元数据 TCP
    Data节点间 8088 分片数据复制 TCP
    Data→Meta节点 8091 注册节点/获取分片元信息 HTTP
  2. 核心交互场景
    节点注册 : Data节点启动时通过HTTP API向Meta节点注册(POST /data
    分片分配 : Meta节点根据replication-factor策略分配分片到Data节点
    写入协调 : 客户端写入数据时,由Meta节点确定目标分片所在Data节点
    故障转移 : Meta节点检测Data节点离线后,自动通过Hinted Handoff机制转移副本

一个集群至少要有三个独立的元节点才能允许一个节点的丢失,如果要容忍n个节点的丢失则需要2n+1个元节点。集群的元节点的数目应该为奇数。不要是偶数元节点,因为这样在特定的配置下会导致故障。
一个集群运行只有一个数据节点,但这样数据就没有冗余了。这里的冗余通过写数据的RP中的副本个数来设置。一个集群在丢失n-1个数据节点后仍然能返回完整的数据,其中n是副本个数。为了在集群内实现最佳数据分配,我们建议数据节点的个数为偶数。

术语 / Glossary

  • measurement:描述了存在关联field中的数据的意义,measurement是字符串。作为tag,fields和time列的容器。相当于MySQL的table,关系/表的意思。单个measurement可以有不同的retention policy(即 一个measurement 中的不同 tag set 可以有不同的 retention policy,构成多组 series)
  • Continuous Query (CQ)是在数据库内部自动周期性跑着的一个InfluxQL的查询,CQs需要在SELECT语句中使用一个函数,并且一定包括一个GROUP BY time()语句。
  • Retention Policy (RP)是InfluxDB数据结构的一部分,描述了InfluxDB保存数据的长短,数据存在集群里面的副本数,以及shard group的时间范围。RPs在每个database里面是唯一的,?连同measurement和tag set定义一个series。当创建一个database时,InfluxDB会自动创建一个叫做autogen的retention policy,其duration为永远,replication factor为1,shard group的duration设为七天。
    • duration:决定InfluxDB中数据保留多长时间。在duration之前的数据会自动从database中删除掉。
    • replication factor:决定在集群模式下数据的副本的个数。InfluxDB在N个数据节点上复制数据,其中N就是replication factor。
    • shard group duration决定了每个shard group跨越多少时间。具体间隔由retention policy中的SHARD DURATION决定。例如,如果retention policy的SHARD DURATION设置为1w,则每个shard group将跨越一周,并包含时间戳在该周内的所有点。
  • series:InfluxDB数据结构的集合,一个特定的series由measurement,tag set和retention policy组成。!field set不是series的一部分
  • schema:数据在InfluxDB里面怎么组织。InfluxDB的schema的基础是database,retention policy,series,measurement,tag key,tag value以及field keys。
  • shard:包含实际的编码和压缩数据,并由磁盘上的TSM文件表示。 每个shard都属于唯一的一个shard group。多个shard可能存在于单个shard group中。每个shard包含一组特定的series。给定shard group中的给定series上的所有点将存储在磁盘上的相同shard(TSM文件)中。
  • shard group:是shard的逻辑组合。shard group由时间和retention policy组织。包含数据的每个retention policy至少包含一个关联的shard group。给定的shard group包含其覆盖的间隔的数据的所有shard。每个shard group跨越的间隔是shard的持续时间。

InfluxDB读写

  1. 命令行工具
    • influx命令行连接本地InfluxDB:直接通过InfluxDB的HTTP接口(如果没有修改,默认是8086)来和InfluxDB通信。(说明:也可以直接发送裸的HTTP请求来操作数据库,例如curl)
      1
      2
      3
      4
      $ influx -precision rfc3339
      Connected to http://localhost:8086 version 1.2.x //
      InfluxDB shell 1.2.x
      >
      InfluxDB的HTTP接口默认起在8086上,所以influx默认也是连的本地的8086端口。-precision参数表明了任何返回的时间戳的格式和精度,如 rfc3339是让InfluxDB返回RFC339格式(YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ)的时间戳。
    • 数据格式:将数据点写入InfluxDB,只需要遵守如下的行协议:
      1
      <measurement>[,<tag-key>=<tag-value>...] <field-key>=<field-value>[,<field2-key>=<field2-value>...] [unix-nano-timestamp]
      InfluxDB里存储的数据被称为时间序列数据,其包含一个数值。时序数据有零个或多个数据点,每一个都是一个指标值。数据点包括time(一个时间戳),measurement(例如cpu_load),至少一个k-v格式的field(也即指标的数值例如 “value=0.64”或者“temperature=21.2”),零个或多个tag,其一般是对于这个指标值的元数据(例如“host=server01”, “region=EMEA”)。
      可以将measurement类比于SQL里的table,其主键索引总是时间戳。tag和field是在table里的其他列,tag是被索引起来的,field没有。不同之处在于InfluxDB里,你可以有几百万的measurements,不用事先定义数据的scheme,且null值不会被存储。
    • 使用CLI插入单条的时间序列数据到InfluxDB中,用INSERT后跟数据点:
      1
      2
      3
      > use testdb
      Using database testdb
      > INSERT cpu,host=serverA,region=us_west value=0.64
      这样一个measurement为cpu,tag是host和region,value值为0.64的数据点被写入了InfluxDB中。
    • 现在我们查出写入的这笔数据:
      1
      2
      3
      4
      5
      6
      7
      > SELECT "host", "region", "value" FROM "cpu"
      name: cpu
      ---------
      time host region value
      2015-10-21T19:28:07.580664347Z serverA us_west 0.64

      > delete FROM "cpu" WHERE "host" = 'serverA' # 不带where将删除measurement所有数据
      我们在写入的时候没有包含时间戳,当没有带时间戳的时候,InfluxDB会自动添加本地的当前时间作为它的时间戳。
  2. HTTP 请求
    • 写数据
      1
      curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000'
    • 读数据
      1
      curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=mydb" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'"
  3. 客户端库,InfluxDB 提供了多种编程语言的客户端库,如Python、Go、Java等,可以方便地在应用程序中读写数据。
  4. 采样和数据保留 https://jasper-zhang1.gitbooks.io/influxdb/content/Guide/downsampling_and_retention.html

集群读写

  • 分片Shard:InfluxDB集群读写的基本单位分片是时间序列数据的物理存储单位,每个分片包含一段时间范围内的数据。
    复制因子为X,则在每个分片的数据同步到X个节点(部署influxdb的主机)上。
    分片的划分依据是时间范围和数据的存储策略(Retention Policy)。在集群环境中,分片可以分布在不同的节点上,以实现数据的分布式存储和负载均衡。这样可以提高数据的读写性能和系统的可扩展性。
    确定数据属于哪个分片的过程主要涉及以下几个步骤:数据写入时,首先根据时间戳确定属于哪个Shard Group(分片组)。然后,基于Measurement和Tag的值计算哈希值。最后,根据哈希值将数据分配到具体的分片。

    shard := shardGroup.shards[fnv.New64a(key) % len(shardGroup.Shards)]

  • 分片组Shard groups:集群在一个分片组内创建分片,以最大限度地利用数据节点的数量。
    分片数计算:当集群有 N 个数据节点且副本因子为 X 时,每个分片组中会创建 floor(N/X) 个分片(向下取整)
    示例:若集群有 4 个数据节点,副本因子为 2,则每个分片组包含 4/2=2 个分片(每个分片在 2 个节点上复制)
  • 集群写入:
    假设一个HTTP写操作被发送到服务器D,数据属于分片1。写操作需要被复制到分片1的所有者:数据节点A和B。当写操作进入D时,该节点从其亚转移的本地缓存中确定需要将写操作复制到A和B,并立即尝试对两者进行写操作。
    每个对HTTP API的请求都可以通过一致性查询参数指定一致性级别。 https://docs.influxdata.com/enterprise_influxdb/v1/concepts/clustering/#write-consistency
  • 集群查询:
    根据查询的时间段和数据的复制因子进行分布的。例如,如果保留策略的复制因子为4,则接收查询的协调数据节点将随机选择存储该分片副本的4个数据节点中的任何一个来接收查询。如果我们假设系统的分片持续时间为一天,那么对于查询覆盖的每一天,协调节点都会选择一个数据节点来接收当天的查询。
    协调节点尽可能在本地执行和完成查询。如果一个查询必须扫描多个shard组(在上面的例子中是多个天),协调节点将查询转发给其他节点,以查找本地没有的shard。查询与扫描自己的本地数据并行转发。查询被分发到尽可能多的节点,以查询每个分片组一次。当结果从每个数据节点返回时,协调数据节点将它们组合成返回给用户的最终结果。
  • Shard Group 与 Shard 的实战示例:
    1. 场景描述,假设有以下配置:
      Retention Policy: Duration: 30d | Replication Factor: 2 | Shard Group Duration: 1d,
      集群数据节点: 4 个(A/B/C/D)
    2. 分片组创建
      时间划分:每天 00:00 自动创建新的分片组(如 2025-04-02 ~ 2025-04-03)
      分片数:4/2=2 个分片(Shard 1 & 2)
    3. 分片分布
      分片 1 | 副本节点 A, B | 存储内容 所有哈希值模2=0的 Series Key 数据
      分片 2 | 副本节点 C, D | 存储内容 所有哈希值模2=1的 Series Key 数据
    4. 数据写入示例
      当写入 cpu,host=svr1 usage=80:Series Key = cpu,host=svr1,哈希值模2=1 ⇒ 分片2,数据同时写入节点 C 和 D
    5. 数据查询流程
      查询 SELECT * FROM cpu WHERE time > '2025-04-02':定位到 2025-04-02 分片组, 协调节点同时向 A/B(分片1)和 C/D(分片2)发起查询, 合并结果后返回

集群部署

Docker安装操作单例InfluxDB https://www.cnblogs.com/nhdlb/p/16409849.html
Docker快速开始集群InfluxDB https://github.com/chengshiwen/influxdb-cluster/wiki#docker-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B

在使用容器多节点部署InfluxDB时,数据库、容器、Docker、主机和Kubernetes(k8s)之间的关系可以理解如下:

  • 数据库(InfluxDB):InfluxDB是一个时序数据库,用于存储和查询时间序列数据。在多节点部署中,InfluxDB可以运行在多个容器中,以实现高可用性和负载均衡。
  • 容器:容器是一个轻量级、独立的运行环境,用于打包和运行应用程序及其依赖项。InfluxDB可以被打包成一个容器镜像,并在多个容器实例中运行。
  • Docker:Docker是一个容器化平台,用于创建、部署和管理容器。Docker负责启动和管理运行InfluxDB的容器。
  • 主机:主机是运行Docker和容器的物理或虚拟机器。在多节点部署中,可能有多个主机,每个主机上运行一个或多个InfluxDB容器。
  • Kubernetes(k8s):一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。Kubernetes可以管理多个主机上的容器,提供服务发现、负载均衡、自动扩展和自愈能力。在多节点部署中,Kubernetes可以管理InfluxDB容器的部署,确保它们在多个节点上运行,并提供高可用性和扩展性。
  • 关系总结:InfluxDB 作为数据库运行在 容器 中。容器 由 Docker 创建和管理。Docker 运行在 主机 上。Kubernetes 管理多个 主机 上的 Docker 容器,提供编排和管理功能。通过这种方式,InfluxDB可以在一个分布式环境中高效运行,利用Kubernetes的编排能力实现自动化管理和扩展。

Kubernetes 存储与 InfluxDB Shard 的关系解析

  1. PV/PVC:是 Kubernetes 管理存储资源的抽象层。PV 描述物理存储资源(如 NFS、云盘等),PVC 是 Pod 对存储资源的请求声明。PVC 绑定到 PV 后,Pod 通过挂载 PVC 使用持久化存储。
  2. Shard:是 InfluxDB 存储引擎的物理存储单元,表现为磁盘上的 TSM 文件(Time-Structured Merge Tree),每个 Shard 对应一个时间范围内的时序数据块。Shard 的存储路径通常位于 PVC 挂载的 /var/lib/influxdb/data 目录下。
    每个 Shard 包含:时间序列索引(.tsi 文件),压缩后的时序数据块(.tsm 文件),WAL(Write-Ahead Log)日志文件(.wal) 其路径结构为:/var/lib/influxdb/data/<database>/<retention_policy>/<shard_id>
  3. 重建 PVC 导致数据丢失的本质问题
    PVC 删除与 PV 回收策略:若 PVC 的回收策略为 Delete(默认),删除 PVC 会导致 Kubernetes 清理其绑定的 PV 及底层存储数据(如 NFS 目录、云盘等)。此时 /var/lib/influxdb 下的 datameta 目录被清空,导致 Shard 文件丢失。
    • Shard 文件丢失会导致对应时间范围的时序数据不可查询,触发 ERR: shard not found 错误。
    • Meta 文件丢失会破坏集群元数据一致性,导致用户权限、分片策略等配置失效。

InfluxDB 备份与恢复

https://docs.influxdata.com/enterprise_influxdb/v1/administration/backup-and-restore/
https://blog.csdn.net/weixin_46560589/article/details/127748939

InfluxDB Enterprise支持在集群实例、单个数据库和保留策略以及单个分片中备份和恢复数据。

  1. 备份整个实例,即所有数据库(全量备份)。命令如下:
    1
    influxd backup -portable /path/to/backup
  2. 备份单个数据库
    1
    influxd backup -portable -database <database_name> /path/to/backup
  3. 增量备份:对于较大的数据集,可以进行增量备份,只备份自上次全量或增量备份以来的数据
    1
    influxd backup -portable -start <timestamp> /path/to/backup

备份的数据可以恢复到新实例或现有实例中。

  1. 恢复整个实例,包括所有的数据库。命令如下:
    1
    influxd restore -portable /path/to/backup
  2. 恢复单个数据库
    1
    influxd restore -portable -db <database_name> /path/to/backup
  3. 有时你可能希望将备份的数据恢复到另一个数据库,可以使用 -newdb 选项来实现:
    1
    influxd restore -portable -db <old_database_name> -newdb <new_database_name> /path/to/backup

导出和导入数据

对于大多数InfluxDB Enterprise应用程序,备份和恢复实用程序提供了备份和恢复策略所需的工具。但是,在某些情况下,标准备份和恢复实用程序可能无法充分处理应用程序中的大量数据。作为标准备份和恢复实用程序的替代方案,可以使用InfluxDB influx_inspect export和涌入-import命令为灾难恢复和备份策略创建备份和恢复过程。

  1. 数据库导出:容器层面命令,指定 数据文件和 写前日志(WAL)文件的存储目录,将指定数据库中指定时间的数据导出到指定文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_test01_dump_out" -database "test01" -start "2024-10-22T00:00:00Z"
    writing out tsm file data for test01/autogen...complete.
    writing out wal file data for test01/autogen...complete.
    root@influxdb-e2cb6c913a191e56c134e-data-0:/# cat influxdb_test01_dump_out
    # INFLUXDB EXPORT: 2024-10-22T00:00:00Z - 2262-04-11T23:47:16Z
    # DDL
    CREATE DATABASE test01 WITH NAME autogen
    # DML
    # CONTEXT-DATABASE:test01
    # CONTEXT-RETENTION-POLICY:autogen
    # writing tsm data
    temp,location=room1 value=24.5 1729589246099070937
    temp,location=room2 value=22.5 1729589253425715740
    temp,location=room3 value=22 1729649010554978701
    # writing wal data
  2. 数据库导入:容器层面执行命令,使用admin账号,指定文件、数据库、时间戳精度
    1
    2
    3
    4
    root@influxdb-e73f149ff7192bd87d190-data-1:/# influx -import -path='influxdb_test01_dump_out' -precision=ns -username='' -password=''
    2024/10/25 03:21:47 Processed 1 commands
    2024/10/25 03:21:47 Processed 2 inserts
    2024/10/25 03:21:47 Failed 0 inserts
  3. 实例导出:把influxdb集群实例中所有数据库的数据导出,不加 -database,加 -compress
  4. 实例导入:加-compressed 导入压缩文件,本质上是先解压后倒入

InfluxDB节点迁移

Data节点迁移方案评审:先迁移后逐个恢复分片数据。已验证在分片副本大小70M、写入数据达2000point/s的情况下直接copy-shard会导致增量数据丢失,考虑在copy-shard前先执行truncate-shards截断热分片(集群中所有写入最新数据的分片,截断后关闭写入,变成冷分片),并在所有Data节点上创建该分片的新热分片副本,也就是在迁移节点上恢复了全部原有分片的新热分片副本,最新数据写入这个副本,然后再逐个从健康节点上的冷分片副本copy-shard恢复出分片的历史数据(迁移前分片副本原有的数据&迁移过程中未能写入的数据),该分片数据完全恢复;自测符合预期

  1. 在做迁移操作前,先记录迁移节点拥有的分片副本,后续从健康节点的相同副本中恢复出来;
    1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl show-shards # 或influx命令行执行show shards
  2. 迁移后更新节点/分片元信息,新data-0节点丢失db1的shard3,另外_internal的db1转移到健康节点data-1上了
    1
    2
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl remove-data influxdb-xx-data-0.influxdb-xx-data:8088
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl add-data influxdb-xx-data-0.influxdb-xx-data:8088
  3. 持续写入数据到db1(只能写入健康节点上的db1分片副本),某一时刻执行truncate-shards,db1的shard3切断,新热分片shard4的分片副本分配到data-1以及迁移后的data-0中,此刻开始写入db1的新数据在data-0和data-1上的分片中一致(一个数据库的分片可能有多个,但只有一个正在写入,其他都是冷分片)
    1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl truncate-shards
  4. 最后再恢复历史数据,也就是执行迁移前的data-0节点拥有的分片副本数据,以及迁移完成前应该写入但没有写入data-0的数据。从健康的data-1上的分片副本copy-shard而来,导出文件可见data-0和data-1上的db1数据完全一致
    1
    2
    3
    # 对于_internal分片的转移 先copy后remove
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl copy-shard influxdb-xx-data-1.influxdb-xx-data:8088 influxdb-xx-data-0.influxdb-xx-data:8088 1
    kubectl exec -i influxdb-xx-meta-0 -n influxdb -- influxd-ctl remove-shard influxdb-xx-data-1.influxdb-xx-data:8088 1
    1
    2
    3
    # 分片的物理文件 wal&tsm
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- ls /var/lib/influxdb/data
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- ls /var/lib/influxdb/wal
    1
    2
    3
    # 可能要等wal落tsm
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influxdb/wal" -out "influxdb_dump_out" -database "db1"
    kubectl exec -i influxdb-xx-data-0 -n influxdb -- md5sum influxdb_dump_out