标题 | 基于Redis的微博系统基本功能设计 |
范文 | 李磊 摘要:微博系统对信息实时性和并发性的要求,传统的关系型数据库无法满足性能要求。Key-Value模型的内存数据库Redis,非常适合微博系统对数据快速存取的需要。 关键词:Redis;Key-Value;微博 中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)25-0061-03 Abstract:Microblog system requirements for information-real-time performance and concurrency. Traditional relational database does not meet performance requirements. Key-Value model memory database Redis, very suitable for the microblog system with fast access to the data needed. Key words:Redis; Key-Value; Microblog 1 概述 微博系统类似于一个群聊的庞大聊天室,每时每刻都会有大量的消息产生,而且产生的消息会反馈给需要的用户,这样就要求数据的读写非常快。关系型数据库在数据量超过一定规模时,由于自身逻辑相对复杂,在信息检索上无法满足用户的体验。 Redis数据库本身的数据就放在内存中,而且有合适的数据结构。Twitter、新浪微博都是目前最大的Redis用户。 2 Redis介绍 Redis是一个速度非常快的非关系型数据库。Redis可以存储键(Key)与5种不同类型的值(Value)之间的映射,可以将存储在内存中的键值对持久化到硬盘。Redis还可以使用复制特性来扩展读性能,使用客户端分片来扩展写性能。 其重要特性如下: ① 持久化:Redis定期把数据异步flush到硬盘进行保存。服务器重启,数据不会丢失。 ② 主从复制:主要用1台服务器进行数据备份与恢复。 ③ Vitual Memory功能:物理内存毕竟是有限的,这技术主要是把很少用的Value保存到硬盘,而Key保留在内存做检索,提高访问性能。 ④ 多种数据结构支持:Redis的Key是string类型,Value的类型有:string、set、list、zset(sorted set)、hash。针对每种数据类型,还提供相应的操作命令,比如list类型有LPOP、LPUSH、RPOP、RPUSH等操作,set类型SDIFF(差运算)、SINTER(交运算)、SUNION(并运算)等操作①。 3 PHP和Redis构建微博系统基本功能 利用PHP的Redis扩展,在PHP中实现微博系统新用户的创建、消息发布、关注与粉丝设计、消息推送等基本功能。PHP版本为5.5.12,Redis版本为3.0.501。 3.1 用户信息表示 Redis hash是一个string类型的field和value的映射表.一个key可对应多个field,一个field对应一个value。将一个对象存储为hash类型,较于每个字段都存储成string类型更能节省内存。新建一个hash对象时开始是用zipmap(又称为small hash)来存储的。这个zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销。尽管zipmap的添加,删除,查找都是O(n),但是由于一般对象的field数量都不太多。所以使用zipmap也是很快的,也就是说添加删除平均还是O(1)。如果field或者value的大小超出一定限制后,Redis会在内部自动将zipmap替换成正常的hash实现.。 这里我们在数据库中表示用户信息和发布的消息都用Redis的hash结构。用户信息如表1。 创建新用户时,我们用到一个user:id:的计数器,实际就是Redis的一个Key,初始一个值,然后每次添加到user:uid的hash后值要自增1。用用户信息中login和id两个filed的值构造另一个hash表users:,用来建立login和id之间的映射。关键代码如下: if($redis->hget("users:",$login)){echo "{$login}已经存在,重新输入";} else{$uidarray=$redis->multi()->incr("user:id:")->exec(); $uid=$uidarray[0]; $userinfo=array( "login"=>$login, "id"=>$uid, "name"=>$name, "following"=>0, "fans"=>0, "posts"=>0, "signup"=>time()); $redis->multi()->hset("users:",$login,$uid) ->hmset("user:{$uid}",$userinfo)->exec();} 3.2 发布的消息表示 用户发布的消息也用hash表示,结构如表2。 发布消息时候,也用到一个计数器message:mid:,其值传给消息中的mid,然后自增1,保证每个消息都有不同的mid。发布消息时,不仅要添加message:mid一个新的值,还要修改user:uid中的posts域的值。关键代码如下: $midarray=$redis->multi()->incr("message:mid:")->exec(); $mid=$midarray[0]; $messageinfo=array( "content"=>$content, "time"=>time(), "mid"=>$mid, "uid"=>$uid, "login"=>$login); $redis->multi()->hmset("message:{$mid}",$messageinfo) ->hincrby("user:{$uid}","posts",1)->exec(); 3.3 用户主页时间线和个人时间线 Redis提供zset这种有序集合数据结构。通过zadd命令添加的成员,按照score的值排序,默认score的值递增。在微博系统中利用该数据结构的特点,可以很方便的取出最新的消息。 用户主页时间是指,当用户登录后,能看到用户以及用户关注的人所发布的消息列表,这个列表以发布消息的时间排序。在Redis中用户主页时间线结构如表3。 用户个人时间线仅仅只有用户个人发布的消息列表,也是以发布时间排序。用户个人时间线profile:uid结构如下表4。 3.4 关注者列表和粉丝列表 微博系统就是要让用户之间分享各自的构想、想法。当A用户开始关注或取消关注B用户的时候,我们不仅要更新A用户的关注列表following:A和A用户的个人信息中关注数量following的值,还要更新B用户的粉丝列表fans:B和B用户的个人信息中粉丝数量fans的值。最后把B用户发布的消息,profile:B中的消息,更新到A用户的主页时间线home:A。两个列表都用Redis的zset有序集合结构表示。表5为关注者列表结构,表6位粉丝列表结构。 开始关注操作关键代码如下: define("HOME_TIMELINE_SIZE",1000); $fkey1="following:{$uid}"; $fkey2="fans:{$other_id}"; $have=$redis->zscore($fkey1,$other_id); if($have==true){ echo "{$uid}已经关注{$other_id}";} else{$time=time(); $values=$redis->multi()->zadd($fkey1,$other_id,$time) ->zadd($fkey2,$uid,$time) ->zrevrange("profile:{$other_id}",0,HOME_TIMELINE_SIZE-1,true) ->exec(); $redis->multi()->hincrby("user:{$uid}","following",$values[0]) ->hincrby("user:{$other_id}","fans",$values[1])->exec(); if($values[2]){//获取$other_id发布的消息不为空 foreach($values[2] as $key=>$value) { $redis->multi()->zadd("home:{$uid}",$value,$key)->exec();}}} 取消操作关键代码如下: define("HOME_TIMELINE_SIZE",1000); $fkey1="following:{$uid}"; $fkey2="fans:{$other_id}"; $have=$redis->zrangebyscore($fkey1,$other_id,$other_id,array(true,1)); if($have==false){echo "{$uid}没有关注{$other_id}";} else{$values=$redis->multi()->zrem($fkey1,$other_id) ->zrem($fkey2,$uid) ->zrevrange("profile:{$other_id}",0,HOME_TIMELINE_SIZE-1,true) ->exec(); $redis->multi()->hincrby("user:{$uid}","following",-$values[0]) ->hincrby("user:{$other_id}","fans",-$values[1])->exec(); if($values[2]){//获取$other_id发布的消息不为空 foreach($values[2] as $key=>$value) {$redis->multi()->zrem("home:{$uid}",$key)->exec();}}} 3.5 消息推送 在3.2节里面说明的是消息发布后,除了进行消息信息添加以外,用户个人信息中发布消息数量posts值得自增。我们还应该接着把发布的消息告诉给个人时间线和主页时间线,也就是在profile:uid(uid为发布消息的用户id)中添加消息编号mid和时间戳time,并且在home:uid(uid为发布消息的用户id)中也添加消息编号和时间戳time,这个时间戳应该是消息发布的时候服务器的时间戳。然后还要在粉丝的主页时间线home:uid(发布消息用户的粉丝id)中添加同样的数据。由于微博系统中有的用户粉丝数量非常大,如果同步更新可能会导致用户长时间等待。所以,在更新的时候,可以先更新fans:uid(uid为发布消息的用户id)集合中前面1000个关注者,对每个关注者的home:uid进行更新。关键代码如下: $redis->multi()->zadd("profile:{$uid}",$time,$mid) ->zadd("home:{$uid}",$time,$mid)->exec(); $fans=$redis->zrevrange("fans:{$uid}",0,1000,true); foreach($fans as $key=>$value) { $redis->zadd("home:{$value}",$time,$mid);} 如果存在超过1000个用户的情况,可以设计一个延迟功能来进行转发,避免发布消息的用户长时间等待。 4 总结和展望 Redis本身提供了很多的数据结构,灵活应用可以构造适合微博系统的数据库。这里我们搭建了php+redis环境,做一个简单的微博系统,实现基本功能。要开发像Twitter、sina微博等系统,还要考虑更复杂的数据构造实现更多的功能,以及如何扩展服务器来提高服务质量。 参考文献: [1] 唐诚.Redis数据库在微博系统中的实践[J].厦门城市学院学报,2012,14(3):55-59. [2] Josianh L Carlson.Redis实战[M].北京:人民邮电出版社,2015. [3] 王艳,董丽丽.NoSql与关系数据库相结合的设计与实践[J].电脑知识与技术, 2014(9). |
随便看 |
|
科学优质学术资源、百科知识分享平台,免费提供知识科普、生活经验分享、中外学术论文、各类范文、学术文献、教学资料、学术期刊、会议、报纸、杂志、工具书等各类资源检索、在线阅读和软件app下载服务。