Redis中点赞和收藏的数据结构应如何设计?

来源:SQLite教程作者:葵司头衔:网络博主
导读:本期聚焦于葵司创作的《Redis中点赞和收藏的数据结构应如何设计?》,敬请观看详情。设计点赞收藏功能时,Redis提供了多种数据结构可供选择,但不同方案的内存占用和性能表现差异明显。Set集合实现简单、支持去重,适合中小规模场景;Hash结构能存储点赞时间等元数据,灵活性更高;BitMap则将内存消耗降到极致,适合海量用户但用户ID需连续。本文从实际业务出发,对比这几种方案的命令用法、内存模型和适用边界,并给出数据一致性处理和混合选型建议,帮助开发者根据自身规模选择最合适的存储策略。

点赞和收藏是内容平台、社交应用中最常见的互动功能,数据量随用户规模线性增长。Redis凭借内存读写和多数据类型支持,常被用来承载这类高并发、低延迟的计数与状态存储。但选错数据结构会导致内存暴涨、命令复杂度升高,甚至影响线上稳定性。本文从实际操作层面拆解Set、Hash、BitMap三种核心方案,分析各自优劣,并讨论与数据库同步时需要注意的问题。

Redis中点赞和收藏的数据结构应如何设计?

使用Set集合实现基础点赞与收藏

Set是Redis中最直观的点赞存储方案。对于每一条内容,用一个Set保存所有点赞用户的ID,key形如like:article:1001,成员为user:10001这样的用户标识。点赞操作使用SADD,取消点赞使用SREM,判断是否点赞使用SISMEMBER,统计总数使用SCARD。收藏功能完全同理,只需把key前缀换成favorite即可。这种设计最大的好处是简单,开发人员一看就懂,配合Redis的集合运算还能轻松实现“共同点赞”、“我点赞的人还赞了哪些内容”等关系查询。

不过Set的缺点也很明显:内存占用偏高。Redis的Set在成员数量较少时会使用紧凑的整数集合(intset)编码,当成员数量超过set-max-intset-entries(默认512)后转为哈希表编码,每个成员要额外存储指针和哈希节点,内存膨胀明显。假设一篇文章有10万点赞用户,每个用户ID平均25字节,仅这一篇文章的点赞Set就可能占用数MB内存。对于千万级内容平台,整体内存开销会非常惊人。因此Set更适合用户量不大、内容数量有限的中小型应用,或者作为过渡方案快速上线。

代码示例:

# 用户10001点赞文章2001
SADD like:article:2001 user:10001
# 用户10002点赞同一篇文章
SADD like:article:2001 user:10002
# 检查用户10001是否点赞
SISMEMBER like:article:2001 user:10001
# 返回 1 表示已点赞
# 统计点赞总数
SCARD like:article:2001
# 返回 2
# 取消点赞
SREM like:article:2001 user:10001

使用Hash结构存储扩展属性

当业务不仅需要知道“谁点了赞”,还要记录点赞时间、取消时间或者用户的其他状态时,Hash就比Set更合适。一种常见做法是把内容ID作为Hash的key,比如like:article:2001,Hash内部的field为用户ID,value为点赞发生的时间戳或一个JSON字符串。这样一条点赞记录就包含了完整的上下文信息,后续产品分析、用户行为追踪都很方便。Redis的HSET、HGET、HEXISTS、HLEN等命令可以直接操作。

Hash的另一个优势是可以利用HINCRBY对单个用户做计数,也可以使用HSCAN分批遍历所有点赞用户,避免像Set那样一次性SMEMBERS可能阻塞Redis。但Hash的value字段会消耗额外内存,如果value只是简单的1或0,相比Set会多出不少开销。如果value存储了时间戳等有意义的数据,那么这部分开销是值得的。在实际项目中,我们常把点赞和收藏合并到一个Hash里,例如interact:article:2001包含两个field:like:user:10001和favorite:user:10001,value分别为对应的时间戳。这样一次HGETALL就能取回用户对某内容的所有互动状态,减少网络往返。

代码示例:

# 用户10001在时间戳1712345678点赞文章2001
HSET like:article:2001 user:10001 1712345678
# 判断是否点赞
HEXISTS like:article:2001 user:10001
# 返回 1
# 获取点赞时间
HGET like:article:2001 user:10001
# 返回 "1712345678"
# 批量获取多个用户点赞状态(使用HMGET)
HMGET like:article:2001 user:10001 user:10002 user:10003
# 返回 ["1712345678", nil, nil]

使用BitMap极致压缩内存

如果业务强调海量用户下的内存控制,并且用户ID是连续的正整数(或者能通过外部映射表转换为连续ID),BitMap是最佳选择。BitMap本质上是字符串,每一位代表一个用户,1表示已点赞,0表示未点赞。例如key为like:article:2001,offset为用户ID(如10001),使用SETBIT设置位,GETBIT读取位,BITCOUNT统计总点赞数。这种方案的内存占用极小:1亿用户只需要约12.5MB,与Set相比减少了数十倍。

BitMap的局限也很明确。首先用户ID必须连续,如果系统中用户ID是UUID或随机字符串,需要额外维护一张“用户ID到连续整数”的映射表,这又会引入一致性维护成本。其次BitMap无法存储额外信息,点赞时间、取消原因等数据只能另存他处。此外,BitMap的读写虽然都是O(1),但BITCOUNT在非常大的字符串上执行时可能耗时较长,需要注意在Redis实例上避免频繁大范围统计。实际使用中,BitMap常配合Redis的BITOP做多个内容的集合运算,比如计算多个文章的共同点赞用户,这在推荐场景中非常有用。

代码示例:

# 假设用户ID为10001,设置该用户点赞位为1
SETBIT like:article:2001 10001 1
# 读取该用户是否点赞
GETBIT like:article:2001 10001
# 返回 1
# 统计该文章总点赞数
BITCOUNT like:article:2001
# 返回 1
# 批量设置多个用户点赞(使用BITFIELD或多次SETBIT)
SETBIT like:article:2001 10002 1
SETBIT like:article:2001 10003 1

混合方案与数据一致性处理

真实业务中很少只用单一结构从头走到尾。比较常见的策略是:核心状态用Set或Hash存储以保证灵活性和可读性,同时维护一份BitMap用于高效统计或去重计算;或者将点赞和收藏分开处理,点赞用BitMap节省内存,收藏用Set因为收藏行为通常伴随列表展示需求。还有一种做法是Redis只存近期热点数据,冷数据定期归档到关系型数据库或对象存储,降低内存压力。无论哪种混合方式,都要提前规划好key的命名规范、过期策略以及数据迁移脚本。

数据一致性是缓存场景的老大难。点赞操作典型的流程是:先写数据库,再更新Redis;或者先更新Redis,再异步落库。前者可能因为数据库成功但Redis更新失败导致缓存不一致,后者可能因为Redis成功但消息丢失导致数据丢失。解决思路通常包括:使用Redis事务(MULTI/EXEC)或Lua脚本保证多个Redis命令原子执行;数据库更新采用可靠消息队列保证最终一致;定期对账任务扫描数据库和Redis的差异并修复。对于点赞这种允许短暂不一致的场景,优先保证可用性,通过过期时间加补偿机制即可满足大多数需求。

最后要提醒的是,所有涉及用户ID的存储都要注意隐私和合规,避免将敏感信息直接作为key或field暴露。设计数据结构时预留版本号或命名空间,方便后续平滑升级。选型时先用小规模数据压测不同方案的实际内存占用和命令耗时,再结合业务增长预期做决定,比盲目套用网上方案要可靠得多。

Redis点赞收藏数据结构修改时间:2026-09-30 03:05:16

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0930/63658.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。