使用Redis构建简单的社交网站
Redis能干什么?使用社交缓存、队列、建简排行榜,网站这些教科书答案我都听过。使用社交但真正让我觉得它“活”过来的建简,是网站一次给朋友写迷你社交网站的经历。那站点小得可怜,使用社交用户不超过五十人,建简可它五脏俱全——有动态流、网站有粉丝关注、使用社交有点赞。建简用MySQL当然也能做,网站但Redis让我用不到三百行代码就把事办了,使用社交而且快得离谱。建简
核心设计就一句话:把每个用户的网站时间线存成List。发一条动态,LPUSH到自己的帖子列表,同时把这条动态的ID推送给所有粉丝的feed列表。读取首页时,LRANGE拉出最近的五十条ID,再批量取哈希结构里的内容。整个过程没有一条SQL join,全是O(1)操作。朋友第一次打开页面时愣了一下,问“这真的是在跑网络请求吗?”——因为响应时间基本是零。
这里面有个细节值得琢磨:哈希结构存帖子正文,键名是post:{ id},字段是user_id、content、timestamp。但点赞数呢?我一开始用INCR,后来发现需要知道“谁赞过”,于是改成SADD把用户ID塞进集合,SCARD取数量。你瞧,同一个数据,不同访问模式,Redis里就得用不同结构。这不像关系型数据库,一张表加索引就完了——你得预先想清楚怎么读。
最让我头疼的其实是关注关系。用集合存粉丝和关注对象,FOLLOW操作就是两个SADD。听起来很简单吧?但要显示“我是否关注了他”就得SISMEMBER,要查互相关注就得SINTER。这些命令单独看都明白,组合起来却容易绕晕。我调试时踩过一个坑:取完粉丝列表后忘了用管道,导致循环里发了上百次请求。延迟从0.1毫秒飙升到30毫秒。后来改成pipeline,一次搞定——Redis快,但别浪费它的快。
哦对了,动态流的“拉模式”和“推模式”之争,我在这个小站里做了个折中。用户量小,直接推,每发一条动态就遍历粉丝列表去LPUSH。可万一有个用户有上百万粉丝呢?那这操作就卡成PPT了。换成拉模式,每次刷新时去聚合关注对象的帖子,又得处理分页和去重。真实世界没有银弹,我最后写了个定时任务,把活跃用户的时间线提前缓存,普通用户实时拉取。丑是丑了点,但够用。
数据持久化这块我吃了教训。默认配置下Redis只做内存快照,我测试时重启服务,所有帖子瞬间清零。朋友在微信上发来一串问号。后来老老实实开了AOF,appendfsync everysec,丢一秒数据可以接受,但别全没了。这个取舍很现实——纯内存方案追求极致性能,但社交网站不能拿用户数据开玩笑。
最后一件事:别把所有东西都塞Redis。搜索功能我用的是SQLite的FTS,因为Redis的搜索语法实在太绕。会话状态我放在Redis里,但用户密码哈希还在MySQL。混着用不丢人,每种工具都有它的脾气。这个项目让我明白,Redis不是万能钥匙,但当你需要处理“热数据”和“实时交互”时,它那种简单粗暴的爽快感,确实让人上瘾。