点赞收藏功能:从初创公司到BAT的技术选型实战

简介: 别被复杂架构忽悠!本文用大白话讲解不同发展阶段的技术选型:创业公司重速度,成长期用Redis提升性能,成熟期上多级缓存,大厂级应对高并发。结合真实场景与代码,教你选最合适的方案,避免过度设计。适合开发者进阶与面试备战。(238字)

别再被高大上的架构忽悠了!真实业务场景下的技术选型指南

不知道大家在实现功能的时候,有没有考虑过到底应该用Redis还是直接写MySQL?可能刚开始干开发的时候,大家不需要考虑,领导怎么说就怎么干。但是如果想要逐渐成长为独当一面的人才,这些是一定要考虑的,再不济在面试的时候也可以应付一下面试官。

今天我就用大白话,告诉你不同阶段该用什么方案。

一、创业公司:怎么快怎么来

场景: 你们团队5个人,产品刚上线,每天就几百个用户

核心诉求: 快速上线验证业务,别整复杂了

基础数据模型设计

-- 文章表
CREATE TABLE article (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    like_count INT DEFAULT 0,  -- 点赞数
    favorite_count INT DEFAULT 0, -- 收藏数
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 用户行为表(最简版本)
CREATE TABLE user_actions (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    article_id BIGINT NOT NULL,
    action_type ENUM('like', 'favorite'), -- 操作类型
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_article (user_id, article_id)
);
// 方案1:最简数据库实现(推荐)
@RestController
public class BasicLikeController {
   

    @PostMapping("/articles/{id}/like")
    public ResponseEntity<?> likeArticle(@PathVariable Long id, 
                                       @RequestParam Long userId) {
   
        // 1. 检查是否已经点赞
        boolean hasLiked = userActionRepository.existsByUserIdAndArticleIdAndActionType(
            userId, id, "like");

        if (hasLiked) {
   
            return ResponseEntity.badRequest().body("已经点过赞了");
        }

        // 2. 开启事务
        return transactionTemplate.execute(status -> {
   
            // 3. 更新文章点赞数
            articleRepository.incrementLikeCount(id);

            // 4. 记录用户行为
            UserAction action = new UserAction();
            action.setUserId(userId);
            action.setArticleId(id);
            action.setActionType("like");
            userActionRepository.save(action);

            return ResponseEntity.ok("点赞成功");
        });
    }
}

为什么选这个方案?

  • 开发快:2小时搞定
  • 不用学新技术:Java开发都会写SQL
  • 好排查:数据都在数据库,查问题方便
  • 成本低:不用买Redis,省服务器钱

可能遇到的问题:

  • 如果突然有个帖子火了,几千人同时点赞,数据库可能会卡
  • 用户多了以后,查询会变慢

解决方案: 先上线,等真遇到问题再说!创业公司最重要的是活下去,不是提前优化。

二、成长型公司:用户破10万了

场景: 你们日活过万,经常有热门内容,数据库开始报警

核心诉求: 保证系统稳定,用户体验要好

// 方案2:Redis缓存 + 异步落库(性价比最高)
@Service
public class LikeServiceV2 {
   

    public void likeArticle(Long articleId, Long userId) {
   
        String userLikeKey = "user_like:" + userId + ":" + articleId;

        // 先用Redis判断是否点过赞(比查数据库快100倍)
        if (redisTemplate.hasKey(userLikeKey)) {
   
            throw new RuntimeException("您已经点过赞了");
        }

        // Redis原子操作增加点赞数(不会出现并发问题)
        String countKey = "article_like_count:" + articleId;
        redisTemplate.opsForValue().increment(countKey);

        // 记录用户点赞关系(30天过期,防止内存爆掉)
        redisTemplate.opsForValue().set(userLikeKey, "1", 30, TimeUnit.DAYS);

        // 异步写到数据库(就算数据库挂了,点赞数还在Redis里)
        CompletableFuture.runAsync(() -> {
   
            saveToDatabase(userId, articleId);
        });
    }

    // 获取点赞数:先查Redis,没有再查数据库
    public Integer getLikeCount(Long articleId) {
   
        String countKey = "article_like_count:" + articleId;
        String count = redisTemplate.opsForValue().get(countKey);
        if (count != null) {
   
            return Integer.parseInt(count);
        }

        // 缓存没有,从数据库查并回填缓存
        Integer dbCount = articleRepository.getLikeCount(articleId);
        redisTemplate.opsForValue().set(countKey, dbCount.toString(), 1, TimeUnit.HOURS);
        return dbCount;
    }
}

为什么选这个方案?

  • 性能提升:Redis比MySQL快得多
  • 用户体验:点赞响应速度从100ms降到10ms
  • 成本可控:买个几百块的Redis够用了
  • 风险可控:Redis挂了还能降级到数据库

部署要点:

  1. Redis一定要设置内存淘汰策略
  2. 记得设置Key过期时间
  3. 做好Redis监控,内存满了及时报警

三、成熟公司:准备迎接百万用户

场景: 你们已经是细分领域头部,每天百万UV,准备融资打市场

核心诉求: 系统要稳定,能抗住流量冲击,不能宕机

核心数据模型优化

-- 优化后的用户行为表
CREATE TABLE user_actions (
    id BIGINT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    article_id BIGINT NOT NULL,
    action_type TINYINT NOT NULL, -- 1:点赞 2:收藏 3:取消点赞
    client_ip INT UNSIGNED, -- 客户端IP(整数存储)
    user_agent_hash BINARY(16), -- 用户代理哈希
    created_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3), -- 毫秒级时间戳
    updated_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),

    -- 复合主键(用于分库分表)
    PRIMARY KEY (user_id, article_id, action_type),
    -- 索引设计
    INDEX idx_article_action (article_id, action_type, created_at),
    INDEX idx_user_action (user_id, action_type, created_at),
    INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY HASH(user_id) PARTITIONS 32; -- 分表
// 方案3:多级缓存 + 消息队列 + 容灾降级
@Service
public class LikeServiceV3 {
   
    // 本地缓存(Caffeine),比Redis还快
    private Cache<Long, Integer> localCache = Caffeine.newBuilder()
        .maximumSize(10000)
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .build();

    public LikeResult likeArticle(LikeRequest request) {
   
        // 1. 频率限制:防止刷赞
        if (!rateLimiter.tryAcquire(request.getUserId())) {
   
            return LikeResult.fail("点得太快了,歇会儿吧");
        }

        // 2. 防重复提交
        String duplicateKey = "req_" + request.getRequestId();
        if (!redisLock.tryLock(duplicateKey, 5)) {
   
            return LikeResult.fail("请不要重复提交");
        }

        try {
   
            // 3. 更新本地缓存
            updateLocalCache(request.getArticleId());

            // 4. 更新Redis
            updateRedisCache(request);

            // 5. 发消息到MQ,让下游慢慢处理
            kafkaTemplate.send("like_topic", request);

            return LikeResult.success("点赞成功");

        } finally {
   
            redisLock.unlock(duplicateKey);
        }
    }
    // 读取策略:Cache-Aside
    // 获取点赞数:本地缓存 → Redis → 数据库
    public Integer getLikeCount(Long articleId) {
   
        // 先查本地缓存(纳秒级)
        Integer count = localCache.getIfPresent(articleId);
        if (count != null) {
   
            return count;
        }

        // 再查Redis(毫秒级)
        count = getFromRedis(articleId);
        if (count != null) {
   
            localCache.put(articleId, count);
            return count;
        }

        // 最后查数据库
        count = getFromDatabase(articleId);
        if (count != null) {
   
            // 回填缓存
            updateCache(articleId, count);
        }

        return count;
    }
}

配套措施:

# 部署架构
前端 → 负载均衡 → 应用集群 → Redis集群 → MySQL集群
                    ↓
                监控告警
                    ↓
                日志分析

为什么选这个方案?

  • 高可用:任何一环挂了都有备用方案
  • 高性能:本地缓存让读取速度飞起
  • 可扩展:水平扩容很容易
  • 有保障:监控告警齐全,出问题能及时发现

四、大厂级别:亿级用户架构

场景: 你是BAT级别的公司,双十一要抗住每秒几十万点赞

核心诉求: 绝对不能宕机,性能要极致,成本要优化

这种场景下,要考虑的问题就多了:

  1. 热点问题:明星发微博,瞬间百万点赞怎么办?
  2. 数据分片:数据太大,一个Redis存不下怎么办?
  3. 跨机房:北京用户点赞,上海用户要立即看到怎么办?
  4. 成本优化:每天几个亿的点赞,存储成本怎么降?
// 方案4:分布式架构 + 智能路由 + 成本控制
@Service 
public class LikeServiceV4 {
   

    public void likeArticle(LikeRequest request) {
   
        // 1. 智能路由:热点数据特殊处理
        String routeKey = routeManager.getRouteKey(request);

        // 2. 分布式锁:保证原子性
        DistributedLock lock = lockFactory.getLock(routeKey);

        try {
   
            if (lock.tryLock(1000)) {
   
                // 3. 写入时序数据库(成本低,适合日志类数据)
                timeseriesDB.writeLikeRecord(request);

                // 4. 更新实时计数(用内存计算,速度快)
                realtimeCounter.increment(request.getArticleId());

                // 5. 数据同步到各地机房
                syncManager.syncToAllDC(request);
            }
        } finally {
   
            lock.unlock();
        }
    }
}

部署架构:

用户 → CDN → 网关层 → 业务层 → 缓存层 → 存储层
      ↓        ↓        ↓        ↓        ↓
   边缘计算   限流降级  服务网格 集群分片 冷热分离

五、怎么选?看这张表就够了

公司阶段 推荐方案 开发周期 服务器成本 维护难度 适用场景
初创公司 直接数据库 1-2天 几百/月 ⭐☆☆☆☆ 验证想法,用户量小
成长公司 Redis缓存 3-5天 1-2千/月 ⭐⭐☆☆☆ 用户过万,追求体验
成熟公司 多级缓存 1-2周 3-5千/月 ⭐⭐⭐☆☆ 准备融资,要稳定性
大厂 分布式架构 1-2月 几万/月 ⭐⭐⭐⭐⭐ 亿级用户,不能宕机

六、实际踩坑经验

坑1:缓存雪崩

// 错误做法:所有Key同时过期
redisTemplate.opsForValue().set("count_123", "100", 1, TimeUnit.HOURS);

// 正确做法:过期时间加随机值
int randomExpire = 3600 + new Random().nextInt(600); // 1小时+随机10分钟
redisTemplate.opsForValue().set("count_123", "100", randomExpire, TimeUnit.SECONDS);

坑2:缓存穿透

// 错误做法:查询不存在的数据,每次都击穿到数据库
public Integer getCount(Long articleId) {
   
    String count = redisTemplate.get("count_" + articleId);
    if (count == null) {
   
        // 如果文章不存在,这里每次都会查数据库
        return articleRepository.getCount(articleId); 
    }
    return Integer.parseInt(count);
}

// 正确做法:缓存空值
public Integer getCount(Long articleId) {
   
    String count = redisTemplate.get("count_" + articleId);
    if (count != null) {
   
        if ("NULL".equals(count)) {
   
            return 0; // 缓存了空值
        }
        return Integer.parseInt(count);
    }

    Integer dbCount = articleRepository.getCount(articleId);
    if (dbCount == null) {
   
        // 数据库也没有,缓存空值,防止继续穿透
        redisTemplate.opsForValue().set("count_" + articleId, "NULL", 5, TimeUnit.MINUTES);
        return 0;
    }

    redisTemplate.opsForValue().set("count_" + articleId, dbCount.toString(), 1, TimeUnit.HOURS);
    return dbCount;
}

七、总结

技术选型就像买车:

  • 创业公司:买辆二手捷达,能跑就行
  • 成长公司:买辆新丰田,省心耐用
  • 成熟公司:买辆宝马,要有面子要舒适
  • 大厂:买车队,还要配维修团队

最好的架构不是最复杂的,而是最适合当前业务阶段的

记住三句话:

  1. 别用开飞机的技术来开拖拉机
  2. 合适的才是最好的
  3. 先上线,再优化
相关文章
|
16小时前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
55 33
|
14天前
|
人工智能 IDE 程序员
Qoder 负责人揭秘:Qoder 产品背后的思考与未来发展
AI Coding 已经成为软件研发的必选项。根据行业的调研,目前全球超过 62% 的开发者正在使用 AI Coding 产品,开发者研发效率提升 30% 以上。当然,有很多开发者用得比较深入,提效超过 50%。
223 21
|
7天前
|
Java API Apache
【Spring Boot实战】EasyPoi神技:一篇搞定“一对多+图片”导入!
本文分享使用Spring Boot + EasyPoi实现复杂Excel导入(含图片和一对多数据)的实战经验。涵盖环境配置、实体注解、一对多处理、图片上传及大文件监听器优化,解决内存溢出问题。附替代官方文档链接,助力高效开发。
|
11天前
|
SQL 人工智能 运维
一场由AI拯救的数据重构之战
本文以数据研发工程师小D的日常困境为切入点,探讨如何借助AI技术提升数据研发效率。通过构建“数研小助手”智能Agent,覆盖需求评估、模型评审、代码开发、运维排查等全链路环节,结合大模型能力与内部工具(如图治MCP、D2 API),实现影响分析、规范检查、代码优化与问题定位的自动化,系统性解决传统研发中耗时长、协作难、维护成本高等痛点,推动数据研发向智能化跃迁。
122 25
一场由AI拯救的数据重构之战
|
3天前
|
人工智能 供应链 小程序
高效赋能数字人:2025 精选工具大推荐
2025 年,生成式 AI 技术推动数字人工具从 “单一功能落地” 迈向 “全链路价值赋能”,不仅能解决 “降本增效” 的基础需求,更能助力个人与企业解锁 “场景创新”。以下精选 5 款能力差异化的数字人工具,从核心技术、适配场景、实用价值等维度拆解,帮你找到能真正落地的数字人解决方案。
|
15天前
|
人工智能 运维 Serverless
函数计算 × MSE Nacos : 轻松托管你的 MCP Server
本文将通过一个具体案例,演示如何基于 MCP Python SDK 开发一个标准的 MCP Server,并将其部署至函数计算。在不修改任何业务代码的前提下,通过控制台简单配置,即可实现该服务自动注册至 MSE Nacos 企业版,并支持后续的动态更新与统一管理。
268 29
|
13天前
|
人工智能 安全 Java
分布式 Multi Agent 安全高可用探索与实践
在人工智能加速发展的今天,AI Agent 正在成为推动“人工智能+”战略落地的核心引擎。无论是技术趋势还是政策导向,都预示着一场深刻的变革正在发生。如果你也在探索 Agent 的应用场景,欢迎关注 AgentScope 项目,或尝试使用阿里云 MSE + Higress + Nacos 构建属于你的 AI 原生应用。一起,走进智能体的新世界。
195 23
|
9天前
|
算法 定位技术 vr&ar
Rokid手势识别深度测评:从技术原理到开发实战
Rokid通过单摄像头实现高精度手势识别与空间感知,结合AI算法与多模态交互,打造轻量高效的AR解决方案。其UXR SDK提供从底层数据到应用层的完整工具链,助力开发者构建教育、工业、消费等多场景AR应用,推动自然人机交互普及。
147 13
|
15小时前
|
编解码 数据可视化 数据挖掘
空间转录组: Visium HD 数据集分析 (1)
空间转录组: Visium HD 数据集分析 (1)
47 26
空间转录组: Visium HD 数据集分析 (1)
|
11天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践
本文介绍RAG(检索增强生成)技术,结合Spring AI与本地及云知识库实现学术分析AI应用,利用阿里云Qwen-Plus模型提升回答准确性与可信度。
375 90
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践