Redis 是一个高效的内存数据库,它支持包括 String 、 List 、 Set 、 SortedSet 和 Hash 等数据类型的存储,在 Redis 中通常根据数据的 key 查询其 value 值, Redis 没有条件查询,在面对一些需要分页或排序的场景时(如评论,时间线), Redis 就不太好不处理了。
前段时间在项目中需要将每个主题下的用户的评论组装好写入 Redis 中,每个主题会有一个 topicId ,每一条评论会和 topicId 关联起来,得到大致的数据模型如下:
{
topicId: 'xxxxxxxx',
comments: [
{
username: 'niuniu',
createDate: 1447747334791,
content: '在 Redis 中分页',
commentId: 'xxxxxxx',
reply: [
{
content: 'yyyyyy'
username: 'niuniu'
},
...
]
},
...
]
}
将评论数据从 MySQL 查询出来组装好存到 Redis 后,以后每次就可以从 Redis 获取组装好的评论数据,从上面的数据模型可以看出数据都是 key-value 型数据,无疑要采用 hash 进行存储,但是每次拿取评论数据时需要分页而且还要按 createDate 字段进行排序, hash 肯定是不能做到分页和排序的。
那么,就挨个看一下 Redis 所支持的数据类型:
**String: **主要用于存储字符串,显然不支持分页和排序。 **Hash: **主要用于存储 key-value 型数据,评论模型中全是 key-value 型数据,所以在这里 Hash 无疑会用到。 **List: **主要用于存储一个列表,列表中的每一个元素按元素的插入时的顺序进行保存,如果我们将评论模型按 createDate 排好序后再插入 List 中,似乎就能做到排序了,而且再利用 List 中的 LRANGE key start stop 指令还能做到分页。嗯,到这里 List 似乎满足了我们分页和排序的要求,但是评论还会被删除,就需要更新 Redis 中的数据,如果每次删除评论后都将 Redis 中的数据全部重新写入一次,显然不够优雅,效率也会大打折扣,如果能删除指定的数据无疑会更好,而 List 中涉及到删除数据的就只有 LPOP 和 RPOP 这两条指令,但 LPOP 和 RPOP 只能删除列表头和列表尾的数据,不能删除指定位置的数据,所以 List 也不太适合。 **Set: **主要存储无序集合,无序!排除。 **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 用于存储具体的键值对数据,我画出了如下的结构图:
在上图的 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 中达到了分页和排序的目的。
1
gouchaoer 2017-01-07 00:23:13 +08:00 via Android
这个。。。 comment 另外存 hash 是不对的,这意味着你取一页就要有一个 zset 操作+几十个 hash get 操作,应该直接把 comment 放 zset 里
|
3
Jaylee 2017-01-07 01:06:07 +08:00
假如有一个用户改了昵称怎么办?
|
7
uuhp2009 2017-01-07 10:56:33 +08:00
什么坑
|
11
owt5008137 2017-01-07 14:37:48 +08:00 via Android
为啥非得把 NoSQL 用成 SQL
|
13
wwwicbd 2017-01-08 00:34:38 +08:00 via iPhone
@owt5008137 这就是挺典型的 redis 应用啊。新版 redis 支持插件,还真有个项目在 redis 里实现了部分 SQL 查询
|