第14卷第3期 厦门城市职业学院学报 Journal of Xiamen City Vocational College V01.14 No.3 Sep.2012 2012年9月 Redis数据库在微博系统中的实践 唐 诚 (苏州工业园区职业技术学院网络信息中心,江苏苏州215123) [摘要】由于微博系统中的信息实时性和并发性,放弃传统的关系性数据库,选取基于内存key— value引擎的NOSQL数据库Redis。利用NodeJS的服务器中的服务端脚本,建立完整的消息接收和推送的 变为现实。同时在Redis中实现了与微博消息相关的key—value存储机构设计、查询和排序,进而与 前端的H1M 手机客户端进行通讯。 【关键词]NOSQL;消息推送;key—value;HTML5; [中图分类号】TP 312 [文献标识码]A [文章编号]2095—2724(2012)03—0055—05 一、选择适合微博系统的数据库 二、Redis的键值的格式定义 Redis的数据在内存中始终是以key值作为 微博系统其实是一个类似群聊的庞大在线聊 天室,由于每时每刻都有大量的实时消息产生, 并且需要即时反馈给各个用户,所以需要保持更 快的速度来读写数据。传统的关系型数据库在数 据量超过规模的时候,由于其自身的关系型系统 逻辑相对复杂,也就导致了读写速度的下降。 索引,value则使用字符串。所以在微博系统中, 首先需要确定的是定义key的值的格式。【2 根据关系型数据库的表字段定义,我们可以 仿照其表名和字段名的方法,用下面的格式来对 key进行定义: 键值对名称:索引标示:value的字段名 比如一个键值对的名称是A,这个键值对保 Redis数据库为了提高效率,把数据都保存 在内存中,或被配置为使用虚拟内存。通过两种 方式可以实现数据持久化:使用快照的方式,将 内存中的数据不断写入磁盘;或使用类似 MySQL的日志方式,记录每次更新的日志。… 存所有人发过的消息,因此对于这个键值对中的 索引标示就是人的ID,如果某个人的ID是25, 他发过的消息是“你好”,那这个键值对的key 格式就用 Key=A:25:MessageContent 对于key—value的存储模式,重点是加速了 键值和内容的快速定位和排序,而且更有利于接 近分布式理论的Consistency(一致性)、Availa- bility(可用性)、Tolerance to network Partitions Value=“你好” 由于ID=25是个唯一的标示,所以对于这 (分区容错性)这三个要求。在构建微博系统 中,可以把软件模型定义实体分解到多个key— value中。 个key也是唯一。 [收稿日期]2012-06-02 [作者简介]唐诚(1977一),男,重庆人,软件工程硕士,苏州工业园区职业技术学院工程师。 ・55・ 厦门城市职业学院学报 2012矩 三、微博系统中的key—value存储设计 【一)微博架构中的角色对象 参与微博的角色可以分为普通人、粉丝、明 星,下面逐一进行分析。 1.普通人 每个微博的注册人都是普通人,每个人都有 一个完整的唯一的在微博系统中的ID号,他作 为微博系统的推动者,也就是发布人。所以发布 人ID是从其他数据库中得到的人的标识值,比 如从其他OAuth2.0协议认证处,或是校园内部 的人员系统数据中得到。 2.粉丝 成为了某个人发布消息的忠实听众,一个普 通发布者当他选择了他所要求的关心的其他人 时,他就从普通发布者变成了粉丝。在Redis中 当普通人成为粉丝后,也就是他订阅了某个人的 信息。 3.名星 一个被其他人关注了订阅的人。一个明星也 可能是其他人的粉丝。 综上所描述,我们要根据以上3种角色,设 计Redis的键值对。 人物关系键值对A=[key=发布人ID, value=发布人ID] 对于粉丝和明星我们只需要把key和value 互相颠倒就可以了,也就是把一个多对多的关系 用key—value值来保存,不同的情况是如果是查 找明星有多少粉丝,就用明星ID作为key,如 果查找粉丝关注了多少明星,就用粉丝的ID作 为key来查找。 这样设定的键值对就存在两种key,可以使 用约定的键值样式设计,如下: 键值对名称:发布人ID:粉丝ID 键值对名称:发布人ID:明星ID 比如ID标示为5的一个发布者的拥有一个 ID为l8的一个粉丝,那内存中就保存如下形 式: [Key A:5:fansID,value=18] 对于所有的发布人的详细信息,我们可以用 传统的关系型数据库或是NoSQL文档型数据库 ・56・ 如MongoDB等来保存持久的信息值。除非客户 端需要数据,我们再进行查询和返回信息。 (二)微博中消息对象 1.发布的消息和回复的评论 发布的消息具有自己的唯一标识,消息内 容,发布人和创建时间等基本内容。回复的评论 也具有自己的唯一标识,消息内容,以及回复人 和创建时间等基本内容。 】 除了以上两种,消息还可以进行转发,也就 是消息嵌套消息,但是可以把转发的内容放入到 一条新的消息中,不用进行递归嵌套,只需要显 示出转发的来源。 综上所述,需要设计3个键值对。 总的消息表B=[key=消息ID,value=消 息内容] 消息关系表C=[key=消息ID,value=评 论消息ID] 类似于粉丝和明星的关系,如果需要知道一 个消息的评论,就把消息ID作为key来查找评 论,同样如果想知道一个评论的评论,当然可以 把一个评论的ID作为key来进行继续查找。 总的消息表是一个巨大的消息集合,它只记 录所有的发布消息,保证所有的消息实体只有一 个。对于消息的保存,由于消息是拥有多个属性 的实体,比如消息的发布时间,消息的发布内 容,消息发布的位置信息等,因此我们需要把大 的实体,分解成多个单一的key—value键值集 合。Redis的Hash数据能存储一个key拥有多个 属性的数据,把这样的hash数据类型运用到消 息总表上十分合适。使用hmset命令去放人每个 消息内容,比如: Redis.hmset(消息ID, {消息内容:con— tent,消息地址:address}); 对于每个消息内容,采用JSON数据格式, 可以完全应对任何NodeJS服务端的数据内容。 2.消息的时间 对于微博系统,消息的显示顺序关系着整个 微博系统的用户体验。消息在到达服务器上的时 候所处的时间,需要进行记录,在显示消息的时 候,需要按照先后顺序显示出来。 第3期 唐诚:Redis数据库在微博系统中的实践 3.消息的接收 对于传统的关系型数据库,是把时间作为一 个字段,与消息的实体相对应。对于Reds数据 库,需要单独创建一个键值保存消息的时间,便 于后面的显示排序。所以创建一个消息的时间键 值表如下: 对于非自己发送的消息,比如订阅的偶像的 消息,需要建立一个键值对G 发布人和接收消息关系键值对G=[key= 发布人ID vlaue=消息ID] 当我们订阅的偶像在某个时刻发出消息后, 消息时间键值表D=[key=消息ID,value =消息发布时间] (三)消息和发布人的关系 1.消息与人的关系 由于微博系统中,每个人需要看到自己发出 的消息,每个消息还要能看到是谁发布的,所以 对于Redis数据库,需要设计2个键值对。 消息和消息发布人关系键值对E=[key= 消息ID,value=消息发布人ID] 消息发布人和消息关系键值对F=[key= 消息发布人ID,vlaue=消息ID] 结合总的消息表键值和发布人的关系键值, 这样就把部分关系数据库中的多对多关系完整引 入到NOSQL的Redis数据库中。需要注意的是, 同一个消息发布人他可以发布多条消息,对于F 键值对的key,在大多数时候是1对多的关系, 所以采用Redis的sadd命令把多个value加入相 同的key键值。 2.消息的推送和订阅机制 每个消息发布者要让粉丝能看到自己发布的 消息,自己就必须作为消息的推送人。 每个消息发布人要看到自己关注的明星的消 息,自己就必须去订阅消息。 基于以上两点,在Redis初始连接的时候, 创建一个订阅端和发布端。在我们的微博系统中 如果一个发布者在键值对A中发现自己有了粉 丝,就完全可以使用发布端的publish命令。同 时一个发布者也可以随时订阅自己的明星,让订 阅端使用subscribe命令来订阅自己的偶像。 比如ID为5的人在客户端订阅了ID为18 的人,服务端需要记录相互关系如下: [key=A:5:loverID value=18] [key A:18:fanslD value=5] 这样通过不同的key值就可以得到人的互相 关系。 根据Redis的推送消息功能,粉丝也就能立刻接 受到该消息,所以此时的发布人ID就是记录粉 丝自己。 四、微博系统中的消息创建和显示 通过上述的8个键值对,可以创建消息,得 到所有的自己的消息和自己偶像发出的消息。在 NodeJS服务端使用了Redis模块,能够使用联合 命令得到相关的数据。 (一)创建自己的消息 客户端提交来的消息内容,需要依次在Re— dis中记录。首先需要通过Redis的incr命令创 建唯一的消息标示,比如一个由发布人ID为6 的人提交的消息,同时Redis已经创建了消息标 示messagelD=12,那内存中数据储存把发布人 ID作为索引标示,应该为: [key “F:6:messageID”,12] [key “B:12”value={content:消息内 容}] 对于B键值对的value,这里可以采用jSOn 数据,还可以把消息发布人的ID也保存进去, 但如果为了更小的粒度划分,我们可以增加E 键值对,如下所示: [key=“E:12:personID”,6] 在记录消息内容后,还需要记录消息在服务 器上到达的时间,比如时间是2012—3—3 13: 00 [key=“D:12:publicTime”value= “2012—3—3 13:00”] 这样通过F,B,D 3个键值对,我们就记 录了一个完整的消息,以备后面的查询。 (二)显示自己的消息 1.消息合并 F和G两个键值对的合并的结果是发布人所 能看到的消息。首先我们使用Redis的sunion一 ・57・ 厦门城市职业学院学报 store命令把两个键值对进行合并,同时查询属 于发布人的消息。 2012焦 安装包创建了一个收发和推送的两个客户端连 接。 vat redisClientGet=redis.createClient(re— 比如下面的javaseript伪代码脚本,得到了 发布人ID为6的人的所有的消息: dis端口号,redis主机IP); var redisClientPut=redis.createClient(redis Redis数据库对象.sunionstore(合并的键值 对,“F:6:messagelD”,“G:6:messagelD”, Function(eli",union) { 对合并的union进行处理 } }); 2.消息的时间排序 得到所有消息后,可以对曾经合并的消息集 合通过Redis的sort命令进行时间排序。NodeJS 的服务端javascript伪代码如下: Redis数据库对象.sort(合并的键值对,” b , [d: :publieTime],”dese”,”alpha”, function(eft,vMues){ 对所有符合条件的消息依次返回给客户 端显示 } ); 由于我们的消息时间是以字符串的形式保 存,所以这里我们通过对字符串的数字先后排序 得到了消息的先后。 五、微博服务端的部署 (一)Redis专用数据库服务器 专门使用一台大容量内存的服务器作为Re. dis数据库。前面所描述的A到G的关系集合保 存所有的微博信息。对于该台数据的端口号和地 址都被登记在微博web服务器NodeJS的脚本中。 (二)微博web服务器 为了达到实时响应的效果,web服务器采用 了NodeJS的expressJS框架,对于客户端的网页 所提交的页面请求,使用json数据格式返回相 关的信息。 1.消息广播的实现 在web服务的nodes脚本中,对于Redis的 微博消息队列的广播,通过NodeJS的Redis npm .58・ 端口号,redis主机iP); 连接redisClientGet用于登记自己关心的偶 像,通过如下的方法: redisClientGet.subscribe(偶像的标示); 而每当偶像自己有消息发布的时候,就用 redisClientPut去发布消息,通过如下的方法: redisClientPut.publish(偶像的标示,偶像 当前发的消息ID); 2.expressJS框架针对网页客户端提交 每次网页通过ajax的提交,比如消息的发 布和设置修改,expressJS首先设置相关的提交路 由,然后在处理过程中,把客户端的数据插入到 Redis数据库中。 六、微博系统的客户端的部署 由于微博消息的创建,属于客户端的操作, 在整个微博系统的架构中,为微博系统设计了网 页和手机端两种客户模式。 (一)手机客户端的建立 对于手机上的客户端,鉴于在android和苹 果iOS上的系统跨平台,主要采用了html5+ phonegap的webapp方式。客户对自己的微博浏 览以及微博的发送,均采用ajax无刷新响应。 为了能让客户实时得到最新的消息,采用了ht— m15的websocket通讯。 对于html5的部分,主要是采用了jQuery— Mobile框架,在每个jQueryMobile框架的初始化 事件中,调用websocket与服务端的soket服务器 进行连接。连接成功后,当用户登录微博系统的 账号后,自动建立socket套接,通过消息的应 答,与服务端进行通讯。 (二)网页客户端的建立 对于网页客户端,主要是采用了微软的 MVC架构。每次与NodeJS服务端的expressjs框 架连接以及提交数据,全部通过ajax的提交方 式。 第3期 唐诚:Redis数据库在微博系统中的实践 七、Redis的深入运用 态,他的相关消息也可以滞后读取。 对于大型的微博系统,如果在数据量大的情 此外,当微博的消息中加入了大量的视频, 况下,上述的消息数据的键值对可以进行更细致 图像和音频后,这些惰性的媒体数据可以通过 的划分。比如当前内存中的部分数据可以存在过 NoSQL的文档型数据库异步保存和读取,保证 期的控制,如果有一部分微博消息是已经处于不 在Redis数据中只保存相关的索引。在这样的情 活动状态,可以通过时间筛选,将其换出内存。 况下,Redis可以充分发挥它的索引和查询机制。 同样对于部分用户,如果在长期处于不活动的状 [参考文献] [1]维基百科.Redis[EB/OL].2011—02—15[2012—06—12].http://zh.wikipedia.ors/ wiki/Redis. [2]hoterran.浅谈Redis数据库的键值设计[EB/OL].2011—09—06[2012—06—23].ht. tp://www.hoterran.i ̄o/mdis_kvdesign. _[3]李锦星.完全用nosql轻松打造千万级数据量的微博系统[EB/OL].2009—06—17[2012一 o7—12].http://wenku.baidu.com/view/a770f6878762caaedd33d4ft.htm1. [责任编辑:郭常斐] ・59・