Redis新API(Bitmap,GEO)

1.Bitmap 位图

1.1 Redis从2.2.0版本开始新增了setbit,getbit,bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展,因为redis存储String类型的数据是以二进制的形式存储,只不过bitmap是运用了二进制,不用String类型。
1.2 命令
命令说明
setbit key offset value设置指定键的二进制位图指定offset偏移量为value[0/1]
getbit key offset获取指定键的二进制位图指定offset偏移量上的value
bitcount key [start] [end]获取指定键的二进制位图所有的value为1有多少位
bitop [operation] destkey key [key …]对指定键做运算并把结果放入destkey目标键中,operation操作有 【AND 与运算 , OR 或运算 , XOR 异或运算 , NOT 非运算】
bitfield key [operation] [u/i]offset valueget是取出key对应的位图,指定value索引位开始,取offset位偏移量的二进制;
1.3 命令demo演示
  • setbit key offset value
    设置指定key的offset位上的bit值为value,value只能是1或0,返回在offset处原来的bit值
setbit lzj 0 1   #返回值 0

在这里插入图片描述

  • getbit key offset
    获取offset处的bit值
getbit lzj 0  #返回值 1
  • bitcount key [start] [end]
    获取开始索引 到 结束索引 之间bit值为1 的数量
bitcount lzj 0 -1 #返回值 1
  • bitop [operation] destkey key [key …]
    把指定的多个键的二进制进行计算操作,并把计算后的二进制放入指定键中,返回结果放入键中字符串长度,最多只能处理64个连续的位。
setbit lzj1 0 1 
bitop and result lzj lzj1  #返回值1
  • bitfield key [operation] [u/i]offset value
    get是取出key对应的位图,指定value索引位开始,取offset位偏移量的二进制;
bitfield result get u8 0  #返回128
bitfield result get i8 0  #返回-128
#其他 还没有弄懂

在这里插入图片描述

1.4 下面可以用java代码通过redistemplate去获取一个字符串的二进制位图
@SpringBootTest(classes = RedissiondemoApplication.class)
class ShopServiceImplTest {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    void consumer() {
    	redisTemplate.opsForValue().set("lzj","jj");
        String res = redisTemplate.opsForValue().get("lzj");
        //输出一个String类型的bitmap位图
        System.out.println(toBinary(res.getBytes()));
        //输出十进制数组
        System.out.println(Arrays.toString(res.getBytes()));
    }

    public static String toBinary(byte[] bytes){
        //把字符串转成字符数组
        StringBuilder result= new StringBuilder();
        for(int i=0;i<bytes.length;i++){
            //byte是8位的,而int是32位,直接转换会在前面补24个1,所以 & 0xFF 消除掉
            StringBuilder binary= new StringBuilder(Integer.toBinaryString(bytes[i] & 0xFF));
            int len=8-binary.length();
            if(len>0){
                //不足8位补够
                for(int j=0;j<len;j++){
                    binary.insert(0,"0");
                }
            }
            result.append(binary).append(" ");
        }
        return result.toString();
    }
}

在这里插入图片描述

可以看到从右到左是从低位到高位,二进制(8bit)转化为 十进制数组。
1.5 Bitmap怎么使用,根据二进制形式 是0 1 组成,所以我们可以用来做为一个用户的签到,用户签到就是1,没有签到就是 0(因为限制大小时512M就是2^32-1位),下面我将实现一个简单的demo。
1.6
@SpringBootTest(classes = RedissiondemoApplication.class)
public class Demo1 {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 用户签到
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 之前的签到状态
     */
    public boolean doSign(int uid, LocalDate date) {
        int offset = date.getDayOfMonth() - 1;
        return redisTemplate.opsForValue().setBit(buildSignKey(uid, date), offset, true);
    }

    /**
     * 检查用户是否签到
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 当前的签到状态
     */
    public boolean checkSign(int uid, LocalDate date) {
        int offset = date.getDayOfMonth() - 1;
        return redisTemplate.opsForValue().getBit(buildSignKey(uid, date), offset);
    }

    /**
     * 获取用户签到次数
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 当前的签到次数
     */
    public long getSignCount(int uid, LocalDate date) {
        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(buildSignKey(uid, date).getBytes()));
    }

    public static String toBinary(byte[] bytes){
        //把字符串转成字符数组
        StringBuilder result= new StringBuilder();
        for(int i=0;i<bytes.length;i++){
            //byte是8位的,而int是32位,直接转换会在前面补24个1,所以 & 0xFF 消除掉
            StringBuilder binary= new StringBuilder(Integer.toBinaryString(bytes[i] & 0xFF));
            int len=8-binary.length();
            if(len>0){
                //不足8位补够
                for(int j=0;j<len;j++){
                    binary.insert(0,"0");
                }
            }
            result.append(binary).append(" ");
        }
        return result.toString();
    }

    /**
     * 获取当月连续签到次数
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 当月连续签到次数
     */
    public long getContinuousSignCount(int uid, LocalDate date) {
        int signCount = 0;
        System.out.println(date.getDayOfMonth());
        //命令: bitfield key get [u/i]offset value 此命令就是get取出key对应的位图,指定value索引位开始,取offset位偏移量的二进制
        BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(date.getDayOfMonth())).valueAt(0);
        List<Long> list = redisTemplate.opsForValue().bitField(buildSignKey(uid, date),bitFieldSubCommands);
        if (list != null && list.size() > 0) {
            // 取低位连续不为0的个数即为连续签到次数,需考虑当天尚未签到的情况
            long v = list.get(0) == null ? 0 : list.get(0);
            for (int i = 0; i < date.getDayOfMonth(); i++) {
                //因为取的是无符号的二进制,所以右移一位,左移一位,依然相等只有是最右边是0[没签到]的时候,
                if (v >> 1 << 1 == v) {
                    // 只有除了第一天以外没签到,才算断签。
                    if (i > 0) break;
                } else {
                    // 签到了 签到数加1
                    signCount += 1;
                }
                //右移一位并赋值,相当于把最左边一位去除
                v >>= 1;
            }
        }
        return signCount;
    }

    /**
     * 获取当月首次签到日期
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 首次签到日期
     */
    public LocalDate getFirstSignDate(int uid, LocalDate date) {
        Long pos = redisTemplate.execute((RedisCallback<Long>) con -> con.bitPos(buildSignKey(uid, date).getBytes(), true));
        return pos < 0 ? null : date.withDayOfMonth((int) (pos + 1));
    }

    /**
     * 获取当月连续签到次数
     *
     * @param uid  用户ID
     * @param date 日期
     * @return 当月连续签到次数
     */
    public long getContinuousSignCount(int uid, LocalDate date) {
        int signCount = 0;
        System.out.println(date.getDayOfMonth());
        //命令: bitfield key get [u/i]offset value 
        BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(date.getDayOfMonth())).valueAt(0);
        List<Long> list = redisTemplate.opsForValue().bitField(buildSignKey(uid, date),bitFieldSubCommands);
        if (list != null && list.size() > 0) {
            // 取低位连续不为0的个数即为连续签到次数,需考虑当天尚未签到的情况
            long v = list.get(0) == null ? 0 : list.get(0);
            for (int i = 0; i < date.getDayOfMonth(); i++) {
                //因为取的是无符号的二进制,所以右移一位,左移一位,依然相等只有是最右边是0[没签到]的时候,
                if (v >> 1 << 1 == v) {
                    // 只有除了第一天以外没签到,才算断签。
                    if (i > 0) break;
                } else {
                    // 签到了 签到数加1
                    signCount += 1;
                }
                //右移一位并赋值,相当于把最左边一位去除
                v >>= 1;
            }
        }
        return signCount;
    }

    private static String formatDate(LocalDate date) {
        return formatDate(date, "yyyyMM");
    }

    private static String formatDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    private static String buildSignKey(int uid, LocalDate date) {
        return String.format("u:sign:%d:%s", uid, formatDate(date));
    }

    private Integer ID = 2017131132;

    /**
     * 当天签到
     */
    @Test
    void sign() {
        LocalDate today = LocalDate.now().minusDays(1);
        String todayDate = formatDate(today,"yyyyMMdd");
        System.out.println(doSign(ID, today)==true?"已签到【" + todayDate + "】":"签到成功【" + todayDate + "】");
    }

    /**
     * 检查当天是否签到
     */
    @Test
    void checkSign(){
        LocalDate today = LocalDate.now().minusDays(1);
        String todayDate = formatDate(today,"yyyyMMdd");
        System.out.println(checkSign(ID,today)==true?"【" + todayDate + "】已签到":"【" + todayDate + "】未签到");
    }

    /**
     * 获取今月 签到次数
     */
    @Test
    void getSignCount(){
        LocalDate today = LocalDate.now();
        long signCount = getSignCount(ID, today);
        System.out.println(today.getMonth().getValue() + "月份签到了" + signCount + "次");
    }

    /**
     * 获取连续签到次数
     * 注意: 如果当天没有签到是不算做断签,因为当天可以签到,如果前天没有签到就是断签了,不属于连续签到计算范围
     */
    @Test
    void getContinuousSignCount(){
        LocalDate today = LocalDate.now();
        long continuousSignCount = getContinuousSignCount(ID, today);
        System.out.println("连续签到" + continuousSignCount + "天");
    }

    /**
     * 获取第一天签到
     */
    @Test
    void getFirstSignDate(){
        LocalDate today = LocalDate.now();
        LocalDate firstSignDate = getFirstSignDate(ID, today);
        System.out.println("首次签到日期是" + formatDate(firstSignDate,"yyyyMMdd"));
    }

    /**
     * 获取用户的签到信息
     */
    @Test
    void getSignInfo(){
        LocalDate today = LocalDate.now();
        getSignInfo(ID, today).forEach((k,v) -> {
            System.out.println(k + "  : " + (v ?"√":"×"));
        });
    }

}

2.GEO

redis 3.2版本以上 才能使用的API
API作用
GEOADD key longitude latitude member用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中
GEOPOS key member [member …]用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
GEODIST key member1 member2 [m/km/ft/mi]用于返回两个给定位置之间的距离。
GEORADIUS key longitude latitude radius [m/km/ft/mi]获取以指定坐标内半径为raduis的圆内的元素个数
GEORADIUSBYMEMBER key member radius [m/km/ft/mi]获取以指定键的成员的内半径为raduis的圆内的元素个数
GEOHASH key member [member …]geohash 来保存地理位置的坐标。
  • GEOADD key longitude latitude member
    用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中
geoadd lzj 113.762141 22.978418 "广东科技学院"
geoadd lzj 113.769143 22.933629 "水濂山森林公园"
  • GEOPOS key member [member …]
    用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
geopos lzj "广东科技学院"
geopos lzj "水濂山森林公园"
  • GEODIST key member1 member2 [m/km/ft/mi]
    用于返回两个给定位置之间的距离。
geodist lzj "广东科技学院" "水濂山森林公园" km
  • GEORADIUS key longitude latitude radius [m/km/ft/mi]
    和GEODIST作用一样,但是 georadiusbymember 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。
    – WITHDIST : 返回距离
    – WITHCOORD : 返回经纬度
    – WITHHASH : 返回geohash值
    – [ASC/DESC] : 规定距离排序顺序
    – COUNT : 返回的指定记录数
georadius lzj 113 22 5 km
  • GEORADIUSBYMEMBER key member radius [m/km/ft/mi]
    获取以指定键的成员的内半径为raduis的圆内的元素个数
    – WITHDIST : 返回距离
    – WITHCOORD : 返回经纬度
    – WITHHASH : 返回geohash值
    – [ASC/DESC] : 规定距离排序顺序
    – COUNT : 返回的指定记录数
georadiusbymember lzj "广东科技学院" 7 km
  • GEOHASH key member [member …]
    geohash 来保存地理位置的坐标
geohash lzj "广东科技学院"





一键查询淘宝/拼多多内部优惠券,每日大额外卖红包,购物省钱的宝藏工具
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值