本文将先容四种利用 Redis 对在线用户进行记录的方案, 这些方案虽然都可以对在线用户的数量进行统计, 但每个方案都有一些自己特有的操作, 并且各个方案的性能特色以及资源花费也各有不同。
方案 1 :利用有序凑集
每当一个用户上线时, 我们就实行 ZADD 命令, 将这个用户以及它的在线韶光添加到指定的有序凑集中:
ZADD \"大众online_users\公众 <user_id> <current_timestamp>
通过利用 ZSCORE 命令检讨指定的用户 ID 在有序凑集中是否有干系联的分值, 我们可以知道该用户是否在线:
ZSCORE \"大众online_users\"大众 <user_id>
而通过实行 ZCARD 命令, 我们可以知道统共有多用户在线:
ZCARD \公众online_users\"大众
利用有序凑集储存在线用户的强大之处在于, 它是本文先容的所有方案当中, 能够实行最多聚合操作的一个方案, 缘故原由在于, 这一方案既可以通过有序凑集的成员(也即是用户的 ID)进行聚合操作, 也可以根据有序凑集的分值(也即是用户的登录韶光)进行聚合操作。
首先, 通过 ZINTERSTORE 和 ZUNIONSTORE 命令, 我们可以对多个记录了在线用户的有序凑集进行聚合打算:
# 打算出 7 天之内都有上线的用户,并将它储存到 7_days_both_online_users 有序凑集当中ZINTERSTORE 7_days_both_online_users 7 \公众day_1_online_users\"大众 \公众day_2_online_users\"大众 ... \"大众day_7_online_users\公众# 打算出 7 天之内统共有多少人上线了ZUNIONSTORE 7_days_total_online_users 7 \公众day_1_online_users\公众 ... \"大众day_7_online_users\"大众
此外, 通过 ZCOUNT 命令, 我们可以统计出在指定的韶光段之内有多少用户在线, 而 ZRANGEBYSCORE 命令则可以让我们获取到这些用户的名单:
# 统计指定时间段内上线的用户数量ZCOUNT \"大众online_users\公众 <start_timestamp> <end_timestamp># 获取指定时间段内上线的用户名单ZRANGEBYSCORE \公众online_users\"大众 <start_timestamp> <end_timestamp> WITHSCORES
通过这一方法, 我们可以知道网站在不同韶光段的上线人数以及上线用户名单, 比如说, 我们可以用这个方法来分别获知网站在清晨、上午、中午、下午和夜晚的上线人数。
方案 2 :利用凑集
正如上一节所说, 利用有序凑集能够同时储存在线用户的名单以及各个用户的上线韶光, 但如果我们只想要记录在线用户的名单, 而不想要储存用户的上线韶光, 那么也可以利用凑集来代替有序凑集, 对在线的用户进行记录。
在这种情形下, 每当一个用户上线时, 我们就实行以下 SADD 命令, 将它添加到在线用户名单当中:
SADD \"大众online_users\公众 <user_id>
通过利用 SISMEMBER 命令, 我们可以检讨一个指定的用户当前是否在线:
SISMEMBER \"大众online_users\"大众 <user_id>
而统计在线人数的事情则可以通过实行 SCARD 命令来完成:
SCARD \"大众online_users\"大众
通过凑集运算操作, 我们可以像有序凑集方案一样, 对不同韶光段或者日期的在线用户名单进行聚合打算。 比如说, 通过 SINTER 或者 SINTERSTORE 命令, 我们可以打算出一周都有在线的用户:
SINTER \公众day_1_online_users\公众 \"大众day_2_online_users\"大众 ... \"大众day_7_online_users\"大众
此外, 通过 SUNION 命令或者 SUNIONSTORE 命令, 我们可以打算出一周内在线用户的总数量:
SUNION \"大众day_1_online_users\"大众 \"大众day_2_online_users\公众 ... \"大众day_7_online_users\"大众
而通过实行 SDIFF 命令或者 SDIFFSTORE 命令, 我们可以知道哪些用户本日上线了, 但是昨天没有上线:
SDIFF \"大众today_online_users\"大众 \"大众yesterday_online_users\公众
又或者事情日上线了, 但是假日没有上线:
# 打算事情日上线名单SINTERSTORE \"大众weekday_online_users\"大众 \"大众monday_online_users\"大众 \"大众tuesday_online_users\公众 ... \公众friday_online_users\公众# 打算假日上线名单SINTERSTORE \"大众holiday_online_users\"大众 \"大众saturday_online_users\"大众 \"大众sunday_online_users\"大众# 打算事情日上线但是假日未上线的名单SDIFF \"大众weekday_online_users\"大众 \公众holiday_online_users\"大众
诸如此类。
方案 3 :利用 HyperLogLog
虽然利用有序凑集和凑集能够很好地完成记录在线人数的事情, 但以上这两个方案都有一个明显的缺陷, 那便是, 这两个方案耗费的内存会随着被统计用户数量的增多而增多: 如果你的网站用户数量比较多, 又或者你须要记录多天/多个时段的在线用户名单并进行聚合打算, 那么这两个方案可能会花费你大量内存。
另一方面, 在有些情形下, 我们只想要知道在线用户的人数, 而不须要知道详细的在线用户名单, 这时有序凑集和凑集储存的信息就会显得多余了。
在须要尽可能地节约内存并且只须要知道在线用户数量的情形下, 我们可以利用 HyperLogLog 来对在线用户进行统计: HyperLogLog 是一个概率算法, 它可以对元素的基数进行估算, 并且每个 HyperLogLog 只须要耗费 12 KB 内存, 对付用户数量非常多但是内存却非常紧张的系统, 这一方案无疑是最佳之选。
在这一方案下, 我们利用 PFADD 命令去记录在线的用户:
PFADD \"大众online_users\公众 <user_id>
利用 PFCOUNT 命令获取在线人数:
PFCOUNT \公众online_users\"大众
由于 HyperLogLog 也供应了打算交集的 PFMERGE 命令, 以是我们也可以用这个命令打算出多个给定时间段或日期之内, 上线的总人数:
# 统计 7 天之内统共有多少人上线了PFMERGE \公众7_days_both_online_users\公众 \公众day_1_online_users\"大众 \"大众day_2_online_users\"大众 ... \"大众day_7_online_users\"大众PFCOUNT \公众7_days_both_online_users\公众
方案 4 :利用位图(bitmap)
回顾上面先容的三个方案, 我们可以得出以上结论:
利用有序凑集或者凑集能够储存详细的在线用户名单, 但是却须要花费大量的内存;而利用 HyperLogLog 虽然能够有效地减少统计在线用户所需的内存, 但是它却没办法准确地记录详细的在线用户名单。那么是否存在一种既能够得到在线用户名单, 又可以只管即便减少内存花费的方法存在呢? 这种方法的确存在 —— 利用 Redis 的位图就可以办到。
Redis 的位图便是一个由二进制位组成的数组, 通过将数组中的每个二进制位与用户 ID 进行逐一对应, 我们可以利用位图去记录每个用户是否在线。
当一个用户上线时, 我们就利用 SETBIT 命令, 将这个用户对应的二进制位设置为 1 :
# 此处的 user_id 必须为数字,由于它会被用作索引SETBIT \公众online_users\公众 <user_id> 1
通过利用 GETBIT 命令去检讨一个二进制位的值是否为 1 , 我们可以知道指定的用户是否在线:
GETBIT \公众online_users\公众 <user_id>
而通过 BITCOUNT 命令, 我们可以统计出位图中有多少个二进制位被设置成了 1 , 也即是有多少个用户在线:
BITCOUNT \公众online_users\"大众
跟凑集一样, 用户也能够对多个位图进行聚合打算 —— 通过 BITOP 命令, 用户可以对一个或多个位图实行逻辑并、逻辑或、逻辑异或或者逻辑非操作:
# 打算出 7 天都在线的用户BITOP \"大众AND\"大众 \"大众7_days_both_online_users\"大众 \"大众day_1_online_users\"大众 \公众day_2_online_users\公众 ... \公众day_7_online_users\公众# 打算出 7 在的在线用户总人数BITOP \公众OR\公众 \"大众7_days_total_online_users\"大众 \"大众day_1_online_users\公众 \公众day_2_online_users\"大众 ... \"大众day_7_online_users\公众# 打算出两天当中只有个中一天在线的用户BITOP \公众XOR\"大众 \"大众only_one_day_online\"大众 \"大众day_1_online_users\"大众 \公众day_2_online_users\"大众
HyperLogLog 方案记录一个用户是否在线须要花费 1 个二进制位, 对付用户数为 100 万的网站来说, 利用这一方案只须要耗费 125 KB 内存, 而对付用户数为 1000 万的网站来说, 利用这一方案也只须要花费 1.25 MB 内存。
虽然位图节约内存的效果不及 HyperLogLog 那么显著, 但是利用位图可以准确地判断一个用户是否上线, 并且能够像凑集和有序凑集一样, 对在线用户名单进行聚合打算。 因此对付想要只管即便节约内存, 但又须要准确地知道用户是否在线, 又或者须要对用户的在线名单进行聚合打算的运用来说, 利用位图可以说是最佳之选。
总结
以下表格总结了以上四个方案的特点:
方案特点 有序凑集 能够同时储存在线用户的名单以及用户的上线韶光,能够实行非常多的聚合打算操作,但是耗费的内存也非常多。 凑集 能够储存在线用户的名单,也能够实行聚合打算,花费的内存比有序凑集少,但是跟有序凑集一样,这个方案花费的内存也会随着用户数量的增多而增多。 HyperLogLog 无论须要统计的用户有多少,只须要耗费 12 KB 内存,但由于概率算法的特性,只能给出在线人数的估算值,并且也无法获取准确的在线用户名单。 位图 在尽可能节约内存的情形下,记录在线用户的名单,并且能够对这些名单实行聚合操作。 由于 Redis 同时支持多种数据构造, 以是一个问题常常可以在 Redis 里面找多种不同的解法, 并且每种解法都有各自的优点和缺陷, 本文先容的问题便是一个很好的例子。
关于统计在线用户的方法就先容到这里, 希望这些方案会给大家带来帮助和启示。
转载地址:http://www.cnblogs.com/mr-amazing/p/6245421.html