Redis位图实现点赞签到相关功能

2022-10-27 15:46:08 浏览数 (2)

前言

对于我们平时的一些社区应用,如微博,知乎,掘金等应用点赞,评论这类功能是不可或缺的,例如点赞功能我们其实是可以通过 mysql 去做实现的,但是每次点赞都去实时改库可以想象一下当遇到一个热点文章例如,前段时间大火特火的爆料某艺人日薪多少帖子,这个上千万乃至亿级的点赞量,这个时候我们再去实时改库的话就不那么恰当,今天我们介绍的主角是Redis的位图操作,接下来我们引出正题,来看一下位图是这么做到的;

介绍

在 Redis 里位图并不是一个真正的数据类型,其实就是一种普通的字符串,也可以说是byte数组。它是定义在字符串类型中,一个字符串类型的值最多能存储512M字节的内容也就是2^32b

❝2^(9(512) 10(1024) 10(1024) 3(8b=1B))=2^32b ❞

应用场景:

  • 用户签到
  • 用户在线状态
  • 统计活跃用户
  • 各种状态值
  • 自定义布隆过滤器
  • 点赞功能

可以想象一下假如我们要统计一个用户一年的签到记录,签了是 1,没签是 0,要记录 365 天。如果使用普通的 key/value,每个用户要记录 365个,当用户上亿的时候,需要的存储空间是惊人的。因为只统计0,1我们可以使用位图来进行存储,这样每天的签到记录只占据一个位,365 天就是 365 个位,46 个字节 (一个字节有8位) 就可以完全容纳下,这样就很节省资源了,同时也提高了效率;

基本命令:

代码语言:javascript复制
SETBIT key offset value 
//对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
//offset 参数必须大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之内)。

GETBIT key offset
//对 key 所储存的字符串值,获取指定偏移量上的位(bit)。

BITCOUNT key
//计算给定字符串中,被设置为 1 的比特位的数量。

BITPOS key bit [start] [end]
//返回位图中第一个值为 bit 的二进制位的位置。

BITOP operation destkey key [key …]
//对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。

BITFIELD
//bitfield 有三个子指令,分别是 get/set/incrby,它们都可以对指定位片段进行读写,
//但是最多只能处理 64 个连续的位,如果超过 64 位,就得使用多个子指令,bitfield 可以
//一次执行多个子指令。

简单使用:

代码语言:javascript复制
//添加
127.0.0.1:6379> setbit a 2 1
(integer) 0
127.0.0.1:6379> setbit a 3 1
(integer) 0
//查找
127.0.0.1:6379> getbit a 2
(integer) 1
//统计
127.0.0.1:6379> bitcount a
(integer) 2

点赞功能

我们先拿点赞功能来做一个简单的编码介绍:

帖子1(post1):

代码语言:javascript复制
postId:1 
postName:《震惊!业余草日薪高达208w,超过马云》

帖子2(post2):

代码语言:javascript复制
postId:2
postName:《业余草持刀狂追某用户8条街,原因是该用户看文不点赞》

用户1(user)

代码语言:javascript复制
id:1001
name:业余草

。。。。

点赞功能

我这里的处理方式是异步改库,点完赞之后异步修改数据库,不要求实时处理结果其实可以用定时任务去批量改库(需要存一定时间内的 postId 和 userId 索引关系);

代码语言:javascript复制
@Override
public boolean giveLike(String userId, Long postId) {
    try (Jedis jedis = redisUtil.getJedis()) {
        //设置用户点赞
        jedis.setbit(userId, postId, true);
        //设置帖子点赞
        jedis.setbit(String.valueOf(postId), Long.valueOf(userId), true);
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                //同时可以异步将点赞信息写到数据库中
                updateGiveLikeInfo(userId, postId);
            }
        });
        return true;
    } catch (Exception e) {
        return false;
    }
}

取消点赞的操作和这个相反将jedis.setbit(userId, postId, false);就OK了!

统计点赞数:

代码语言:javascript复制
@Override
public Long getGiveLikeByUserId() {
    try (Jedis jedis = redisUtil.getJedis()) {
        String userId = "1001";
        String PostId = "1";
        System.out.println(jedis.bitcount(userId));
        System.out.println(jedis.bitcount(PostId));
        return 1L;
    } catch (Exception e) {
        return 0L;
    }
}

在页面展示的时候我们我们只需要查询当前的这个 bitmap 的总数就 ok 了!

范围统计

当然我觉得范围查找最实用的场景就是签到统计 第一种玩法:userId 为 key,时间戳为 offset,签到 1 未签到 0 可以使用以下命令:

  • BITPOS key bit start :从start 1个字节开始查找,直到尾部
  • BITPOS key bit start end:从start 1字节开始到end 1字节之间查找

第二种玩法:时间为 key,userId 为 offset,签到 1 未签到 0 可以使用以下命令:

  • BITOP operation destkey key [key ...]

对一个或多个保存二进制位的字符串 key 进行位操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种

❝ok!一个简单的位图实现点赞等功能就实现了,大家一个举一反三哈,玩法还是很多的,大家也可以根据自己的需求去做优化,希望可以对大家有帮助,有不对的地方希望大家可以提出来的,共同成长。 ❞

0 人点赞