我认为您需要换个角度看待一些问题,才能得出答案。
“后台线程运行频率如何?”要回答这个问题,需要先回答以下几个问题:您可以承受多少数据丢失?MySQL中的数据是基于什么原因存储的,并且有多频繁访问该数据?例如,如果DB每天只需查询一次以生成报告,则可能只需要每天更新一次。另一方面,如果Redis实例关闭了怎么办?您可以丢失多少次增量并仍然保持“正常”?这些将提供有关更新MySQL实例的频率的答案,我们无法为您提供解答。
我会使用非常不同的策略来在Redis中存储此数据。假设您决定每小时“刷新到DB”,则可以使用哈希表将每个命中存储在具有以下结构的键名中:
interval_counter:DD:HH
interval_counter:total
使用页面ID(例如URI的MD5摘要、URI本身或您当前使用的任何ID)作为哈希键,并对页面视图进行两次递增;每个哈希值各一次。这为您提供了每个页面的当前总数和要更新的页面子集。
然后,您可以设置cron作业在整点后一分钟左右运行,通过获取前一个小时的哈希值来下载所有具有更新视图计数的页面。这为您提供了一种非常快速的获取数据以更新MySQL DB的方法,同时避免了任何需要进行时间戳等计算或玩弄技巧的需要。通过从不再进行增量的键中提取数据,可以避免由于时钟偏移而导致的竞争条件。
您可以在每日键上设置过期时间,但我更愿意使用cron作业在成功更新DB后将其删除。这意味着如果cron作业失败或未能执行,则仍然存在数据。它还通过不更改键来为前端提供了完整的已知点击计数数据集。如果您愿意,甚至可以保留每日数据以便能够查看页面的流行程度。例如,如果您通过cron作业设置到期时间而不是删除来保留每日哈希值7天,则可以显示每个页面过去一周每天的流量。
执行两个hincr操作可以单独完成或者通过流水线完成,仍然表现良好,并且比在代码中进行计算和数据操作更有效率。
现在是关于过期低流量页面与内存使用的问题。首先,您的数据集似乎不需要大量内存。当然,很大程度上取决于您如何识别每个页面。如果您有数字ID,则内存要求将相当小。如果您仍然有太多的内存,可以通过配置进行调整,如果需要,甚至可以使用32位编译的redis来显著减少内存使用。例如,我在互联网十大繁忙论坛之一中管理的数据(在
此答案中描述)消耗了不到3GB的数据。我还将计数器存储在比我在此处描述的“时间窗口”键中更多。
话虽如此,在这种用例中,Redis是缓存。如果在上述选项之后仍然使用太多内存,则可以在键上设置过期时间并将过期命令添加到每个ht中。更具体地说,如果按照上述模式执行,您将执行以下操作:
hincr -> total
hincr -> daily
expire -> total
这使您可以通过每次访问时延长其过期时间来保持任何活跃使用的内容的新鲜度。当然,要做到这一点,您需要包装您的显示调用以捕获总哈希上hget的空答案,并从MySQL DB中填充它,然后递增。您甚至可以将两者作为递增操作。这将保留上述结构,并且如果Redis节点需要重新填充,则可能需要相同的代码库来更新Redis服务器。为此,您需要考虑并决定哪个数据源将被视为权威。
您可以通过根据您从早期问题确定的数据完整性参数修改间隔来调整cron作业的性能。要获得更快运行的cron作业,您可以缩小窗口。使用此方法,缩小窗口意味着您应该有一个较小的要更新的页面集合。这里的一个重要优势是您不需要找出需要更新的键,然后去获取它们。您可以执行hgetall并迭代哈希的键以进行更新。这也通过一次检索所有数据来节省了许多往返。在任一情况下,如果您可能还需要考虑第二个Redis实例从主实例中读取。您仍将对主服务器执行删除,但这些操作速度更快,不太可能在写入密集的实例中引入延迟。
如果您需要Redis DB的磁盘持久性,那么一定将其放在从实例上。否则,如果您有很多数据经常更改,则RDB转储将不断运行。
希望这可以帮助您。没有“标准答案”,因为要正确使用Redis,您需要首先考虑如何访问数据,而这与用户和项目之间存在很大差异。在这里,我基于此描述采取了以下路线:两个消费者访问数据,一个仅用于显示,另一个用于确定更新其他数据源。