简历 preview
简历上 :
实现了一个通用的 阅读、点赞、收藏 服务。使用 Kafka 进行解耦
数据库表设计 :
通用表设计 :
id,biz,bizid,readCnt,CollectCnt,LikeCnt
为 bizid 和 biz 创建一个联合唯一索引
点赞表设计 :
id,biz,bizid,uid,status
为 bizid 和 biz和uid 创建一个联合唯一索引, 用 status进行软删除
数据库处理
对于 阅读、点赞、收藏 操作都会涉及到一个 check-dothing 的并发问题
即 : 当用户点赞的时候 文章是否已经点赞过了,如果点赞过需要+1,否则创建 ,这里统一采用 do on conflict update 处理
不过需要注意的是 : update 的时候我们需要保证自增 所以需要使用 gorm.Expr 来获取数据库运行的值,而不是直接 select-update
缓存处理
对于写入方,优先写入数据库,后写入redis
对于查询方,优选查redis 然后再查数据库
对于写入的过程,因为我们使用的 redis key 是一次缓存了 3个 like,read,collect 三个字段
所以我们自增的时候使用的是 HINCRBY 指令,对于 key 不存在的情况下会有歧义 。所以使用 lua 控制了一下 仅只有 key 存在的时候才会自增
消息队列
新增 统计事件 引入 kafka 给 阅读、点赞服务解耦
在阅读文章服务,使用 kafka 异步发送,设置 10次每批,1s超时进行聚合发送消息
消费端同样设置 10条每批,1s超时来进行组合消费
每次消费 使用一个 map 进行聚合 同个biz+biz_id的内容,优化之前一次点赞就需要一次 insert 的 操作
并且通过开启事务进行消费,防止 MVCC 中的 刷盘操作,将原先要进行10次的刷盘降低到1次。
问题
一致性问题的处理
不处理
从业务上来说,没有必要要求阅读数、点赞数或者收藏数量一定是严格准确的,因为即便不准确,也不会对用户 产生什么不好的影响。
更进一步说,只有高并发的文章才会有并发问题。而高并发的文章,阅读数、点赞数或者收藏数本身就很多,你 少一点点无所谓。
亮点
大量使用了事务,来保证在同时操作两个表的时候,保持住 ACID 语义。
大量使用了 Upsert 语义 , 因为很多情况下我们都不知道数据库中是否已经存在数据
大量使用 lua 脚本来保证缓存中的数据是正确的, 但是没有彻底解决缓存一致性的问题
在查询接口中,只缓存总数数据,, 并不会缓存个人是否收藏或者点赞的数据。
总分:62 / 100
按「2 年 Go 后端」面试标准看:方向对、有工程意识,但表达粗糙、关键点浅、经不起追问。面试里能撑 15–20 分钟开场,深入一追容易露怯。
分项打分
| 维度 | 分数 | 说明 |
|---|---|---|
| 业务建模与表设计 | 14/20 | 通用计数表 + 点赞明细表思路正确,索引方向对 |
| 并发与数据正确性 | 12/20 | 知道 Upsert / Expr,但 check-then-act、事务边界讲不清 |
| 缓存设计 | 12/20 | Cache-Aside + Lua 防空 key 自增有亮点,一致性论证弱 |
| 消息队列与解耦 | 13/20 | Kafka 批量、聚合、减刷盘有工程味道 |
| 表达与面试叙事 | 6/10 | 口语化、错别字多,「大量使用」空洞,像笔记不像口述稿 |
| 亮点与深度 | 5/10 | 亮点空泛,缺少权衡、失败场景、可观测性 |
优点
- 表设计合理:
biz + biz_id抽象通用互动能力,点赞用(biz, biz_id, uid)唯一 +status软删除,符合常见业务。 - 踩过真实坑:
ON CONFLICT+gorm.Expr自增、RedisHINCRBY空 key 用 Lua 护栏,说明写过代码而不只是背概念。 - 有吞吐意识:Kafka 批量生产/消费、按
biz+biz_id聚合、事务合并刷盘,对 2 年经验来说是加分项。 - 一致性取舍有业务理由:承认最终一致、高热内容允许误差,比死磕「强一致」更成熟——前提是能讲清边界。
缺点(面试官会盯的)
- 「不处理一致性」太绝对:面试官会问:DB 成功 Redis 失败?Kafka 重复消费?取消点赞与计数对不上?需要有策略(补偿、对账、幂等),不能只说「少一点点无所谓」。
- 亮点写法很伤:连续「大量使用事务/Upsert/Lua」像模板,没有场景、没有代价。事务多 ≠ 好,还可能拖慢吞吐。
- 技术细节易被打穿:
- 写库再写缓存:失败怎么回滚/补偿?
- Lua「仅 key 存在才 incr」:key 不存在时计数会丢吗?靠查询回源还是异步重建?
- Kafka 聚合:幂等键、乱序、部分失败重试怎么做?
- 「防止 MVCC 刷盘」表述不严谨,容易被纠正。
- 缺个人态查询设计:只说不缓存「是否点赞」,但列表页如何批量查个人态?N+1?位图/本地缓存?没写。
- 表达与文档质量差:
优选/check-dothing、front matter 的xxxx、标点混乱,会降低「能讲清楚」的印象。
建议(按面试口述改)
- 改成 STAR 口述结构:背景(互动服务压力)→ 方案(表 + Kafka + Redis)→ 难点(空 key、重复消费)→ 结果(QPS/延迟/错误率,哪怕是压测数字)。
- 准备 3 个追问标准答:
- DB/Redis 不一致:异步对账 + 定时校准热点 key
- Kafka 至少一次:消费幂等(唯一键 + Upsert)
- 取消点赞:
status翻转 + 计数GREATEST(cnt-1,0),防负值
- 删掉「大量使用」,改成「在点赞写明细+改计数时用事务;计数更新用 Upsert;缓存用 Lua 保证 key 存在才 HINCRBY」。
- 补一张口述链路:读请求 → Redis Hash → miss 回源 DB 回填;写请求 → DB Upsert →(可选)发 Kafka → 消费者聚合写计数 → 更新缓存。
- 亮点换成可量化的 2–3 条:例如「批量聚合把热点文章写放大降到约 1/N」「Lua 避免幽灵 key」「个人态与总数分离降低缓存体积」。
面试官一句话结论
材料有「做过互动域」的骨架,适合当提纲;按现在写法直接上面试,深度和表达大概卡在 及格偏上。把一致性策略、幂等、失败路径和口述结构补齐,冲到 75–80 比较现实。