Redis In Action 笔记(三):用户登录、浏览记录的缓存与管理
说明
原书中的例子使用的语言是 python,这里使用 php 对该例子进行改写、注释并测试运行结果。
例子要实现的功能是缓存用户登录的token,登录时间、浏览记录,并对这些数据进行日常的筛选、清理等。
数据结构(示意图)
登录数据
key:login:
,类型:hash
+-+ login: +---------------+ hash +--+
| |
| token +-------------> user_id |
| (key) (value) |
| |
+------------------------------------+
最近访问数据
key:recent:
,类型:zset
+-+ recent: +--------------+ zset +--+
| |
| token +-------------> timestamp |
| (member) (score) |
| |
+------------------------------------+
浏览记录数据
key:viewed:user_id
,类型:zset
+-+ viewed:123(uid) +------+ zset +--+
| |
| token +-------------> timestamp |
| (member) (score) |
| |
+------------------------------------+
程序实现
设置常量和Redis连接
const QUIT = false;
const LIMIT = 1000; //原书为10000000
$redis = new Redis();
$redis->connect('127.0.0.1', '6379') || exit('连接失败!');
检查用户是否登录
function checkToken($redis, $token)
{
return $redis->hGet('login:', $token);
}
更新token
function updateToken($redis, $token, $user, $item = null)
{
$now = time();
//添加或更新用户的token
$redis->hSet('login:', $token, $user);
//添加或更新用户最近访问时间
$redis->zAdd('recent:', $now, $token);
if ($item) {
$redis->zAdd('viewed:' . $token, $now, $item);
//表示从0开始,到倒数第26个之间的数据删除(闭区间,最后一个为-1)
//也即是从-1数到-25这个范围内的数据保留-->保留最后25个
$redis->zRemRangeByRank('viewed:' . $token, 0, -26);
}
}
清理数据
function cleanSessions($redis)
{
while (!QUIT) { //可以改为守护进程或cron job任务来定期执行
//统计最近访问记录数量
$size = $redis->zCard('recent:');
//如果数量没有超过限制,则休眠1s,重新检查
if ($size <= LIMIT) {
sleep(1);
continue;
}
$end_index = min($size - LIMIT, 100);
//$end_index的值为[0-100]区间的整数
//取出recent: 集合保留0-100之间的记录,也即超过LIMIT的这一部分
//下面将这一部分进行删除
$tokens = $redis->zRange('recent:', 0, $end_index - 1);
var_dump($redis->zRange('recent:', 0, 0, true)['token-0']);
$session_keys = [];
foreach ($tokens as $token) {
$session_keys[] = 'viewed:' . $token;
}
//删除相应用户的浏览记录
$aa = $redis->delete($session_keys);
//删除相应用户的登录信息
$redis->hDel('login:', ...$tokens);
//删除相应用户的最近访问记录
$redis->zRem('recent:', ...$tokens);
}
}
程序运行
/假设用户访问了30个商品,商品id从1到30
//最终viewed:123有序集合的基数是25
for ($i = 0; $i < 30; $i++) {
updateToken($redis, 'user-1-token', 123, $i);
}
$count = $redis->zCard('viewed:user-1-token');
echo $count . PHP_EOL; //输出25
//根据token查找用户
$is_login = checkToken($redis, 'user-1-token');
echo (bool)$is_login . PHP_EOL;
//模拟大量用户访问
for ($i = 0; $i < 1050; $i++) {
updateToken($redis, 'token-' . $i, 123 + $i, $i);
}
//清理数据,释放缓存,以免大量用户信息占据缓存使缓存耗光
//view:xxx集合的数量、'recent:','login:'的基数将保持在LIMIT(程序开头设置为1000)
cleanSessions($redis);
附录
- 参考资料:https://redislabs.com/ebook/part-1-getting...
- 完整代码:https://github.com/HubQin/redis-in-action-...
本作品采用《CC 协议》,转载必须注明作者和本文链接