牛客论坛项目中使用到Redis的地方总结

news/2024/5/20 13:48:48 标签: redis, 数据库, 缓存

在这里插入图片描述

实体分为很多类,实体的确定要通过实体类型和实体id两个属性同时确定。牛客论坛中使用到了3类实体:
在这里插入图片描述

在这里插入图片描述

1 登录

使用到的Redis命令:

set key value // 设置指定key的值为value
get key // 获取指定key的值

1.1 存储/获取验证码

验证码文本,登录前,用户根据验证码图片输入验证码文本,用户提交登录,服务器会将用户输入的验证码文本于缓存在Redis中的验证码文本做字符串比较。

1、将验证码存入Redis

String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

2、获取验证码

String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
kaptcha = (String) redisTemplate.opsForValue().get(redisKey);

1.2 存储/获取登录凭证

登录成功后,用户获得登录凭证,里面存储了用户id、凭证过期时间、状态,用于登录和登出功能。

1、存储登录凭证:

String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
redisTemplate.opsForValue().set(redisKey, loginTicket);

2、获取登录凭证:

String redisKey = RedisKeyUtil.getTicketKey(ticket);
LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);

2 点赞

使用到的Redis命令:

// 以下是集合的命令
sadd key element [element …] // 向集合key添加一个或多个元素
srem key element [element …] // 移除集合key中的一个或多个元素
sismember key memeber // 用于判断元素 member是否集合的成员。
scard key  // 获取集合的成员数

// 以下是字符串的命令
incr key	//将key中储存的字符串数值增一
decr key	//将key中储存的字符串数值减一
get key 	// 获取字符串数值

2.1 用户给实体点赞 / 取消点赞

public void like(int userId, int entityType, int entityId, int entityUserId) {
    redisTemplate.execute(new SessionCallback() {
        @Override
        public Object execute(RedisOperations operations) throws DataAccessException {
            String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
            String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId);

            boolean isMember = operations.opsForSet().isMember(entityLikeKey, userId);

            // 开始一个Redis事务
            operations.multi();

            if (isMember) {
                operations.opsForSet().remove(entityLikeKey, userId);
                operations.opsForValue().decrement(userLikeKey);
            } else {
                operations.opsForSet().add(entityLikeKey, userId);
                operations.opsForValue().increment(userLikeKey);
            }

            // 执行Redis事务,并返回结果
            return operations.exec();
        }
    });
}

2.2 查询某实体点赞的数量

public long findEntityLikeCount(int entityType, int entityId) {
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        return redisTemplate.opsForSet().size(entityLikeKey);
    }

2.3 查询某人对某实体的点赞状态

public int findEntityLikeStatus(int userId, int entityType, int entityId) {
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        return redisTemplate.opsForSet().isMember(entityLikeKey, userId) ? 1 : 0;
    }

2.4 查询某个用户获得的赞

public int findUserLikeCount(int userId) {
        String userLikeKey = RedisKeyUtil.getUserLikeKey(userId);
        Integer count = (Integer) redisTemplate.opsForValue().get(userLikeKey);
        return count == null ? 0 : count.intValue();
    }

3 关注

使用到的Redis命令

zadd key score member [score member …]	// 向有序集合key添加一个或多个成员,或者更新已存在成员的分数
zrem key member [member …]	// 移除有序集合key中的一个或多个成员
zcard key	// 获取有序集合key的成员数
zscore key member	//返回有序集合key中,成员member的分数
zrange key start end [withscores]	//返回有序集合key中,指定区间内的成员
zrevrange key start end [withscores]	//返回有序集合key中,指定区间内的成员,通过索引,分数从高到低

3.1 用户关注某个实体

某个用户关注的实体,有序集合里面存放的是entity_id,分数是时间,实体类型在key里。
可以关注某个用户,entity_type=3
可以关注某个帖子,entity_type=1
可以关注某条评论,entity_type=2

public void follow(int userId, int entityType, int entityId) {
    redisTemplate.execute(new SessionCallback() {
        @Override
        public Object execute(RedisOperations operations) throws DataAccessException {
            String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
            String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

            operations.multi();

            operations.opsForZSet().add(followeeKey, entityId, System.currentTimeMillis());
            operations.opsForZSet().add(followerKey, userId, System.currentTimeMillis());

            return operations.exec();
        }
    });
}

3.2 用户取消关注

public void unfollow(int userId, int entityType, int entityId) {
    redisTemplate.execute(new SessionCallback() {
        @Override
        public Object execute(RedisOperations operations) throws DataAccessException {
            String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
            String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

            operations.multi();

            operations.opsForZSet().remove(followeeKey, entityId);
            operations.opsForZSet().remove(followerKey, userId);

            return operations.exec();
        }
    });
}

3.3 查询关注的实体的数量

public long findFolloweeCount(int userId, int entityType) {
    String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
    return redisTemplate.opsForZSet().zCard(followeeKey);
}

3.4 查询实体的粉丝的数量

public long findFollowerCount(int entityType, int entityId) {
    String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
    return redisTemplate.opsForZSet().zCard(followerKey);
}

3.5 查询当前用户是否已关注该实体

由于有序集合没有类似集合那样sismember key memeber // 用于判断元素 member是否集合的成员。
所以只能用获取分数,判断分数是不是为空

public boolean hasFollowed(int userId, int entityType, int entityId) {
    String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
    return redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
}

3.6 查询某用户关注的人(关注列表) ,分页展示

public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
    String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
    Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);

3.7 查询某用户的粉丝 (粉丝列表),分页展示

public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
    String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
    Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);

4 网站数据统计

使用到的Redis命令:

// 以下是HyperLogLog的命令
pfadd key element [element ...] // 向HyperLogLog添加元素
pfcount key [key ..]  // 计算独立用户数
pfmerge destkey sourcekey [sourcekey ...] // 求出多个HyperLogLog的并集并复制给destkey

// 以下是bitmaps的命令
setbit key offset value // 设置第offset个位置的值
getbit key offset   // 获取第offset个位置的值
bitop or destkey key [key ...] //多个bitmaps做Or运算
bitcount key [start end] // 获取bitmaps指定范围值为1的个数

4.1 统计独立访客

独立访客,union
1、将指定的IP计入UV

public void recordUV(String ip) {
    String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));
    redisTemplate.opsForHyperLogLog().add(redisKey, ip);
}

2、统计指定日期范围内的UV

// 合并这些数据
String redisKey = RedisKeyUtil.getUVKey(df.format(start), df.format(end));
redisTemplate.opsForHyperLogLog().union(redisKey, keyList.toArray());

// 返回统计的结果
return redisTemplate.opsForHyperLogLog().size(redisKey);

4.2 统计日活跃用户

日活跃用户,or位运算
1、 将指定用户计入DAU

public void recordDAU(int userId) {
    String redisKey = RedisKeyUtil.getDAUKey(df.format(new Date()));
    redisTemplate.opsForValue().setBit(redisKey, userId, true);
}

2、 统计指定日期范围内的DAU

// 进行OR运算
return (long) redisTemplate.execute(new RedisCallback() {
    @Override
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
        String redisKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end));
        connection.bitOp(RedisStringCommands.BitOperation.OR,
                redisKey.getBytes(), keyList.toArray(new byte[0][0]));
        return connection.bitCount(redisKey.getBytes());
    }
});

5 缓存设计

使用到的Redis命令:

set key value // 设置指定key的值为value
get key // 获取指定key的值
del key [key …]	 // 删除一个或多个key

5.1 优先从缓存中取值

private User getCache(int userId) {
    String redisKey = RedisKeyUtil.getUserKey(userId);
    return (User) redisTemplate.opsForValue().get(redisKey);
}

5.2 取不到时初始化缓存数据

private User initCache(int userId) {
    User user = userMapper.selectById(userId);
    String redisKey = RedisKeyUtil.getUserKey(userId);
    redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
    return user;
}

5.3 数据变更时清除缓存数据

private void clearCache(int userId) {
    String redisKey = RedisKeyUtil.getUserKey(userId);
    redisTemplate.delete(redisKey);
}

6 热帖排行

使用到的Redis命令

sadd key element [element …]	// 向集合key添加一个或多个元素
	spop key	// 移除并返回集合中的一个随机元素

里面存放的是帖子id,用于启动定时任务计算帖子分数,有哪些帖子要计算分数。

6.1 记录要计算分数的帖子

// 
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, postId);

## 6.1 获取所有要计算分数的帖子

while (operations.size() > 0) {
    this.refresh((Integer) operations.pop());
}

http://www.niftyadmin.cn/n/5481585.html

相关文章

Java Set基础篇

目录 前言一、常用Set1.1 Set1.1.1 特点 1.2 HashSet1.2.1 特点1.2.2 使用 1.3 TreeSet1.3.1 特点1.3.2 使用 1.4 LinkedHashSet1.4.1 特点1.4.2 使用 二、对比总结 目录 前言 一、常用Set 1.1 Set Set是一个继承自Collection的接口&#xff1a; public interface Set<…

JavaScript数组操作方法全录

改变原数组的方法&#xff1a; push() - 将一个或多个元素添加到数组的末尾&#xff0c;并返回新数组的长度。 pop() - 从数组中移除最后一个元素&#xff0c;并返回该元素。 shift() - 从数组中移除第一个元素&#xff0c;并返回该元素。 unshift() - 将一个或多个元素添加到…

GD32F470_GY30光照传感器模块 数字光强度BH1750FVI光照度

2.29 BH1750光照强度检测传感器 采用ROHM原装BH1750FVI芯片供电电源:3-5V&#xff0c;光照度范围:0-65535lx传感器内置16bitAD转换器&#xff0c;直接数字输出&#xff0c;省略复杂的计算&#xff0c;省略标定&#xff0c;不区分环境光源接近于视觉灵敏度的分光特性&#xff0…

初识--数据结构

什么是数据结构&#xff1f;我们为什么要学习数据结构呢....一系列的问题就促使我们不得不了解数据结构。我们不禁要问了&#xff0c;学习C语言不就够了吗&#xff1f;为什么还要学习数据结构呢&#xff1f;这是因为&#xff1a;数据结构能够解决C语言解决不了的问题&#xff0…

ELK Stack、Kafka 和 Filebeat 认识和使用上手

ELK Stack、Kafka 和 Filebeat 认识和使用上手 ELK Stack 是一个强大的日志管理和分析平台&#xff0c;由 Elasticsearch、Logstash 和 Kibana 组成。Elasticsearch 负责存储和索引日志数据&#xff0c;Logstash 用于日志处理和转换&#xff0c;而 Kibana 提供可视化界面。在现…

外包干了3天,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

Linux第87步_阻塞IO实验

阻塞IO是“应用程序”对“驱动设备”进行操作&#xff0c;若不能获取到设备资源&#xff0c;则阻塞IO应用程序的线程会被“挂起”&#xff0c;直到获取到设备资源为止。 “挂起”就是让线程进入休眠&#xff0c;将CPU的资源让出来。线程进入休眠后&#xff0c;当设备文件可以操…

在线预约小程序怎么做

在快节奏的现代生活中&#xff0c;无论是预约理发、还是预定餐厅&#xff0c;亦或是挂号就医&#xff0c;我们都希望有一个更加便捷、高效的方式来完成这些任务。而今&#xff0c;随着科技的发展&#xff0c;一款全新的在线预约小程序应运而生&#xff0c;为我们的生活带来了前…