Redis是一个高效的内存数据库,它支持包括String
、List
、Set
、SortedSet
和Hash
等数据类型的存储,在Redis中一般根据数据的key查询其value值,Redis没有条件查询,在面对一些须要分页或排序的场景时(如评论,时间线),Redis就不太好不处理了。数据库
前段时间在项目中须要将每一个主题下的用户的评论组装好写入Redis中,每一个主题会有一个topicId,每一条评论会和topicId关联起来,获得大体的数据模型以下:json
{ topicId: 'xxxxxxxx', comments: [ { username: 'niuniu', createDate: 1447747334791, content: '在Redis中分页', commentId: 'xxxxxxx', reply: [ { content: 'yyyyyy' username: 'niuniu' }, ... ] }, ... ] }
将评论数据从MySQL查询出来组装好存到Redis后,之后每次就能够从Redis获取组装好的评论数据,从上面的数据模型能够看出数据都是key-value型数据,无疑要采用hash进行存储,可是每次拿取评论数据时须要分页并且还要按createDate字段进行排序,hash确定是不能作到分页和排序的。spa
那么,就挨个看一下Redis所支持的数据类型:code
String: 主要用于存储字符串,显然不支持分页和排序。blog
Hash: 主要用于存储key-value型数据,评论模型中全是key-value型数据,因此在这里Hash无疑会用到。排序
List: 主要用于存储一个列表,列表中的每个元素按元素的插入时的顺序进行保存,若是咱们将评论模型按createDate排好序后再插入List中,彷佛就能作到排序了,并且再利用List中的LRANGE key start stop
指令还能作到分页。嗯,到这里List彷佛知足了咱们分页和排序的要求,可是评论还会被删除,就须要更新Redis中的数据,若是每次删除评论后都将Redis中的数据所有从新写入一次,显然不够优雅,效率也会大打折扣,若是能删除指定的数据无疑会更好,而List中涉及到删除数据的就只有LPOP和RPOP这两条指令,但LPOP和RPOP只能删除列表头和列表尾的数据,不能删除指定位置的数据,因此List也不太适合。内存
Set: 主要存储无序集合,无序!排除。rem
SortedSet: 主要存储有序集合,SortedSet的添加元素指令ZADD key score member [[score,member]...]
会给每一个添加的元素member绑定一个用于排序的值score,SortedSet就会根据score值的大小对元素进行排序,在这里就能够将createDate看成score用于排序,SortedSet中的指令ZREVRANGE key start stop
又能够返回指定区间内的成员,能够用来作分页,SortedSet的指令ZREM key member能够根据key移除指定的成员,能知足删评论的要求,因此,SortedSet在这里是最适合的。字符串
因此,我须要用到的数据类型有SortSet和Hash,SortSet用于作分页排序,Hash用于存储具体的键值对数据,我画出了以下的结构图:get
在上图的SortSet结构中将每一个主题的topicId做为set的key,将与该主题关联的评论的createDate和commentId分别做为set的score和member,commentId的顺序就根据createDate的大小进行排列。
当须要查询某个主题某一页的评论时,就可主题的topicId经过指令zrevrange topicId (page-1)×10 (page-1)×10+perPage这样就能找出某个主题下某一页的按时间排好顺序的全部评论的commintId。page为查询第几页的页码,perPage为每页显示的条数。
当找到全部评论的commentId后,就能够把这些commentId做为key去Hash结构中去查询该条评论对应的内容。
这样就利用SortSet和Hash两种结构在Redis中达到了分页和排序的目的。