MST

星途 面试题库

面试题:Redis中BITOP命令在用户活跃度统计场景的应用

假设你要统计一个网站每天用户的登录情况,使用Redis的BITOP命令,如何设计数据结构来记录一年内每天每个用户是否登录,并通过BITOP命令统计每月活跃用户数?
27.2万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 数据结构设计
    • 为每个用户分配一个Redis的string类型键,键名可以设计为user:{user_id}:login:year:{year},其中{user_id}是用户的唯一标识,{year}是年份。
    • 这个string类型的值使用位(bit)来记录每天的登录情况。一年有365天(不考虑闰年),所以这个string的长度为365位。每一位对应一天,1表示用户当天登录,0表示未登录。例如,如果用户在1月1日登录,那么第1位就设为1;如果在1月2日未登录,第2位就设为0。
  2. 统计每月活跃用户数
    • 对于每个月,我们可以使用BITOP命令的AND操作来统计活跃用户数。
    • 首先,获取每个用户对应月份的位序列。由于一年365天,1月有31天,2月(非闰年)有28天等。
    • 假设要统计1月的活跃用户数,对于每个用户user:{user_id}:login:year:{year},提取第1到31位。
    • 可以使用Redis的脚本(Lua脚本)来批量处理这些操作。示例Lua脚本如下:
local month_start = 1
local month_end = 31
local active_users = 0
for _, user_key in ipairs(KEYS) do
    local bit_sequence = redis.call('GET', user_key)
    local month_bits = string.sub(bit_sequence, month_start, month_end)
    local bit_count = 0
    for i = 1, #month_bits do
        if string.sub(month_bits, i, i) == '1' then
            bit_count = bit_count + 1
        end
    end
    if bit_count > 0 then
        active_users = active_users + 1
    end
end
return active_users
  • 在这个脚本中,KEYS是用户键的列表。通过遍历每个用户键,获取其位序列,提取对应月份的位,统计其中1的个数,如果1的个数大于0,则该用户在该月活跃,活跃用户数加1 。最后返回活跃用户数。
  • 也可以不使用脚本,通过编程语言(如Python)结合Redis客户端库来实现同样的逻辑:
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)

month_start = 1
month_end = 31
active_users = 0
user_keys = r.keys('user:*:login:year:2023')
for user_key in user_keys:
    bit_sequence = r.get(user_key)
    month_bits = bit_sequence[month_start - 1:month_end]
    bit_count = month_bits.count(b'1')
    if bit_count > 0:
        active_users += 1
print(active_users)
  • 这里假设Redis服务器运行在本地,端口6379,数据库0 。先获取所有用户键,然后对每个用户键进行处理,统计活跃用户数。

另一种使用BITOPAND操作方式:

  • 可以为每个月创建一个临时的位序列,通过BITOP AND操作将所有用户对应月份的位序列进行AND运算,然后统计结果位序列中1的个数,就是活跃用户数。
  • 例如在Python中:
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)

month_start = 1
month_end = 31
user_keys = r.keys('user:*:login:year:2023')
tmp_key = 'tmp:month:1:active_users'
r.delete(tmp_key)
r.bitop('AND', tmp_key, *user_keys)
bit_sequence = r.get(tmp_key)
month_bits = bit_sequence[month_start - 1:month_end]
active_users = month_bits.count(b'1')
print(active_users)
  • 这里先删除可能存在的临时键tmp:month:1:active_users,然后通过BITOP AND操作将所有用户键对应位序列进行AND运算到临时键,最后统计临时键对应月份位序列中1的个数得到活跃用户数。