本文和大家分享的主要使用redis实现动态时间段内统计排序的相关操作方法,希望对大家学习redis 有所帮助,一起来看看吧。
问题描述
需要根据某类数据在动态时间段内的统计值对这些数据进行排名。例如按过去24小时内点赞数排名的帖子,每隔一小时计算一次结果。以下描述均针对这个例子展开。
解决思路
针对这种问题,我的第一反应是直接通过mysql一张数据表记录所有数据的每一条统计值改变的行为,例如记下每个帖子在哪个时间点被谁点赞。排序结果直接通过select + where + order_by + limit。简单粗暴,但效率低下,扩展性差,而且当数据量很多时,会导致数据库查询效率低下。
那么为了提高效率,mysql换成redis如何?将这些统计值改变的记录移到redis的zset中,为每个帖子建立一条zset,zset的每一条member代表一条点赞记录,field为帖子id,score记录点赞时间。利用zcount查询每个帖子有效时间段内的点赞总数。redis查询也快,看起来可行,但当帖子基数和点赞数很大时,zset中成员量暴增,而且对于过期数据怎么处理?而且把点赞量换成点击量呢?就要记录每个用户对每篇帖子的点击行为了,这个方案是否合理?
仔细想一下这个问题,其实我们只需要关注帖子的点赞数量,而不是每条点赞行为。因此可以考虑利用redis记录帖子在每个时间点内的点赞数量,并且定期删掉过期的数据,例如记录每条帖子每个小时内的点赞数。这样就可以知道过去24小时内这个帖子的点赞总数了。具体的方案下面将展开。
数据结构
需要两个redis数据结构,分别存放 过程 和 结果,因为要定期过期一些数据,所以要通过 过程 记录数据统计值的历史记录,同时 结果 记录所有有效过程内的总体排序结果。
例如当前时间9:00,就需要知道当前9点到昨天9点的所有点赞数量,也就不需要昨天8点的点赞数了,所以结果里面需要加上今天9点的数据,减去昨天8点的数据。而随着时间往后推移,昨天9点到今天9点的数据也会依次被减掉,所以需要记录有效时间段内每个时间点的数据,这就是过程的作用了。结果的作用显而易见了,记录结果并支持排序。
·
过程 利用 hash 结构存放在有效期内每个时间节点的所有帖子的统计值,例如建立: last_1, last_2, last_3 ……等24个key(因为是统计过去24小时内),每个hash key内记录该节点到下个节点的时间段内被点赞的帖子(field)及其点赞数(value) 为什么采用hash结构而不是list或set,因为在定时更新统计数据过程中,需要先获取当前最新,再加一或减一,hash的 hincrby 方法支持原子操作,可以在一个事务内完成这两个操作。试想在list结构下,对同一个帖子的多个并发点赞,可能导致数据错误。
·
·
结果 利用 zset 结构存放所有帖子的统计总数,也就是排序结果。包括所有帖子(member)及其统计总数(value) zset支持记录score以及按score排序,也可以支持分页获取数据。
·
想法实现
基于以上过程和结果的redis结构,需要处理统计数据的更新,以及排序结果的定时更新两个关键流程。
问题描述
需要根据某类数据在动态时间段内的统计值对这些数据进行排名。例如按过去24小时内点赞数排名的帖子,每隔一小时计算一次结果。以下描述均针对这个例子展开。
解决思路
针对这种问题,我的第一反应是直接通过mysql一张数据表记录所有数据的每一条统计值改变的行为,例如记下每个帖子在哪个时间点被谁点赞。排序结果直接通过select + where + order_by + limit。简单粗暴,但效率低下,扩展性差,而且当数据量很多时,会导致数据库查询效率低下。
那么为了提高效率,mysql换成redis如何?将这些统计值改变的记录移到redis的zset中,为每个帖子建立一条zset,zset的每一条member代表一条点赞记录,field为帖子id,score记录点赞时间。利用zcount查询每个帖子有效时间段内的点赞总数。redis查询也快,看起来可行,但当帖子基数和点赞数很大时,zset中成员量暴增,而且对于过期数据怎么处理?而且把点赞量换成点击量呢?就要记录每个用户对每篇帖子的点击行为了,这个方案是否合理?
仔细想一下这个问题,其实我们只需要关注帖子的点赞数量,而不是每条点赞行为。因此可以考虑利用redis记录帖子在每个时间点内的点赞数量,并且定期删掉过期的数据,例如记录每条帖子每个小时内的点赞数。这样就可以知道过去24小时内这个帖子的点赞总数了。具体的方案下面将展开。
数据结构
需要两个redis数据结构,分别存放 过程 和 结果,因为要定期过期一些数据,所以要通过 过程 记录数据统计值的历史记录,同时 结果 记录所有有效过程内的总体排序结果。
例如当前时间9:00,就需要知道当前9点到昨天9点的所有点赞数量,也就不需要昨天8点的点赞数了,所以结果里面需要加上今天9点的数据,减去昨天8点的数据。而随着时间往后推移,昨天9点到今天9点的数据也会依次被减掉,所以需要记录有效时间段内每个时间点的数据,这就是过程的作用了。结果的作用显而易见了,记录结果并支持排序。
·
过程 利用 hash 结构存放在有效期内每个时间节点的所有帖子的统计值,例如建立: last_1, last_2, last_3 ……等24个key(因为是统计过去24小时内),每个hash key内记录该节点到下个节点的时间段内被点赞的帖子(field)及其点赞数(value) 为什么采用hash结构而不是list或set,因为在定时更新统计数据过程中,需要先获取当前最新,再加一或减一,hash的 hincrby 方法支持原子操作,可以在一个事务内完成这两个操作。试想在list结构下,对同一个帖子的多个并发点赞,可能导致数据错误。
·
·
结果 利用 zset 结构存放所有帖子的统计总数,也就是排序结果。包括所有帖子(member)及其统计总数(value) zset支持记录score以及按score排序,也可以支持分页获取数据。
·
想法实现
基于以上过程和结果的redis结构,需要处理统计数据的更新,以及排序结果的定时更新两个关键流程。