Redis基础知识及案例分享

Redis基础知识

1 Redis入门

  • Redis是一款基于键值对的NoSQL数据库,它的值支持多种数据结构:字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
  • Redis将所有的数据都存放在内存中,所以它的读写性能十分惊人。同时,Redis还可以将内存中的数据以快照或日志的形式保存到硬盘上,以保证数据的安全性。
  • Redis典型的应用场景包括:缓存、排行榜、计数器、社交网络、消息队列等。

2 Spring整合Redis

  • 引入依赖

spring-boot-starter-data-redis

  • 配置Redis

配置数据库参数
编写配置类,构造RedisTemplate

# redis
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        // 设置key序列化的方式
        redisTemplate.setKeySerializer(RedisSerializer.string());
        // 设置value序列化方式
        redisTemplate.setValueSerializer(RedisSerializer.json());
        // 设置hashKey序列化方式
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置hashValue序列化方式
        redisTemplate.setHashValueSerializer(RedisSerializer.json());

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
  • 访问Redis - redisTemplate.opsForValue()
redisTemplate.opsForHash()
redisTemplate.opsForList()
redisTemplate.opsForSet()
redisTemplate.opsForZSet()

Redis实现点赞

1 点赞

  • 支持对帖子、评论点赞。

    • 第1次点赞,第2次取消点赞。
  • 首页点赞数量

    • 统计帖子的点赞数量。
  • 详情页点赞数量

    • 统计点赞数量。
    • 显示点赞状态。
  1. 创建Key
public class RedisKeyUtil {
    private static final String SPLIT = ":";
    private static final String LIKE_KEY = "like:entity";

    // set(userIds)
    public static String getLikeKey(int entityType, int entityId) {
        return LIKE_KEY + SPLIT + entityType + SPLIT + entityId;
    }
}
  1. 点赞Service层
@Service
public class LikeService {

    @Autowired
    RedisTemplate redisTemplate;

    // 点赞
    public void like(int userId, int entityType, int entityId) {
        // key
        String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId);
        // 查看是否已经点赞
        Boolean member = redisTemplate.opsForSet().isMember(likeKey, userId);
        if (member) {
            redisTemplate.opsForSet().remove(likeKey, userId);
        } else {
            redisTemplate.opsForSet().add(likeKey, userId);
        }
    }

    // 查询点赞数量
    public long findEntityLikeCount(int entityType, int entityId) {
        // key
        String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId);
        return redisTemplate.opsForSet().size(likeKey);
    }
    // 查询某人点赞状态
    public int findEntityLikeStatus(int userId, int entityType, int entityId) {
        // key
        String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId);
        // 1 是点赞 0 是没点赞
        return redisTemplate.opsForSet().isMember(likeKey, userId) ? 1 : 0;
    }
}

  1. 点赞Controller层
@Controller
public class LikeController {
    @Autowired
    LikeService likeService;

    @Autowired
    HostHolder hostHolder;

    @RequestMapping(path = "/like", method = RequestMethod.POST)
    @ResponseBody
    public String like(int entityType, int entityId) {
        // 需要拦截器
        // 当前用户
        User user = hostHolder.getUser();

        // 点赞
        likeService.like(user.getId(), entityType, entityId);

        // 点赞数量
        long likeCount = likeService.findEntityLikeCount(entityType, entityId);

        // 点赞状态
        int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);

        // 返回结果
        Map<String, Object> map = new HashMap<>();
        map.put("likeCount", likeCount);
        map.put("likeStatus", likeStatus);

        return CommunityUtil.getJSONString(0,null,map);
    }
}

2 我收到的赞

  • 重构点赞功能
    • 以用户为key,记录点赞数量
    • increment(key),decrement(key)
  • 开发个人主页
    • 以用户为key,查询点赞数量
  1. 创建Key
public class RedisKeyUtil {
    private static final String SPLIT = ":";
    private static final String PREFIX_LIKE_KEY = "like:entity";
    private static final String PREFIX_USER_LIKE = "like:user";

    // set(userIds)
    public static String getLikeKey(int entityType, int entityId) {
        return PREFIX_LIKE_KEY + SPLIT + entityType + SPLIT + entityId;
    }

    // 某个用户的赞
    // like:user:userId -> int
    public static String getUserLikeKey(int userId) {
        return PREFIX_USER_LIKE + SPLIT + userId;
    }

}
  1. 我收到的赞Service层
@Service
public class LikeService{

    @Autowired
    RedisTemplate redisTemplate;

    // 点赞
    public void like(int userId, int entityType, int entityId, int entityUserId) {
        /**
         * userId是当前点赞的那个人,entityUserId是被赞的那个人
         */
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // key
                String entityLikeKey = RedisKeyUtil.getLikeKey(entityType, entityId);
                // 用户收到的赞
                String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId);
                // 查看是否已经点赞
                Boolean member = operations.opsForSet().isMember(entityLikeKey, userId);
                operations.multi();
                if (member) {
                    operations.opsForSet().remove(entityLikeKey, userId);
                    operations.opsForValue().decrement(userLikeKey);
                } else {
                    operations.opsForSet().add(entityLikeKey, userId);
                    operations.opsForValue().increment(userLikeKey);
                }
                return operations.exec();
            }
        });

    }

    // 统计某人收到点赞的数量
    public int findUserLikeCount(int entityUserId) {
        String likeKey = RedisKeyUtil.getUserLikeKey(entityUserId);
        Integer count = (Integer) redisTemplate.opsForValue().get(likeKey);
        return count == null ? 0 : count.intValue();
    }

}
  1. controller层
@Controller
public class LikeController {
    @Autowired
    LikeService likeService;

    @Autowired
    HostHolder hostHolder;

    @RequestMapping(path = "/like", method = RequestMethod.POST)
    @ResponseBody
    public String like(int entityType, int entityId, int entityUserId) {
        // 需要拦截器
        // 当前用户
        User user = hostHolder.getUser();

        // 点赞
        likeService.like(user.getId(), entityType, entityId, entityUserId);

        // 点赞数量
        long likeCount = likeService.findEntityLikeCount(entityType, entityId);

        // 点赞状态
        int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);

        // 返回结果
        Map<String, Object> map = new HashMap<>();
        map.put("likeCount", likeCount);
        map.put("likeStatus", likeStatus);

        return CommunityUtil.getJSONString(0,null,map);
    }
}

Redis实现关注

1 关注、取消关注

  • 需求
    • 开发关注、取消关注功能。
    • 统计用户的关注数、粉丝数。
  • 关键
    • 若A关注了B,则A是B的Follower(粉丝),B是A的Followee(目标)。
    • 关注的目标可以是用户、帖子、题目等,在实现时将这些目标抽象为实体。
  1. Key
public class RedisKeyUtil {
   // 粉丝
    private static final String PREFIX_FOLLOWER = "follower";
    // 关注者
    private static final String PREFIX_FOLLOWEE = "followee";

    // 关注者
    public static String getFolloweeKey(int entityType, int entityUserId) {
        return PREFIX_FOLLOWEE + SPLIT + entityUserId + SPLIT + entityType;
    }
    // 粉丝
    public static String getFollowerKey(int entityType, int entityUserId) {
        return PREFIX_FOLLOWER + SPLIT + entityType + SPLIT + entityUserId;
    }
}
  1. Service,关注用有序集合保存,将日期保存为分数,这样可以实现按时间排序
@Service
public class FollowService{

    @Autowired
    RedisTemplate redisTemplate;


    // 关注
    public void follow(int entityType, int entityUserId, int userId) {
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // 粉丝,该用户是否包含当前用户的id
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityUserId);
                // 关注者,当前用户是否关注该用户
                String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, userId);

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

                return operations.exec();
            }
        });
    }
    // 取消关注
    public void unfollow(int entityType, int entityUserId, int userId) {
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // 粉丝,该用户是否包含当前用户的id
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityUserId);
                // 关注者,当前用户是否关注该用户
                String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, userId);

                operations.multi();

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

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

    // 关注数量
    public long findFollowerCount(int entityType, int entityUserId) {
        // 关注者,当前用户是否关注该用户
        String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, entityUserId);
        return redisTemplate.opsForZSet().zCard(followeeKey);
    }

    // 粉丝数量
    public long findFolloweeCount(int entityType, int entityUserId) {
        // 粉丝,该用户是否包含当前用户的id
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityUserId);
        return redisTemplate.opsForZSet().zCard(followerKey);
    }

    // 是否关注
    public boolean hasFollow(int entityType, int entityUserId, int userId) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, userId);
        return redisTemplate.opsForZSet().score(followeeKey, entityUserId) != null;
    }
}

  1. Controller

2 关注、粉丝列表

  • 业务层
    • 查询某个用户关注的人,支持分页。
    • 查询某个用户的粉丝,支持分页。
  • 表现层
    • 处理“查询关注的人”、“查询粉丝”请求。
    • 编写“查询关注的人”、“查询粉丝”模板。
  1. Service
// 关注列表
    public List<Map<String, Object>> followees(int userId, int offset, int limit) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(ENTITY_TYPE_USER, userId);
        // 取
        Set<Integer> set = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);
        if (set == null) {
            return null;
        }
        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer id : set) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(id);
            Double score = redisTemplate.opsForZSet().score(followeeKey, id);
            map.put("user", user);
            map.put("followTime", new Date(score.longValue()));

            list.add(map);
        }
        return list;
    }

    // 粉丝列表
    public List<Map<String, Object>> followers(int userId, int offset, int limit) {
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
        Set<Integer> set = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);
        if (set == null) {
            return null;
        }
        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer id : set) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(id);
            Double score = redisTemplate.opsForZSet().score(followerKey, id);
            map.put("user", user);
            map.put("followTime", new Date(score.longValue()));

            list.add(map);
        }
        return list;
    }
  1. Controller
@RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET)
    public String followees(@PathVariable("userId") int userId, Page page, Model model) {
        User user = userService.findUserById(userId);
        if (user == null) {
            throw new RuntimeException("该用户不存在!");
        }
        model.addAttribute("user", user);

        page.setPath("/followees/" + userId);
        page.setRows((int)followService.findFolloweeCount(ENTITY_TYPE_USER, userId));
        page.setLimit(5);

        // 判断用户是否关注状态
        List<Map<String, Object>> userList = followService.followees(userId, page.getOffset(), page.getLimit());
        for (Map<String, Object> map : userList) {
            User u = (User) map.get("user");
            map.put("hasFollowed", hasFollow(u.getId()));

        }
        model.addAttribute("users", userList);
        return "/site/followee";
    }

    @RequestMapping(path = "/followers/{userId}", method = RequestMethod.GET)
    public String followers(@PathVariable("userId") int userId, Page page, Model model) {
        User user = userService.findUserById(userId);
        if (user == null) {
            throw new RuntimeException("该用户不存在!");
        }
        model.addAttribute("user", user);

        page.setPath("/followers/" + userId);
        page.setRows((int)followService.findFollowerCount(ENTITY_TYPE_USER, userId));
        page.setLimit(5);

        // 判断用户是否关注状态
        List<Map<String, Object>> userList = followService.followers(userId, page.getOffset(), page.getLimit());
        for (Map<String, Object> map : userList) {
            User u = (User) map.get("user");
            map.put("hasFollowed", hasFollow(u.getId()));

        }
        model.addAttribute("users", userList);
        return "/site/follower";
    }

    private boolean hasFollow(int userId) {
        if (hostHolder.getUser() == null) {
            return false;
        }
        return followService.hasFollowed(ENTITY_TYPE_USER, userId, hostHolder.getUser().getId());
    }

使用Redis优化登陆模块

  • 使用Redis存储验证码
    • 验证码需要频繁的访问与刷新,对性能要求较高。
    • 验证码不需永久保存,通常在很短的时间后就会失效。
    • 分布式部署时,存在Session共享的问题。
  • 使用Redis存储登录凭证
    • 处理每次请求时,都要查询用户的登录凭证,访问的频率非常高。
  • 使用Redis缓存用户信息
    • 处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值