一些八股概念辨析
mcp与skill
如果把mcp比作车子,那么skill就是如何骑车
Function Calling(函数调用) 是大模型的一项能力,指大语言模型根据用户提问,主动识别意图、选择并调用外部工具 / 预设函数,把自然语言指令转为结构化函数调用请求,执行后再整合结果返回给用户。
Function Calling 是底层通信能力 / 协议,Skill 是上层封装的业务能力 / 功能集合,二者是「实现方式」和「业务载体」的关系。
1. Function Calling(函数调用 / 工具调用)
本质:技术协议、调用机制
作用:让大模型按约定格式(JSON)输出函数名 + 参数,对接外部代码 / 接口,是通用底层能力。
粒度:最小执行单元 = 单个函数 / 接口。
范围:纯技术层,和业务无关,所有模型 / 框架通用。
2. Skill(技能)
本质:面向用户的业务功能、能力包
作用:给用户提供完整服务,是产品 / 业务层面的抽象。
粒度:一个 Skill 可包含多个函数、多轮调用、逻辑编排。
范围:业务层,平台 / 产品自定义(如天气技能、计算器技能、点餐技能)
TCP链接建立为什么是三次握手不是两次?
两次只能证明「客户端发得出、服务端收得到」,无法验证服务端发得出、客户端收得到,会造成服务端资源浪费。
TCP关闭链接的四次握手,部分情况能合三次
TCP 断开是双向关闭,默认一方先主动关闭,分两个独立方向收尾。
标准流程(客户端主动断开)
1. 第一次挥手:客户端 → 服务端
客户端发 FIN
含义:客户端不再发送新数据,请求关闭「客户端→服务端」方向。
2. 第二次挥手:服务端 → 客户端
服务端发 ACK
含义:收到你的关闭请求。
关键点:此时服务端还能继续向客户端发剩余数据,通道没彻底关。
3. 第三次挥手:服务端 → 客户端
当服务端所有数据都发送完毕后,发 FIN
含义:服务端也不再发数据,请求关闭「服务端→客户端」方向。
4. 第四次挥手:客户端 → 服务端
客户端发 ACK
含义:收到,双方彻底断开连接。
能合是极端情况,大部分时候还是四次挥手
客户端发 FIN 后,服务端恰好没有任何剩余数据
那么服务端可以把 ACK + FIN 合并成一个报文,此时会变成三次挥手。
TCP 协议在完成四次挥手后是直接断开吗,要等待多久才断开
以客户端主动关闭为例,走完四次挥手后分两端状态,重点讲等待机制、时长、作用。
一、完整状态流转(客户端主动断连)
角色:主动方(客户端)、被动方(服务端)
- 客户端 → 服务端:
FIN(第一次挥手) - 服务端 → 客户端:
ACK(第二次挥手) - 服务端 → 客户端:
FIN(第三次挥手) - 客户端 → 服务端:
ACK(第四次挥手)
两端最终行为
- 被动方(服务端):收到第四次挥手的
ACK后 立即关闭连接,进入CLOSED状态,无等待。 - 主动方(客户端):发出第四次挥手的
ACK后,不会马上关闭,进入TIME_WAIT状态,必须等待一段时间。
二、TIME_WAIT:等待多久?
标准规定:等待 2MSL
- MSL(Maximum Segment Lifetime):报文段最大生存时间,RFC 标准定义 MSL = 2 分钟
- 所以
2MSL = 4 分钟(240 秒)
补充:实际操作系统会做优化,Linux 默认 MSL 设为 30s,对应 2MSL = 60 秒。
等待结束后,主动方才进入 CLOSED,连接彻底释放。
三、为什么一定要等 2MSL?(两个核心目的)
1. 确保最后一个 ACK 报文能被对方收到
第四次挥手的 ACK 是主动方发的最后一个包:
- 如果这个
ACK丢失,被动方(服务端)会超时重发 FIN; - 主动方处在
TIME_WAIT阶段,收到重传的FIN后,会重新补发 ACK; - 若不等、直接关闭,主动方端口已释放,无法回应重传的 FIN,被动方会一直重传、资源无法释放。
2. 防止「残留旧报文」干扰新连接
网络中可能存在延迟的老旧 TCP 报文,会在链路里游荡:
2MSL时长足以保证本连接所有旧报文全部从网络中消失;- 避免同一端口后续新建连接时,收到上一次连接的过期数据,造成数据错乱。
四、补充常见状态 & 易错点
CLOSE_WAIT
被动方收到 FIN、回复 ACK 后进入此状态,代表本地还有数据要发,发完才会发 FIN。如果大量CLOSE_WAIT堆积,一般是应用层没调用关闭套接字(代码漏关连接)。端口复用问题
主动方端口在TIME_WAIT期间被占用,短时间内无法立刻用同端口新建连接;高并发服务常通过调整内核参数(如tcp_tw_reuse)优化。反向场景:服务端主动断开
逻辑完全一致:主动断开的一端进入 TIME_WAIT 等待 2MSL,被动端收到 ACK 直接关闭。
总结
- 四次挥手后:被动端立刻断开,主动端进入 TIME_WAIT 等待;
- 标准等待时长:
2MSL(标准4分钟,Linux 常用60秒); - 等待原因:保证最后 ACK 可靠送达、清空网络残留旧报文。
IPC(进程间通信)
1. 管道(Pipe)
无名管道:亲缘进程(父子进程)专用
半双工(单向通信)
简单、系统自带
2. 命名管道(FIFO)
有名字,任意进程都能用
半双工
以文件形式存在
3. 消息队列(Message Queue)
消息链表,按消息格式收发
全双工、独立于进程
支持优先级、消息类型
4. 共享内存(Shared Memory)
最快的 IPC
多个进程直接访问同一块内存
缺点:需要自己加锁(信号量)控制同步
5. 信号量(Semaphore)
不是传数据,是锁、同步机制
控制多进程临界区访问
防止并发冲突
6. 信号(Signal)
软中断,简单通知机制
例:kill -9、Ctrl+C
只能传信号,不能传大量数据
7. 套接字(Socket)
跨主机通信
最通用、网络编程必备
支持 TCP/UDP
8. 内存映射(mmap)
将文件映射到内存
进程读写内存 = 读写文件
大文件、高并发常用
Go Map 真删除 vs 假删除
Go 语言里 delete(map, key) 是真正的物理删除,会直接把键值对从内存中移除;而假删除(软删除) 是业务层的逻辑删除,不会真正移除数据,只是标记为「已删除」,查询时过滤掉标记数据即可。
下面分两部分讲清楚:原生真删除 + 业务假删除(核心)
一、Go Map 原生真删除(delete 内置函数)
这是 Go 官方提供的物理删除,直接清除内存中的键值对,释放空间。
用法
1 | delete(你的map, 要删除的key) |
特点
- 无返回值:即使 key 不存在,
delete也不会报错 - 并发不安全:多协程操作必须加锁
- 真正释放:键值对彻底从 map 中消失
示例
1 | package main |
二、Go Map 假删除(软删除 / 逻辑删除)
假删除 = 不删数据,只打删除标记
适用场景:需要保留历史数据、审计日志、恢复数据的业务。
实现方案
把 map 的值定义为结构体,增加一个 Deleted bool 删除标记:
- 标记
Deleted: true= 已删除(假删除) - 标记
Deleted: false= 正常数据 - 查询/遍历只返回未标记的数据
完整代码示例
1 | package main |
输出结果
1 | 有效数据: |
假删除核心优势
- 数据可恢复:把
Deleted改回false就恢复 - 保留历史:不会丢失原始数据
- 业务可控:查询、统计时自主过滤删除数据
- 无内存释放:数据仍在 map 中,只是被标记隐藏
三、真删除 vs 假删除 对比
| 特性 | 真删除(delete) |
假删除(标记删除) |
|---|---|---|
| 数据是否消失 | 是,物理移除 | 否,保留在 map 中 |
| 内存是否释放 | 是 | 否 |
| 能否恢复 | 不能 | 能(修改标记) |
| 实现方式 | 内置 delete() 函数 |
结构体 + Deleted 标记 |
| 使用场景 | 无需保留的临时数据 | 需留痕、可恢复的业务数据 |
总结
- Go 原生没有假删除,
delete(map, key)是真·物理删除 - 假删除是业务逻辑:用结构体+删除标记实现
- 真删除适合清理无用数据;假删除适合需要保留历史、可恢复的数据
- 假删除的核心:标记为已删除 + 查询时过滤
回表查询
普通索引 只存 索引列的值 + 主键 ID
你查询时,数据库先在索引树找到目标数据的主键 ID
然后拿着这个 ID 去聚簇索引(原表数据) 查一遍,拿到完整行数据
第 3 步就是回表




