美 的 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 → IXSCAN,docsExamined: 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.xml 中 elasticsearch 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 java 或 jps -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 -version 和 mvn -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 对应一类能力:email、file、baidu-drive、webdav… 核心:定义统一的输入输出格式 ,模型只跟 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 Code 是 Anthropic 出品的 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=xxx、GROUP=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 —— 线程池使用规范

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

      判断标准:只要两类任务的执行时间量级不同(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高,则使用jstat、jmap排查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

9.19 AI Hot 0919

  1. Agent 是什么?Agent = LLM + Harness,我的理解是大模型的思考能力驱动智能体真正的完成一些人能做的事情。首先要能将手工操作拆分为可复现?的任务,更加适合完成重复性工作。
  2. Harness 是什么?Agent 中除了大模型,其余的影响到其稳定交付效果的内容,如会话管理、工具编排、失败重试、熔断机制。。从 AI 工程的发展来说,一开始对于一些短链路、单轮次的任务,做好提示词优化,“把任务讲清楚”,明显改变模型的输出效果;但随着 Agent 火起来,真正进入环境里做事,能够调 API、查数据,提示词工程(PE)达到了上限,转向上下文工程(CE),核心是 “要让模型在正确的时间拿到正确的信息”,包含所有会影响模型当前决策的信息总和,历史会话、背景上下文、用户输入、系统规则、过程中调用工具的返回、检索知识库结果等等,Prompt 只是 Context 的一部分,Context 是输入环境的工程化;随着 AI 更多的承担长链路任务,其工作的稳定性成为关键点,Harness Engineering(HE)解决的是 “让模型在真实执行中持续做对”,不仅要提供环境让 Agent 把任务做起来,还要持续盯着执行过程、检查最终结果、跑偏了要纠正、失败了能恢复。
  3. Langgraph → Agent?Graph/Chain 是节点和路由编排的 workflow,本质上是业务逻辑的 if-else 实现,但在 Agent 的场景下,原来复杂的“状态机
    被模型接管了,变成一个基于 ReAct 的 loop(原来的流程图坍缩为一个model-execute),Agent 基于 Prompt+Skills+Reasoning-Acting 自行调度拆解任务+逐步执行+分析结果(for 循环),自由度更高,而 Langgraph 稳定性更高,生产仍然可用于安全约束、审计校验等,但也存在问题(业务复杂导致节点难以维护,固定走全流程可能导致效率低)
  4. Multi-Agent 架构?适用于:一个复杂任务里存在多个相对独立的认知职责,这些职责需要不同的Context、工具、权限、目标或评测标准,且它们之间还需要协作;否则,就算是繁杂但场景类似的长任务,也可以通过一个 Agent+workflow+tool 实现。这样,可以避免不同环节、差异很大的 Context 堆积在一起,影响其中单 Agent 生成;避免不同的 tools、权限堆积,影响单 Agent 判断使用;可以实现没有前后依赖的 Agent 的并行 work;可以做到不同 Agent 的独立评测和迭代。
  5. 通用 Agent / 个人工作 Agent 化?我在 Codex 里有一个 career-os 项目,能够评价当前简历,沉淀个人求职能力事实,根据 JD 适配微调 cv,定时拉取招聘信息。。
  6. Skills?个人/团队校验的抽象,帮助 AI 从通用型人才 → 专家型人才,披露式的 md文件,大模型自己判断需不需要加载。自己梳理的:纯知识类(CMDB平台答疑自助,内网 IT 排障指引),工具使用类(问数相关。。)
  7. MCP / Tools?支持 AI 调用工具与外部交互。
  8. LLM。预训练(数据集→文字token化→训练(调整模型参数使其”学会下一个token是什么”)→得到基础模型(输入词→神经网络→下一个词))→ 后训练→ 强化学习
  9. RAG?帮助 AI 补充内部知识到上下文。
  10. 智能问数。nl2sql 帮助用户通过对话快速查询企业数据;引入新技术,实现旧系统重构,简单;实际落地,系统性工程,难。不仅要把已跑通、可复用的人工查数流程(澄清数据关系,统一查数口径,元数据治理,,这些规则约束,与“智能”无关的脏活累活才是关键),拆分成 AI 可理解、可执行的任务步骤,还要考虑失败重试、结果评价、、

9.20 CMDB-Agent 智能问数三次迭代演进与技术能力体系梳理

整体背景:从原有基于阿里 DataAgent 的静态流程问数项目切入,伴随对 Agent 范式、运行时架构、多 Agent 分层的逐步深入,完成三次架构迭代;核心目标是从「配置化流程工具」走向「工程化 Agent 运行时」,强化 AI Agent 的工程设计与落地能力,而非单纯优化问答效果。

一、第一代(问数 1.0):静态 Workflow 模式/静态 DAG/Graph 流程式 Agent(阿里 DataAgent 改造)

  • 架构形态:人工预先定义完整业务 DAG 有向无环图,固定节点与跳转路由,本质是人编排流程,大模型只负责节点内单一任务。
  • 本质定位:核心是人工编排的有向工作流(DAG Workflow),不是自驱动 Agent。大模型是每个节点的“执行器”,不是“决策者”。只有纯资产数据查询能力,整条链路强制走完:意图匹配 → 概念别名映射 → 表检索 → SQL生成 → SQL执行。
  • 知识库构建
    知识类型 规模 最佳实现方式 是否需要RAG
    概念型知识(别名映射:小机=主机) 几百条 后端内存词典 + 字符串匹配 ❌ 完全不需要
    枚举型知识(系统枚举:MOPS、CMDB 等系统名、产品名) 几万条 结构化模糊查询接口(like/分词匹配) ❌ 不需要向量RAG
    1. 两类知识完全没必要上向量 RAG:
      • 概念型是精确同义词,词典匹配确定性最高、不耗 token、零延迟;用 RAG 反而会出现召回漏项、语义漂移。
      • 枚举型是结构化数据,适合做数据库层面的模糊关键词检索,向量 Embedding 反而丢失精确匹配能力。
    2. 可以放在同一个「知识澄清 Skill」里,对外只暴露一个工具入口,内部自动走不同匹配逻辑:
      • 先匹配概念词典,做术语标准化;
      • 再匹配枚举库,补全实体全称/对应表;
      • 统一返回给大模型标准化后的业务术语。
      • 好处:大模型只需要调用一个工具就能完成知识澄清,减少工具选择成本,避免乱调用。
  • Workflow 模式的痛点
    1. 全链路执行成本高:无论问题多简单,都要跑完所有节点,排查问题要逐节点核对,调一次 prompt 半小时。
    2. 扩展成本高:新增一类能力就要加节点、改路由,侵入主流程。
    3. 混合诉求处理能力弱:用户一句话同时包含答疑+查询,固定分支很难流畅衔接。
    4. 优点也很明显:可控性极强,调优到位后效果上限很高,这也是你人工调完效果不错的原因。
    5. 不是要无脑抛弃 workflow,想清楚再上 Agent。。

二、第二代(问数 2.0):基于 Harness 运行时的 ReAct 自驱动 Agent

当前工作的核心:用内部 Harness 平台做运行时底座,把人工 Workflow 改成大模型动态调度的 ReAct 循环。不是重新发明能力,而是把原有能力重新封装,交给 AI 按需调用。

ps:以下内容还处于设计阶段,未评估,未落地!!

  1. 核心设计思想

    • 底层:Harness 运行时统一提供会话管理、重试、熔断、循环控制、状态持久化。
    • 能力层:把原 Workflow 里的每个能力,从「固定节点」改造为「可被动态调用的 Skill」。
    • 决策层:大模型根据用户问题,自主判断要不要调用知识、要不要查表结构、要不要执行 SQL,走一步看一步。
    • 约束层:通过系统提示词 + Skill 描述 + 运行时边界,三层规训 AI 行为,防止乱调用、死循环。
    • 核心范式转变 ➡️ 从「人定流程」转向「大模型动态决策流程」,底层基于 ReAct(Reason 思考 → Act 调用工具 → Observe 观察结果)循环范式;平台提供类 Harness 运行时底座,统一管控会话、容错、状态,业务只注册 Skill。
    • 解决的核心问题 ➡️
      1. 按需调用工具:纯答疑直接调用知识库结束,不触发 SQL 全链路,降低无效调用与 token 消耗。
      2. 天然支持混合诉求:可以先调用知识库解答权限问题,再继续调用查询工具完成数据检索,不需要预先配置分支。
      3. 扩展成本大幅降低:新增业务能力只需要注册新 Skill、补充提示词,不需要修改主执行流程
  2. 完整运转逻辑(ReAct 循环)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    用户输入 → 进入 ReAct 循环
    ↓
    大模型 Reason 思考:
    ① 这是什么类型的问题?(答疑 / 数据查询 / 混合)
    ② 当前信息够不够?要不要调用工具?
    ③ 该调用哪个工具?传什么参数?
    ↓
    调用对应 Skill → Skill 内部调用底层 MCP → 返回结果
    ↓
    结果写入会话上下文 → 再次交给大模型思考
    ↓
    大模型判断:信息足够 → 输出最终答案;信息不足 → 继续循环
    ↓
    运行时兜底:达到最大循环次数强制终止
  3. 系统提示词完整设计(直接可用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    你是 CMDB 智能问数助手,负责解答 CMDB 平台使用问题,以及查询 CMDB 内的资产、配置、系统、部门等数据。

    【意图分类与处理原则】
    1. 平台使用类问题(权限申请、查不到数据、操作报错、字段含义、使用方法):
    - 调用 knowledge_clarify 工具获取知识库答案,整理后直接回复用户,不需要查询数据库。
    2. 资产数据查询类问题(查设备、配置、系统、统计):
    - 不确定业务术语对应关系时,先调用 knowledge_clarify 做概念对齐。
    - 不清楚表结构、字段含义、表关联关系时,必须调用 get_table_schema 获取元数据。
    - 生成 SQL 后调用 execute_sql 执行查询,拿到结果后整理返回。
    3. 混合类问题(既有疑问又要查数据):先解答疑问,再执行数据查询。

    【工具使用规则】
    - 禁止编造表名、字段名、关联关系,所有表结构信息必须来自 get_table_schema。
    - 禁止编造运维答案,所有答疑内容必须来自 knowledge_clarify 返回结果。
    - 只允许生成 SELECT 查询语句,禁止任何修改、删除、建表操作。
    - 一次只调用一个工具,拿到结果后再决定下一步。

    【输出要求】
    - 数据查询结果用清晰的表格或条目呈现。
    - 查询不到数据时,说明可能原因,引导用户核对条件或权限。
    - 遇到无法解决的问题,引导用户联系管理员。
  4. MCP 原子能力层设计(按能力域拆分,不按表拆分)
    底层 MCP 只做原子能力,不带业务语义,是 Skill 的执行底座。

    MCP 名称 能力说明 入参 核心职责
    mcp_knowledge_dict 知识查询底层接口 query 内部同时处理概念词典匹配 + 枚举值模糊查询,统一返回标准化结果
    mcp_get_schema 表元数据查询接口 table_name(可选) 返回表用途、字段、注释、表与表之间的外键关联关系
    mcp_execute_sql SQL 执行接口 sql 底层做安全校验(AST 解析、白名单、权限、行数限制),执行查询返回结果
    mcp_qa_search 运维长文档知识库 query 向量检索运维手册、故障排查、权限流程等长文本答案

关键:查询类就一个通用 SQL 执行 MCP,所有单表、联表、统计都走它,联表逻辑交给大模型根据 schema 里的关联信息生成 SQL,数据库原生做 join。绝对不要一张表一个 MCP,也不要做“资产大类万能查询接口”。

  1. Skill 业务能力层设计(对大模型友好的语义封装)
    Skill = 给大模型看的「工具说明书」 + 入参校验 + 异常兜底 + 调用 MCP。
    Skill 名称 给大模型的功能描述 绑定 MCP 使用场景
    knowledge_clarify 业务概念澄清与运维知识问答。当用户使用口语化术语、询问字段含义、平台使用问题、权限问题、报错排查时调用。 mcp_knowledge_dict + mcp_qa_search 答疑、术语对齐、概念澄清
    get_table_schema 获取 CMDB 表的元数据信息,包括表用途、字段说明、表之间的关联关系。不确定表结构、字段、关联时必须调用。 mcp_get_schema 数据查询前的表信息获取
    execute_cmdb_sql 执行 CMDB 数据查询 SQL,只支持 SELECT 语句。已经确认表结构、生成合法查询语句后调用。 mcp_execute_sql 最终数据查询执行

设计原则:Skill 数量尽量少而精,3 个核心工具足够。工具越多,大模型选错的概率越高。

  1. 关键分流逻辑:不用人工写 if-else 分支,靠三层约束自然分流

    1. 系统提示词明确定义:什么场景用什么工具,什么场景不要用什么工具。
    2. Skill 描述强化边界:每个 Skill 的描述里明确写“适用场景”和“不适用场景”。
    3. 运行时反馈修正:如果大模型调错工具,Skill 返回明确的错误提示(“这是数据查询工具,答疑请使用 knowledge_clarify”),大模型拿到报错会自动修正,重新调用正确工具。
  2. 安全与兜底设计

    1. 运行时层:最大循环次数(比如 8 次)、单工具调用超时、整体会话超时。
    2. Skill 层:入参校验、异常捕获、友好错误返回。
    3. MCP 层:SQL 语法白名单、表权限、行级权限、返回行数上限、执行超时、全量日志审计。

三、第三代(3.0):手搓轻量化运行时(个人技术沉淀,非生产核心卖点)

  • 定位
    • 不是生产项目的迭代方向,是个人技术深化的 Demo。
    • 目标:把第二代 Harness 平台封装好的运行时能力,通过 Java 显式手搓实现一遍,彻底理解 Agent 运行时的底层原理。
    • 附加亮点:在里面实现多专家子 Agent 串行流水线 + 模型分层调度(主 Agent 用 Pro,子 Agent 用 Flash),作为个人技术认知的差异化亮点。
  • 和第二代的关系
    • 第二代:生产落地,用公司内部 Harness 平台,重点是业务设计、Skill 封装、提示词工程。
    • 第三代:个人复刻,手搓迷你 Harness,重点是理解会话管理、ReAct 循环、子 Session 隔离、熔断重试这些运行时内核。
  • 核心实现模块
    1. Session 抽象 + Redis 持久化,支持父/子 Session 上下文隔离
    2. Skill 接口定义 + 注册调度机制
    3. ReAct 主循环实现
    4. 重试、熔断、最大循环次数兜底
    5. 主-子 Agent 串行调度:主 Agent 统筹,SQL 生成、SQL 审查各为独立子 Agent

四、下一步落地建议(先聚焦第二代生产重构)

  1. 先跑最小闭环:先上线 3 个核心 Skill + 一套系统提示词,跑通“纯答疑”“纯查询”“混合问题”三类典型 case。
  2. 和第一代做对比验证:拿同一批问题,对比两代的:工具调用次数、响应时长、问题解决率、迭代成本。用数据证明 ReAct 模式在开放输入场景下的优势。
  3. 再和团队对齐粒度:重点对齐 MCP 安全策略、SQL 执行权限、表元数据规范,这三个是生产落地的核心卡点。
  4. 业余时间做第三代手搓 Demo:不影响主业的前提下,把运行时内核撸一遍,形成自己的 Agent 工程化认知。