diff --git a/docs/interview/index.md b/docs/interview/index.md
index 654a4d7..f328cf2 100644
--- a/docs/interview/index.md
+++ b/docs/interview/index.md
@@ -1,3 +1,8 @@
# 面经
面试经验记录与总结。
+
+| 公司 | 岗位 | 轮次 | 日期 |
+|------|------|------|------|
+| [去哪儿](qunar-ai-fullstack.md) | AI 全栈 | 一面 | — |
+| [奇安信](qianxin-ai-fullstack-round1.md) | AI 全栈开发 | 一面 | 2025-09-10 |
diff --git a/docs/interview/qianxin-ai-fullstack-round1.md b/docs/interview/qianxin-ai-fullstack-round1.md
new file mode 100644
index 0000000..e6821aa
--- /dev/null
+++ b/docs/interview/qianxin-ai-fullstack-round1.md
@@ -0,0 +1,784 @@
+# 奇安信 AI 全栈开发 — 一面
+
+> 9.10 面试,耗时一小时,体验不错,面试官非常友好
+> 以下内容根据回忆记录,未录音
+
+---
+
+## 1. 自我介绍
+
+常规开场,简要介绍个人背景、技术栈和项目经历。
+
+!!! tip "回答框架"
+ - **Who**:姓名、学历、工作年限
+ - **What**:核心技术栈(如 Go + Redis + MySQL + AI)
+ - **Highlight**:1-2 个最有亮点的项目
+ - **Why**:为什么投这个岗位
+
+---
+
+## 2. 两段实习哪一段最具有挑战性 & 若干实习追问
+
+面试官希望了解实习经历中的深度思考和技术挑战,而非简单罗列做了什么。
+
+!!! tip "STAR 法则回答框架"
+ - **Situation**:项目的背景和业务上下文
+ - **Task**:你负责的具体任务和目标
+ - **Action**:你采取的技术方案和关键决策
+ - **Result**:量化的成果
+
+**回答要点:**
+
+- 选择技术难度最高或业务影响最大的那段实习
+- 重点描述遇到的技术挑战和解决方案
+- 面试官会根据回答进行深入追问,需要对项目细节了然于胸
+
+---
+
+## 3. 滑动窗口分布式限流 & 数据结构 & 过期时间设置
+
+### 3.1 滑动窗口限流原理
+
+滑动窗口限流是对固定窗口的改进,解决了固定窗口在窗口边界处可能出现的突发流量问题。
+
+```mermaid
+graph LR
+ subgraph 固定窗口
+ W1[窗口1
00:00-01:00
计数=100] --> W2[窗口2
01:00-02:00
计数=0]
+ end
+ subgraph 滑动窗口
+ S1[00:30-01:30
统计最近60s]
+ S2[00:45-01:45
统计最近60s]
+ end
+```
+
+**核心思想**:统计当前时刻往前推 N 秒内的请求数量,窗口随时间持续滑动。
+
+### 3.2 Redis 实现方案 — Sorted Set
+
+最常用的实现方式是使用 Redis 的 **ZSET(有序集合)**:
+
+| 字段 | 说明 |
+|------|------|
+| **Member** | 请求唯一标识(UUID 或雪花 ID) |
+| **Score** | 请求的时间戳(毫秒级) |
+
+**核心逻辑:**
+
+```go
+// 滑动窗口限流 — Lua 脚本保证原子性
+local key = KEYS[1] -- 限流 key
+local now = tonumber(ARGV[1]) -- 当前时间戳(ms)
+local window = tonumber(ARGV[2]) -- 窗口大小(ms)
+local limit = tonumber(ARGV[3]) -- 窗口内最大请求数
+
+-- 1. 移除窗口外的过期成员
+redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
+
+-- 2. 统计窗口内的请求数
+local count = redis.call('ZCARD', key)
+
+-- 3. 判断是否限流
+if count < limit then
+ redis.call('ZADD', key, now, ARGV[4]) -- 添加当前请求
+ redis.call('PEXPIRE', key, window) -- 设置过期时间
+ return 1 -- 允许
+else
+ return 0 -- 拒绝
+end
+```
+
+### 3.3 数据结构选择
+
+| 方案 | 数据结构 | 精确度 | 内存开销 | 复杂度 |
+|------|---------|--------|---------|--------|
+| **ZSET** | 有序集合 | 精确 | 较高(每请求一个 member) | O(log N) |
+| **Bitmap** | 位图 | 时间片级别 | 极低 | O(1) |
+| **滑动窗口计数器** | Hash | 近似 | 低 | O(1) |
+
+### 3.4 过期时间设置
+
+!!! warning "关键细节"
+ ZSET 本身不会自动删除过期 member,需要配合 `PEXPIRE` 设置 key 的 TTL。
+
+- **TTL 设置为窗口大小**:每次写入时用 `PEXPIRE key window_ms` 刷新 key 的过期时间
+- **惰性清理**:每次请求时先执行 `ZREMRANGEBYSCORE` 清理窗口外的数据
+- **避免内存泄漏**:如果 key 长期无访问,TTL 到期后 Redis 会自动删除整个 key
+
+---
+
+## 4. MULTI 能否保证原子性 & MULTI/EXEC 和 Lua 有什么区别
+
+### 4.1 MULTI/EXEC 的原子性
+
+`MULTI/EXEC` 提供的是**部分原子性**(也称为"事务"),但与关系型数据库的事务有本质区别:
+
+| 特性 | MULTI/EXEC | 数据库事务 |
+|------|-----------|-----------|
+| 命令入队 | 命令在 EXEC 之前只是入队,不执行 | 语句逐条执行 |
+| 执行方式 | EXEC 时一次性按序执行 | 逐条执行,可回滚 |
+| 回滚 | **不支持回滚**,某条命令失败不影响其他命令 | 支持回滚 |
+| 隔离性 | 单线程保证不会被其他命令插入 | 通过锁和 MVCC 保证 |
+
+```mermaid
+sequenceDiagram
+ participant C as 客户端
+ participant R as Redis
+ C->>R: MULTI
+ R-->>C: OK
+ C->>R: SET key1 value1
+ R-->>C: QUEUED
+ C->>R: SET key2 value2
+ R-->>C: QUEUED
+ C->>R: EXEC
+ R-->>C: [OK, OK]
+```
+
+!!! warning "MULTI 不支持回滚"
+ 如果 EXEC 中某条命令执行失败(如对 string 执行 LPUSH),Redis **不会回滚**其他已执行的命令。这是 Redis 的设计哲学:保持简单和高性能。
+
+### 4.2 MULTI/EXEC vs Lua 脚本
+
+| 维度 | MULTI/EXEC | Lua 脚本 |
+|------|-----------|---------|
+| **原子性** | 命令按序执行,但不支持条件逻辑 | 完全原子,支持条件分支 |
+| **条件逻辑** | 不支持(无法根据前一条结果决定后续操作) | 支持 if/else/循环 |
+| **网络开销** | 多条命令 + EXEC,多次网络往返(入队阶段) | 一次性发送整个脚本 |
+| **错误处理** | 某条失败继续执行后续命令 | 脚本内可处理错误逻辑 |
+| **可复用** | 不支持 | 可用 `EVALSHA` 缓存脚本,减少传输 |
+| **阻塞风险** | 不会长时间阻塞 | 复杂脚本可能阻塞 Redis |
+
+**选择建议:**
+
+- **简单批量操作**(无条件逻辑)→ MULTI/EXEC
+- **需要条件判断**(如限流:先查计数再决定是否放行)→ Lua 脚本
+- **高性能要求** → Lua 脚本(一次网络往返 + 原子执行)
+
+---
+
+## 5. Redis 单线程为什么快 & Redis 不可用如何处理
+
+### 5.1 Redis 单线程为什么快
+
+| 因素 | 说明 |
+|------|------|
+| **纯内存操作** | 数据存储在内存中,读写速度是磁盘的 10 万倍 |
+| **单线程避免上下文切换** | 不存在多线程的锁竞争和上下文切换开销 |
+| **I/O 多路复用** | 使用 epoll/kqueue 实现单线程同时处理大量连接 |
+| **高效数据结构** | SDS、ZIPLIST、SKIPLIST 等针对场景优化的数据结构 |
+| **简单通信协议** | RESP 协议解析简单高效 |
+
+```mermaid
+graph TD
+ subgraph Redis 单线程模型
+ EP[epoll 多路复用] -->|就绪事件| EL[事件循环 Event Loop]
+ EL -->|读事件| RH[命令解析与执行]
+ EL -->|写事件| WH[响应发送]
+ RH -->|写回客户端| WH
+ end
+```
+
+!!! note "Redis 6.0+ 的多线程"
+ Redis 6.0 引入了 **I/O 多线程**,但仅用于网络 I/O(读写 socket),**命令执行仍然是单线程**。这样既利用多核加速网络处理,又保持了命令执行的简单性和原子性。
+
+### 5.2 Redis 不可用如何处理
+
+```mermaid
+graph TD
+ A[Redis 不可用] --> B{部署模式?}
+ B -->|单机| C[降级方案]
+ B -->|主从| D[哨兵自动故障转移]
+ B -->|集群| E[Cluster 自动故障转移]
+ C --> C1[本地缓存兜底]
+ C --> C2[直接访问数据库 + 限流]
+ C --> C3[返回默认值/缓存空值]
+ D --> D1[Sentinel 检测主节点下线]
+ D --> D2[选举新主节点]
+ D --> D3[客户端自动切换]
+ E --> E1[节点间 Gossip 检测]
+ E --> E2[故障节点的从节点晋升]
+```
+
+**降级策略:**
+
+| 策略 | 适用场景 | 实现方式 |
+|------|---------|---------|
+| **本地缓存兜底** | 读多写少 | 使用 sync.Map 或 LRU Cache 缓存热点数据 |
+| **直接查库 + 限流** | 数据一致性要求高 | 限制穿透到数据库的 QPS,防止雪崩 |
+| **返回默认值** | 容忍短暂不一致 | 返回预设的默认值或静态数据 |
+| **熔断降级** | 非核心功能 | 使用 Hystrix/sentinel 熔断,返回降级结果 |
+
+---
+
+## 6. 多级缓存相关问题
+
+### 6.1 多级缓存架构
+
+```mermaid
+graph LR
+ R[请求] --> L1[L1 本地缓存
进程内 / sync.Map]
+ L1 -->|未命中| L2[L2 分布式缓存
Redis]
+ L2 -->|未命中| DB[数据库 MySQL]
+ DB -->|回写| L2
+ L2 -->|回写| L1
+```
+
+### 6.2 各级缓存对比
+
+| 层级 | 介质 | 延迟 | 容量 | 一致性 | 适用数据 |
+|------|------|------|------|--------|---------|
+| **L1** | 进程内存(Go: sync.Map/bigcache) | 纳秒级 | 小(受 JVM/Go 内存限制) | 难以保证 | 极热点、变更少 |
+| **L2** | Redis | 毫秒级 | 大(集群可扩展) | 较好保证 | 通用热点数据 |
+| **L3** | MySQL / 磁盘 | 十毫秒级 | 海量 | 强一致 | 全量数据 |
+
+### 6.3 常见问题
+
+**缓存一致性问题:**
+
+- **写时失效**:更新数据库时同时删除缓存(Cache Aside 模式)
+- **延迟双删**:先删缓存 → 更新数据库 → 延迟 N 毫秒 → 再删缓存
+- **消息队列异步**:数据库变更事件发送到 MQ,消费者更新缓存
+
+**L1 缓存的特殊挑战:**
+
+- 多实例间 L1 缓存不一致 → 需要广播失效(如 Redis Pub/Sub)
+- L1 缓存容量有限 → 需要精心设计淘汰策略(LRU / LFU / TTL)
+
+---
+
+## 7. 认证链路 & SAML 是什么 & OAuth2.0 原理
+
+### 7.1 认证(Authentication)vs 授权(Authorization)
+
+| 概念 | 含义 | 典型问题 |
+|------|------|---------|
+| **认证(AuthN)** | 你是谁 | 用户名密码、MFA、SSO |
+| **授权(AuthZ)** | 你能做什么 | RBAC、ACL、OAuth Scope |
+
+### 7.2 SAML(Security Assertion Markup Language)
+
+SAML 是一种基于 XML 的**联邦身份认证**标准,主要用于企业级 SSO(单点登录)。
+
+```mermaid
+sequenceDiagram
+ participant U as 用户/浏览器
+ participant SP as 服务提供方(SP)
+ participant IdP as 身份提供方(IdP)
+ U->>SP: 访问受保护资源
+ SP->>U: 302 重定向到 IdP
+ U->>IdP: 携带 SAMLRequest
+ IdP->>IdP: 认证用户(登录页)
+ IdP->>U: 返回 SAML Response(XML 断言)
+ U->>SP: 提交 SAML Response
+ SP->>SP: 验证签名、解析断言
+ SP->>U: 授权访问
+```
+
+**SAML 核心组件:**
+
+| 组件 | 说明 |
+|------|------|
+| **IdP(身份提供方)** | 负责认证用户,如 Okta、ADFS |
+| **SP(服务提供方)** | 需要认证的应用 |
+| **Assertion(断言)** | 用户身份信息的 XML 声明 |
+| **XML Signature** | 保证断言的完整性和不可否认性 |
+
+### 7.3 OAuth 2.0 原理
+
+OAuth 2.0 是一种**授权框架**,核心目标是让第三方应用在**不接触用户密码**的情况下,获取对用户资源的有限访问权限。
+
+**四种授权模式:**
+
+| 模式 | 适用场景 | 安全性 |
+|------|---------|--------|
+| **授权码模式** | 有后端的 Web 应用 | 最高(推荐) |
+| **隐式模式** | 纯前端 SPA | 较低(已不推荐) |
+| **密码模式** | 高度信任的第一方应用 | 中(不推荐第三方使用) |
+| **客户端凭证** | 服务间通信 | 高(无用户参与) |
+
+**授权码模式完整流程:**
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant C as 客户端(第三方应用)
+ participant A as 授权服务器
+ participant R as 资源服务器
+ U->>C: 点击"授权"
+ C->>A: 重定向:/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&scope=xxx
+ A->>U: 显示授权页面
+ U->>A: 确认授权
+ A->>C: 302 重定向 redirect_uri?code=AUTH_CODE
+ C->>A: POST /token (code + client_secret)
+ A->>C: 返回 access_token + refresh_token
+ C->>R: 请求资源 + Bearer access_token
+ R->>C: 返回受保护资源
+```
+
+### 7.4 SAML vs OAuth 2.0
+
+| 维度 | SAML | OAuth 2.0 |
+|------|------|-----------|
+| **目的** | 联邦身份认证(SSO) | 资源授权 |
+| **数据格式** | XML | JSON |
+| **典型场景** | 企业 SSO(登录 Google Workspace) | 第三方应用授权(GitHub 登录) |
+| **Token 类型** | SAML Assertion | Access Token (JWT) |
+| **复杂度** | 重(XML 解析、签名验证) | 轻(REST + JSON) |
+
+---
+
+## 8. 缓存穿透 & 布隆过滤原理
+
+### 8.1 缓存穿透
+
+缓存穿透是指查询一个**一定不存在**的数据,由于缓存未命中,每次都直接查询数据库。
+
+```mermaid
+graph LR
+ R[请求 id=-1] --> C[缓存]
+ C -->|未命中| DB[数据库]
+ DB -->|不存在| C2[返回空]
+ C2 --> R2[下次请求 id=-1]
+ R2 --> C
+ C -->|仍未命中| DB
+```
+
+**解决方案:**
+
+| 方案 | 原理 | 适用场景 |
+|------|------|---------|
+| **缓存空值** | 查询为空时也写入缓存(短 TTL) | 数据量可控 |
+| **布隆过滤器** | 预判断 key 是否可能存在 | 大量 key,允许一定误判 |
+| **接口参数校验** | 在入口层拦截非法参数 | 参数有明确约束 |
+
+### 8.2 布隆过滤器原理
+
+布隆过滤器是一种**概率型数据结构**,用于判断一个元素**是否可能存在于集合中**。
+
+| 判断结果 | 实际含义 |
+|---------|---------|
+| **不存在** | 一定不存在(100% 准确) |
+| **存在** | 可能存在(有假阳性率) |
+
+**核心原理:**
+
+```mermaid
+graph TD
+ subgraph 布隆过滤器
+ B[位数组 Bit Array
m 位,初始全 0]
+ end
+ subgraph 插入过程
+ E[元素 x] --> H1[Hash1(x) → 位置 i1]
+ E --> H2[Hash2(x) → 位置 i2]
+ E --> H3[Hashk(x) → 位置 ik]
+ H1 -->|置 1| B
+ H2 -->|置 1| B
+ H3 -->|置 1| B
+ end
+ subgraph 查询过程
+ Q[查询 y] --> C1[Hash1(y) → 位置 i1]
+ Q --> C2[Hash2(y) → 位置 i2]
+ Q --> C3[Hashk(y) → 位置 ik]
+ C1 --> J{所有位都为 1?}
+ C2 --> J
+ C3 --> J
+ J -->|是| R1[可能存在]
+ J -->|否| R2[一定不存在]
+ end
+```
+
+**关键参数:**
+
+| 参数 | 含义 | 关系 |
+|------|------|------|
+| **m** | 位数组大小 | m 越大,误判率越低 |
+| **n** | 预期插入元素数量 | n 越大,误判率越高 |
+| **k** | 哈希函数个数 | k = (m/n) × ln2 时最优 |
+| **p** | 误判率(假阳性率) | p ≈ (1 - e^(-kn/m))^k |
+
+!!! warning "布隆过滤器的局限"
+ - **不支持删除**:置 0 可能影响其他元素(需要删除时考虑 Counting Bloom Filter)
+ - **有假阳性**:判断"存在"时可能误判
+ - **需要预估容量**:容量不足时误判率快速上升
+
+---
+
+## 9. MySQL 引擎有哪些 & InnoDB 特性
+
+### 9.1 MySQL 存储引擎对比
+
+| 特性 | InnoDB | MyISAM | Memory | Archive |
+|------|--------|--------|--------|---------|
+| **事务支持** | ✅ | ❌ | ❌ | ❌ |
+| **行级锁** | ✅ | ❌(表锁) | ❌(表锁) | ❌ |
+| **外键** | ✅ | ❌ | ❌ | ❌ |
+| **MVCC** | ✅ | ❌ | ❌ | ❌ |
+| **全文索引** | ✅(5.6+) | ✅ | ❌ | ❌ |
+| **崩溃恢复** | ✅(redo log) | ❌ | ❌ | ❌ |
+| **存储限制** | 64TB | 256TB | 受限于内存 | 无限制 |
+| **适用场景** | OLTP、高并发 | 读多写少 | 临时表、缓存 | 日志归档 |
+
+### 9.2 InnoDB 核心特性
+
+```mermaid
+graph TD
+ subgraph InnoDB 架构
+ subgraph 内存
+ BP[Buffer Pool
数据页 + 索引页]
+ LG[Log Buffer
redo log 缓冲]
+ CB[Change Buffer
非唯一索引变更缓冲]
+ end
+ subgraph 磁盘
+ SYS[系统表空间 ibdata1]
+ UD[用户表空间 .ibd]
+ RL[Redo Log ib_logfile]
+ UL[Undo Log undo_001]
+ end
+ BP -->|脏页刷新| UD
+ LG -->|顺序写| RL
+ CB -->|合并| UD
+ end
+```
+
+**核心特性详解:**
+
+| 特性 | 说明 |
+|------|------|
+| **Buffer Pool** | 将数据页和索引页缓存在内存中,通过 LRU 变体算法管理 |
+| **Change Buffer** | 对非唯一二级索引的 DML 操作先缓存,后台异步合并,减少随机 I/O |
+| **Redo Log** | WAL(Write-Ahead Logging)机制,保证事务的持久性,崩溃后可恢复 |
+| **Undo Log** | 保存数据修改前的版本,用于事务回滚和 MVCC 一致性读 |
+| **聚簇索引** | 数据按主键顺序存储,主键查询极快 |
+| **MVCC** | 多版本并发控制,读操作不阻塞写操作 |
+| **自适应哈希索引** | InnoDB 自动为热点页建立哈希索引,加速等值查询 |
+
+---
+
+## 10. 幻读 & 隔离级别 & MVCC
+
+### 10.1 幻读定义
+
+**幻读(Phantom Read)**:在同一事务中,两次执行相同的范围查询,第二次查询返回了第一次查询没有看到的**新行**(由其他事务 INSERT 产生)。
+
+```mermaid
+sequenceDiagram
+ participant T1 as 事务 T1
+ participant T2 as 事务 T2
+ T1->>T1: SELECT * FROM users WHERE age > 20
结果:3 行
+ T2->>T2: INSERT INTO users(name, age) VALUES('新用户', 25)
+ T2->>T2: COMMIT
+ T1->>T1: SELECT * FROM users WHERE age > 20
结果:4 行(幻读!)
+```
+
+### 10.2 四种隔离级别
+
+| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现方式 |
+|---------|------|-----------|------|---------|
+| **READ UNCOMMITTED** | ✅ 可能 | ✅ 可能 | ✅ 可能 | 无锁,直接读最新数据 |
+| **READ COMMITTED (RC)** | ❌ 避免 | ✅ 可能 | ✅ 可能 | 每次读生成新 ReadView |
+| **REPEATABLE READ (RR)** | ❌ 避免 | ❌ 避免 | ⚠️ 部分避免 | 事务开始时生成 ReadView |
+| **SERIALIZABLE** | ❌ 避免 | ❌ 避免 | ❌ 避免 | 读加共享锁,写加排他锁 |
+
+!!! note "InnoDB 在 RR 级别下如何解决幻读"
+ InnoDB 的 RR 级别通过 **MVCC + Next-Key Lock** 两种机制配合解决幻读:
+ - **快照读**(普通 SELECT):MVCC 保证读到事务开始时的快照,看不到新插入的行
+ - **当前读**(SELECT ... FOR UPDATE / INSERT / UPDATE / DELETE):Next-Key Lock 锁住范围,阻止其他事务在该范围内插入新行
+
+### 10.3 MVCC 机制
+
+MVCC(Multi-Version Concurrency Control)通过保存数据的多个版本,实现读写不阻塞。
+
+**核心组件:**
+
+| 组件 | 说明 |
+|------|------|
+| **隐藏列 DB_TRX_ID** | 最近修改该行的事务 ID |
+| **隐藏列 DB_ROLL_PTR** | 回滚指针,指向 undo log 中的上一个版本 |
+| **隐藏列 DB_ROW_ID** | 隐式主键(无显式主键时使用) |
+| **ReadView** | 事务在某一时刻看到的数据快照 |
+
+**ReadView 结构:**
+
+| 字段 | 含义 |
+|------|------|
+| **m_ids** | 创建 ReadView 时,系统中活跃(未提交)的事务 ID 列表 |
+| **min_trx_id** | m_ids 中的最小值 |
+| **max_trx_id** | 系统应分配给下一个事务的 ID(当前最大 ID + 1) |
+| **creator_trx_id** | 创建该 ReadView 的事务 ID |
+
+**可见性判断规则:**
+
+```go
+func isVisible(rowTrxID uint64, rv *ReadView) bool {
+ // 1. 如果行的事务 ID 等于创建者 ID,可见(自己修改的)
+ if rowTrxID == rv.creatorTrxID {
+ return true
+ }
+ // 2. 如果行的事务 ID 小于最小活跃事务 ID,可见(事务已提交)
+ if rowTrxID < rv.minTrxID {
+ return true
+ }
+ // 3. 如果行的事务 ID 大于等于最大事务 ID,不可见(事务在 ReadView 之后开始)
+ if rowTrxID >= rv.maxTrxID {
+ return false
+ }
+ // 4. 如果行的事务 ID 在活跃列表中,不可见(事务未提交)
+ if rv.m_ids.contains(rowTrxID) {
+ return false
+ }
+ // 5. 否则可见(事务已提交)
+ return true
+}
+```
+
+**RC vs RR 的关键区别:**
+
+| 隔离级别 | ReadView 生成时机 | 效果 |
+|---------|------------------|------|
+| **RC** | 每次 SELECT 都生成新的 ReadView | 能看到其他事务已提交的最新数据 |
+| **RR** | 事务中第一次 SELECT 时生成,后续复用 | 整个事务看到一致的快照 |
+
+---
+
+## 11. Channel 阻塞非阻塞 & 应用 & context
+
+### 11.1 Channel 阻塞与非阻塞
+
+Go 的 channel 天然是阻塞的,但可以通过 `select` + `default` 实现非阻塞操作。
+
+**阻塞行为:**
+
+| 操作 | 有缓冲 Channel | 无缓冲 Channel |
+|------|----------------|---------------|
+| **ch <- v** | 缓冲区满时阻塞 | 没有接收者时阻塞 |
+| **<-ch** | 缓冲区空时阻塞 | 没有发送者时阻塞 |
+
+**非阻塞实现:**
+
+```go
+// 非阻塞发送
+select {
+case ch <- value:
+ // 发送成功
+default:
+ // channel 满或无接收者,跳过
+}
+
+// 非阻塞接收
+select {
+case msg := <-ch:
+ // 接收到消息
+default:
+ // channel 空或无发送者,跳过
+}
+```
+
+### 11.2 Channel 的典型应用
+
+| 模式 | 说明 | 示例 |
+|------|------|------|
+| **生产者-消费者** | 解耦生产和消费速率 | 日志收集、任务队列 |
+| **扇入(Fan-in)** | 多个 channel 合并到一个 | 聚合多个数据源 |
+| **扇出(Fan-out)** | 一个 channel 分发给多个消费者 | 并行处理 |
+| **超时控制** | 配合 `time.After` 实现超时 | HTTP 请求超时 |
+| **退出信号** | 配合 `done` channel 通知退出 | 优雅关闭 |
+
+```go
+// 生产者-消费者模式
+func producer(ch chan<- int) {
+ for i := 0; i < 100; i++ {
+ ch <- i
+ }
+ close(ch)
+}
+
+func consumer(ch <-chan int) {
+ for v := range ch {
+ fmt.Println(v)
+ }
+}
+```
+
+### 11.3 Context 的作用
+
+`context.Context` 是 Go 并发控制的核心,用于在 goroutine 之间传递**取消信号**、**超时**和**请求级值**。
+
+| 方法 | 说明 |
+|------|------|
+| `context.Background()` | 根 Context,通常在 main/init 中使用 |
+| `context.WithCancel(parent)` | 返回可手动取消的 Context |
+| `context.WithTimeout(parent, d)` | 超时后自动取消 |
+| `context.WithDeadline(parent, t)` | 到达指定时间后自动取消 |
+| `context.WithValue(parent, k, v)` | 携带请求级键值对 |
+
+```go
+func worker(ctx context.Context) {
+ for {
+ select {
+ case <-ctx.Done():
+ fmt.Println("收到取消信号:", ctx.Err())
+ return
+ default:
+ // 执行业务逻辑
+ doWork()
+ }
+ }
+}
+
+// 使用
+ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+defer cancel()
+go worker(ctx)
+```
+
+!!! warning "Context 使用规范"
+ - Context 应作为函数的**第一个参数**,命名为 `ctx`
+ - 不要将 Context 存储在 struct 中,而是显式传递
+ - `context.WithValue` 只用于传递请求级元数据(如 traceID),不要传递业务参数
+
+---
+
+## 12. gin 特性 & 错误传递及处理
+
+### 12.1 gin 核心特性
+
+| 特性 | 说明 |
+|------|------|
+| **高性能路由** | 基于 radix tree(压缩前缀树),路由匹配 O(k) 复杂度 |
+| **中间件支持** | 链式中间件,支持 `c.Next()` 和 `c.Abort()` 控制流程 |
+| **参数绑定** | 自动将请求数据绑定到结构体(JSON、Query、Form、URI) |
+| **分组路由** | 路由分组,支持嵌套和中间件复用 |
+| **错误管理** | `c.Errors` 收集错误,支持统一错误处理 |
+
+### 12.2 错误传递与处理
+
+gin 提供了 `c.Error()` 方法收集错误,配合 `c.Errors` 统一处理。
+
+**错误收集:**
+
+```go
+func AuthMiddleware() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ token := c.GetHeader("Authorization")
+ if token == "" {
+ // 收集错误,而不是直接返回
+ c.Error(errors.New("missing authorization header"))
+ c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
+ return
+ }
+ c.Next()
+ }
+}
+```
+
+**统一错误处理中间件:**
+
+```go
+func ErrorHandler() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ c.Next()
+
+ // 请求处理完成后,统一处理错误
+ if len(c.Errors) > 0 {
+ err := c.Errors.Last().Err
+ // 根据错误类型返回不同状态码
+ switch e := err.(type) {
+ case *ValidationError:
+ c.JSON(400, gin.H{"error": e.Message})
+ case *NotFoundError:
+ c.JSON(404, gin.H{"error": e.Message})
+ default:
+ c.JSON(500, gin.H{"error": "internal server error"})
+ }
+ }
+ }
+}
+```
+
+### 12.3 错误处理最佳实践
+
+```go
+// 定义业务错误类型
+type AppError struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+}
+
+func (e *AppError) Error() string { return e.Message }
+
+// Handler 中使用
+func GetUser(c *gin.Context) {
+ id := c.Param("id")
+ user, err := userService.GetByID(id)
+ if err != nil {
+ // 使用 c.Error 收集,由中间件统一处理
+ _ = c.Error(&AppError{Code: 404, Message: "user not found"})
+ return
+ }
+ c.JSON(200, user)
+}
+```
+
+---
+
+## 13. AI 编程工具 & 交互过程
+
+### 13.1 主流 AI 编程工具
+
+| 工具 | 定位 | 核心能力 |
+|------|------|---------|
+| **Cursor** | AI-first IDE | 内联补全、多文件编辑、上下文感知 |
+| **GitHub Copilot** | 代码补全插件 | 实时代码建议、广泛语言支持 |
+| **Claude Code** | CLI Agent | 深度代码理解、工具调用、项目级重构 |
+| **Windsurf** | AI IDE | Cascade 流式编辑、多步推理 |
+
+### 13.2 AI 辅助编程交互过程
+
+```mermaid
+graph TD
+ U[开发者] -->|描述需求| AI[AI 编程工具]
+ AI -->|生成代码| R[代码初稿]
+ R -->|Review| U
+ U -->|反馈修改| AI
+ AI -->|迭代优化| R2[改进代码]
+ R2 -->|测试验证| U
+ U -->|确认| F[最终代码]
+```
+
+### 13.3 高效使用 AI 编程的实践
+
+| 实践 | 说明 |
+|------|------|
+| **精准上下文** | 提供项目结构、依赖关系、编码规范,而非泛泛描述 |
+| **任务拆解** | 将大任务拆成小的、可验证的子任务 |
+| **迭代式开发** | AI 生成初稿 → 人工 Review → 反馈修改 → 再次验收 |
+| **Prompt 模板化** | 对高频场景沉淀可复用的 Prompt |
+| **验证闭环** | AI 生成的代码必须经过单测、Lint、人工 Review |
+
+!!! tip "面试中如何回答"
+ 面试官问 AI 编程工具时,重点不是列举工具名称,而是:
+ 1. **你用了哪些工具**,在什么场景下用
+ 2. **交互过程是什么样的**,如何给 AI 提供上下文
+ 3. **提效效果如何**,有没有量化数据
+ 4. **遇到过什么问题**,如何保证代码质量
+
+---
+
+## 面试总结
+
+这次奇安信一面考察面较广,涵盖了后端开发的核心知识:
+
+| 考察维度 | 题目 | 核心考察点 |
+|---------|------|-----------|
+| **缓存** | 滑动窗口限流、多级缓存、缓存穿透、布隆过滤器 | 缓存系统设计深度 |
+| **Redis** | MULTI/Lua、单线程模型、高可用 | Redis 原理和实战 |
+| **MySQL** | 引擎对比、幻读、隔离级别、MVCC | 数据库原理理解 |
+| **认证** | SAML、OAuth 2.0 | 安全认证体系 |
+| **Go 语言** | Channel、Context、gin | Go 工程实践 |
+| **AI 工程** | AI 编程工具使用 | AI 辅助开发经验 |
+
+!!! tip "面试准备建议"
+ - 缓存和 Redis 是后端面试的高频考点,需要理解原理 + 能写代码
+ - MySQL MVCC 需要理解到 ReadView 的可见性判断规则
+ - Go 的 Channel 和 Context 是并发编程的核心,需要掌握各种模式
+ - AI 编程相关问题需要有实际使用经验,不能只谈理论
diff --git a/mkdocs.yml b/mkdocs.yml
index d51d1bd..3b5a1b8 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -87,6 +87,7 @@ nav:
- 面经:
- interview/index.md
- 去哪儿 AI 面-AI 全栈方向: interview/qunar-ai-fullstack.md
+ - 奇安信 AI 全栈-一面: interview/qianxin-ai-fullstack-round1.md
- 项目:
- project/index.md
- Open-API 计费平台: