快速回顾
| 项目 | 内容 |
|---|---|
| 缓存对象 | 业务查询结果、查询向量和近期对话,数据可从事实源重建 |
| Cache Aside | 读缓存 → 未命中回源并回填;数据库事务提交后删除相关缓存 |
| 租户隔离 | 业务 Key 包含组织及数据范围;对话按组织、用户和会话隔离 |
| 向量缓存 | 按模型版本、编码配置和问题区分;命中后仍按当前权限和版本检索 |
| 一致性 | 主动失效与 TTL 控制陈旧时间,业务执行前重新校验当前事实 |
| 故障处理 | 受控回源,对热点 Key 合并重建请求,记录删除失败并补偿 |
| 优化指标 | 命中率、数据库查询次数、embedding 调用次数、平均耗时、P95 与更新可见延迟 |
面试主回答
我们主要用 Redis 缓存业务查询结果、查询向量和近期对话。业务查询采用 Cache Aside,未命中时回源 MySQL 并设置 TTL;业务修改提交后删除相关缓存,让后续请求重建。
多租户缓存 Key 包含组织和必要的数据范围,避免不同用户共用不该共享的结果。查询向量缓存按模型版本、编码配置和问题区分,命中后仍执行当前权限和版本条件下的检索。近期对话按组织、用户和会话隔离,MySQL 保存完整消息。
TTL 按数据变化频率分别设置,Redis 故障时受控回源。涉及调度、审批和权限的操作重新校验当前事实,避免使用陈旧缓存执行。优化效果主要通过命中率、数据库查询次数、向量编码次数和请求 P95 来观察。
缓存对象与读写流程
面试回答
主要是减少几类重复开销。机型资料和部分统计结果会被反复查询,直接回源会重复执行 SQL 和聚合;知识检索中,同一个问题反复生成查询向量也会增加模型调用;多轮问答还需要频繁加载近期消息。
因此我用 Redis 缓存业务读结果、查询向量以及近期对话和常用偏好,让重复请求尽量复用已有结果。MySQL 仍保存业务记录、消息和来源,Redis 中的数据可以回源重建。真正调度、审批或者修改资源时,仍然查询当前业务事实并校验,避免把缓存中的旧状态直接当作执行依据。
实现细节
业务查询缓存、查询向量缓存与上下文缓存分别减少数据库访问、编码调用和消息加载。评估效果要看对应的完整请求路径,不能仅用 Redis 单次读取速度判断收益。
面试回答
我把缓存分为三类。业务读缓存保存机型资料、部分查询和经营统计,减少重复 SQL;查询向量缓存保存问题的 embedding,避免重复编码;近期对话缓存保存组织、用户和会话范围内的近期消息,加快上下文装配。
缓存内容需要能够从持久化记录或模型计算重建。资产现在能否调度、任务有没有冲突、工单是否允许提交,这些在实际执行前仍然按当前事实检查。查询向量命中也只省掉编码步骤,后续知识检索依然要带当前权限、时间和版本条件,不能把向量缓存当成可直接复用的答案。
实现细节
三类缓存的职责见表。查询向量不包含检索结果的访问授权;业务读缓存的范围需要随有效数据范围变化,命中后仍保留必要的访问检查。
| 缓存类型 | 缓存内容 | 主要作用 |
|---|---|---|
| 业务读缓存 | 机型资料、部分业务查询和经营统计结果 | 减少重复 SQL 与聚合计算 |
| 查询向量缓存 | 问题经 embedding 模型编码后的向量 | 避免重复编码 |
| 近期对话缓存 | 当前用户、组织和会话的近期消息 | 加快上下文装配 |
面试回答
读取时先查 Redis,命中就使用缓存结果,未命中则查 MySQL,再回填并设置 TTL。修改时先提交数据库事务,再删除受影响的缓存,由下一次读取重建。
选择删除而不逐一更新,是因为一次业务修改可能同时影响详情、列表和多个统计结果,全部重新计算容易复杂且遗漏。比如成本单据审核后,要让相关展示和统计失效。删除放在事务提交后,避免其他请求在新事务尚未提交时就查到旧值并重新缓存。并发回填和删除失败仍需单独处理,这个顺序本身不能保证强一致。
实现细节
读路径为缓存、数据库、回填,写路径为事务提交、缓存失效。受影响缓存需要结合查询依赖确定;提交前删除可能让并发读取重新缓存尚未提交更新前的旧值。
Key 与过期策略
面试回答
Key 要包含所有会影响结果的范围和查询参数。业务详情通常包含组织、资源类型和资源 ID;统计还要加有效数据范围、时间区间和条件;对话按组织、用户和会话隔离;查询向量则按模型版本、编码配置和查询文本区分。
同一个组织里,不同成员能看到的记录可能不同,所以只加组织 ID 还不够,涉及数据范围的结果要继续区分。命中缓存也不能跳过必要的访问校验。查询向量只是文本编码,可以在编码兼容时复用,但真正检索时仍然应用当前用户的知识库、权限、时间和版本条件。
实现细节
各对象的 Key 组成见表。不要把用户可读结果与单纯查询向量混在同一种缓存里;权限范围不同的结果不能因查询文本相同而共用。
| 缓存对象 | Key 的组成 |
|---|---|
| 业务详情 | 组织 ID + 资源类型 + 资源 ID |
| 统计结果 | 组织 ID + 数据范围 + 时间区间 + 查询条件 |
| 近期对话 | 组织 ID + 用户 ID + 会话 ID |
| 查询向量 | embedding 模型版本 + 编码配置 + 查询文本 |
面试回答
查询向量必须和文档索引处在兼容的向量空间。模型版本、维度或预处理方式变化后,即使查询文本一样,生成的向量也可能不兼容,所以 Key 中要包含模型和编码配置,升级时使用新的缓存命名空间。
文本规范化只处理无意义差异,比如首尾空白,不能把地区、日期、机型编号删掉,否则不同问题可能共用同一个向量。缓存的是问题编码,文档内容更新后,如果编码配置没变,查询向量仍可使用,再对当前索引执行检索;旧模型向量则不能直接与新索引混用。
实现细节
缓存版本与文档索引编码版本需要配套管理。模型升级造成的兼容性变化,与普通文档新增、删除造成的内容变化分别处理,后者不自动使所有兼容查询向量失效。
面试回答
TTL 根据数据变化频率、回源成本和能接受的陈旧时间分别设置。相对稳定的机型资料可以长一些,频繁变化的业务查询和统计短一些,并配合写入后的主动失效。查询向量在模型不变时比较稳定,过期主要用于控制容量和淘汰旧问题。
近期对话同时限制会话过期时间和消息数量,避免无限增长。大量同类缓存再给基础 TTL 增加少量随机偏移,减少同时失效造成的回源高峰。TTL 只是限制旧值可能保留的时间,在有效期内也可能已经过时,所以执行操作前仍然检查当前业务事实。
实现细节
不同缓存的过期考虑见表,不统一设置一个固定时长。TTL 与主动失效共同控制展示数据陈旧窗口,权限和执行条件不依赖缓存有效期来保证正确性。
| 缓存对象 | TTL 考虑 | 配合措施 |
|---|---|---|
| 相对稳定的机型资料 | 可保留较长时间 | 变更后主动失效 |
| 频繁变化的业务查询与统计 | 较短时间,控制陈旧窗口 | 写入后主动失效 |
| 查询向量 | 主要控制容量与旧问题淘汰 | 模型或配置变化使用新命名空间 |
| 近期对话 | 会话级过期 | 消息数量上限 |
| 同类缓存 | 基础 TTL 增加少量随机偏移 | 避免集中失效;执行仍检查当前事实 |
一致性与故障处理
面试回答
我们采用先提交数据库修改、再删除相关缓存,下一次读取回源重建。对普通展示数据,接受短时间最终一致,通过主动失效和 TTL 限制陈旧窗口;删除失败会记录并补偿,不能只打日志就结束。
这个方案仍然存在并发窗口,比如请求 A 查到旧值,请求 B 更新数据库并删除缓存,之后 A 才把旧值回填。TTL 能限制它存在多久,但不能消除这个窗口。因此调度、审批和权限判断在执行时重新查询当前事实,不直接根据展示缓存决定是否写入。我会把展示一致性的要求和业务执行的正确性分别处理。
实现细节
需要关注事务提交前失效、提交后删除失败和并发旧值回填三类时序。补偿负责重做失效动作,TTL 负责限制残留旧值时间,二者都不等价于数据库与 Redis 的强一致事务。
面试回答
Redis 不可用时按缓存类型回源。业务读结果回 MySQL,查询向量重新调用编码服务,近期消息从 MySQL 按需要加载。同时限制超时和并发,避免缓存故障把所有压力直接转移到数据库或模型服务。
热点 Key 失效时,会合并同一个 Key 的重建请求,让一个请求加载,其他请求等待或受控回源,减少相同 SQL 和编码任务同时执行。这只是缓存重建保护,不能替代业务事务或状态锁。监控时也区分正常未命中和缓存服务故障,分别看命中率、回源耗时和错误数量。
实现细节
各缓存的回源路径见表。近期消息读取遵守上下文预算,编码回源限制排队与等待。合并重建只减少同一 Key 的重复计算,业务写入仍按自身并发规则执行。
| 情况 | 回源或恢复 | 保护措施 |
|---|---|---|
| 业务读缓存不可用 | 回源 MySQL | 超时、并发与数据库压力控制 |
| 查询向量缓存不可用 | 重新调用编码服务 | 限制编码并发与等待 |
| 近期消息缓存不可用 | 从 MySQL 加载 | 按上下文预算读取 |
| 热点 Key 失效 | 合并同一 Key 的重建请求 | 避免大量请求重复回源 |
评测与结果
面试回答
我会固定数据规模、请求分布和并发条件,对比启用缓存前后的完整请求。业务查询看缓存命中率、数据库查询次数,以及平均耗时和 P95;查询向量缓存再看实际 embedding 调用次数;同时检查数据更新后多久能在查询结果中看到。
实验还要区分未预热和已预热,避免把一次理想命中当成全部请求表现。只有重复 SQL 或编码次数下降、用户等待改善,并且结果陈旧时间在可接受范围内,才能说明缓存起到了作用。单看 Redis 读取很快,无法证明整个业务接口变快,更不能说明数据更新仍然正确。
实现细节
指标定义见表。前后请求条件保持一致,预热状态单独记录;把查询性能收益和更新可见延迟一起观察,避免用陈旧结果换取表面上的低延迟。
| 指标 | 观察内容 |
|---|---|
| 缓存命中率 | 是否存在足够的重复访问 |
| 数据库查询次数 | 重复 SQL 和聚合查询是否减少 |
| embedding 实际调用次数 | 向量缓存是否减少重复编码 |
| 请求平均耗时、P95 | 用户等待是否改善 |
| 数据更新后的可见延迟 | 是否出现不可接受的陈旧结果 |
易混点与回答检查
易混点:查询向量缓存不是检索结果缓存;TTL 控制陈旧窗口,不能保证强一致;命中缓存不免除访问校验。
回答检查
补充记录
简历表述:缓存业务数据、检索向量和近期对话,采用 Cache Aside 并设置 TTL。
事实 / 代码依据:待补充。