redis实现点赞功能

Mysql

将用户的点赞记录写入Mysql

  • 记录文章被赞的次数
  • 用户点赞了那些文章

缺点

热门文章会有很多用户点赞,甚至是短时间内被大量点赞,数据库读写压力大。

redis存储、批量刷回数据库

redis存储数据快,支持多种数据结构,且支持incr方法

优点

  1. 性能高
  2. 缓解数据库读写压力

缺点

  1. 开发复杂
  2. 不能保证数据安全性,redis挂掉的时候会丢失数据,同时不及时同步redis中的数据, 可能会在redis内存置换的时候被淘汰掉

具体实现

Mysql

新增文章点赞数字段,用户点赞文章表。

Redis

public void saveLiked2Redis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.LIKE.getCode());
    }

    public void unlikeFromRedis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.UNLIKE.getCode());
    }

    public void deleteLikedFromRedis(String likedUserId, String likedPostId) {
        String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId);
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
    }

    public void incrementLikedCount(String likedUserId) {
       redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, 1);
    }

    public void decrementLikedCount(String likedUserId) {
       redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, -1);
    }

    public List getLikedDataFromRedis() {
        Cursor> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE);
        List list = new ArrayList<>();
        while (cursor.hasNext()){
            Map.Entry entry = cursor.next();
            String key = (String) entry.getKey();

            String[] split = key.split("::");
            String likedUserId = split[0];
            String likedPostId = split[1];
            Integer value = (Integer) entry.getValue();

            UserLike userLike = new UserLike(likedUserId, likedPostId, value);
            list.add(userLike);

            redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
        }

        return list;
    }

    public List getLikedCountFromRedis() {
        Cursor> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE);
        List list = new ArrayList<>();
        while (cursor.hasNext()){
            Map.Entry map = cursor.next();
            String key = (String)map.getKey();
            LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue());
            list.add(dto);
redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key);
        }
        return list;
    }

使用hashscan方法获取单个hashkey的所有数据,

  1. scan命令的时间复杂度虽然也是O(N),但它是分次进行的,不会阻塞线程。
  2. scan命令提供了limit参数,可以控制每次返回结果的最大条数。
  3. scan命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。
  4. scan命令返回的是一个游标,从0开始遍历,到0结束遍历。

key下的field过多的情况,可以考虑根据按时间或区模的方式拆分key

你可能感兴趣的:(redis实现点赞功能)