美文网首页
你真的了解Redis忙?(一)

你真的了解Redis忙?(一)

作者: 以南之南_b9a1 | 来源:发表于2019-11-07 23:34 被阅读0次

    redis的基础数据结构

    1.String

    常见命令:set get incry decr

    2. hash

    常见命令:hmset hmget hkeys

    3. set(集合)

    常见命令:sadd smembers

    4. list

    常见命令:lpush lpop

    5. zset (有序集合)

    常见命令:zadd
    zcard (获取有序集合的成员数)
    zremrangebyscore (命令用于移除有序集中,指定分数(score)区间内的所有成员)
    zrange (通过索引区间返回有序集合指定区间内的成员)

    6.HyperLogLog

    作用:是用来做基数统计的算法,每个key只需要花费 12 KB 内存 标准误差在0.81%
    常见命令;pfadd pfcount
    使用场合:用于统计UV 和PV

    7.GeoHash

    简单介绍:如果利用数据库来算"附近的人"?如果使用关系型数据库,如何存储?大家都用过摩拜单车 ,就会查看附近的车辆。Redis 提供了Geo 指令 一共有6个
    常见命令:
    geoadd 指令携带集合名称和经纬度以及三元组
    geoadd company 118.2901 39.0909 alibaba
    geoadd company 209.2102 39.9028 jd
    geolist 指令计算两个元素之间的距离,携带集合名称,距离单位。
    geolist company alibaba jd km
    geohash 获取元素的hash 值
    geohash company jd 返回的是base32位编码。
    georadiusbymember 指令用来查询指定元素附近其他的元素。
    georadiusbymember company jd km count 3 asc 范围20公里以内最多3个元素按照距离正序排列。

    8.布隆过滤器

    应用场景:我们在使用新闻客户端看新闻时,他会给我们推荐新的内容,他每次推荐都会去重,去掉那些看过的内容。如果历史记录存再关系型数据库里,需要频繁的exists查询,当系统并发量很高时,数据库是很难抗拒压力的。
    常见命令:bf.add bf.exists (bf.madd和bf.mexists)
    缺点:不是特别精确,会误判,也不是特别不准确,只要参数设置合理,它的准确的可以很高。

    9.位图

    开发场景:平时开发中,我们都需要对一些bool 类型的数据进行存取,比如用户一年签到的数据。签了为1 没签到就是0 。如果有普通的key/value 那么当用户上亿 需要的存储空间 是惊人的。
    为了解决这个问题我们可以使用redis提供的位图这种数据结构。每个签到就占一个位,365天就365个位。
    常用命令:getbit setbit bitcount bitpop bitfield
    Key的格式为u:sign:uid:yyyyMM

    # 用户2月17号签到
    setbit u:sign:1234:201802 16 1  # 偏移量是从0开始,所以要把17减1
    # 检查2月17号是否签到
    getbit u:sign:1234:201802 16
    # 统计2月份的签到次数
    bitcount u:sign:1000:201902
     获取2月份前28天的签到数据
    bitfield u:sign:1000:201902 get u28 0
    # 获取2月份首次签到的日期
    bitpos u:sign:1000:201902 0 # 返回的首次签到的偏移量,加上1即为当月的某一天
    

    redis的分布式锁

    redis除了上述 应用 还可以用于做分布式锁。举个栗子
    redis中缓存了一个int值 现在有两个客户端需要同时读取这个数,然后+20,次数就会出现并发问题 如下图


    redis分布式锁.png

    redis的分布式锁 其实就是占坑,当别的进程进来的时候,发现已经有人了,那么就会放弃或者稍后再试。
    命令

    setnx  //命令 
    setnx lock:user:123 true
    do something
    del lock:user:123
    

    那么就会出现问题,如果do something 出现异常了,锁就没有释放,这样就会永远陷入死锁,永远不会释放。

    解决方案1. 给锁设置失效时间
    setnx lock:user:123 true
    expire lock:user:123 5
    //do something
    del lock:user:123
    

    此时也会有问题 ,如果在加锁和设置锁超时之间服务器挂掉了,那么锁也不会得到释放,究其原因是因为:加锁和设置时间不是原子指令。如果这两条指令一起执行就不会有问题。
    redis2.8 版本以上加入了set 指令的扩展,是的setnx 和expire一起执行,

    set lock:user:123 true ex 5 nx ok
    //do something
    del lock:user:123
    

    缺陷:
    1.不能解决超时问题:如果加锁或者释放锁的执行时间过长,超出了锁的时间限制,就会出现问题。

    利用redis做简单限流:

    案例:如果有几片文章,我们限制这个用户几分钟内只能回复几次,就可以用redis来做简单限制
    方案:我们使用zset 结构,score为时间戳,value唯一就行。可以把zset中的score看做一个窗口 ,我们只维护窗口以内的数据。

      /**
        userId:用户编号
        userAction:用户事件,比如点赞,回复评论。
         provit:限制事件,单位为秒
         maxcount:次数
        **/
        public boolean isAllowAction(int userId,String userAction,int provit,int maxCount) throws IOException {
            String key = String.format("userAction:%d:%d",userId,userAction);
            Jedis jedis = new Jedis();
            long systemTime = System.currentTimeMillis();
            Pipeline pipeline = jedis.pipelined();
            pipeline.multi();
            pipeline.zadd(key,systemTime,systemTime+"");
            pipeline.zremrangeByScore(key,0,systemTime - provit * 1000);
            Response<Long> userAccount = pipeline.scard(key);
            pipeline.expire(key,provit+1);
            pipeline.exec();
            pipeline.close();
            return userAccount.get() > maxCount;
        }
    

    如何查询redis中的某个Key呢?

    1.简单暴力用keys 和正则表达式 keys * ;如果redis中有成千上百万的key 这样会不会太慢了?而且keys 命令是遍历所有的Keys,如果在线上环境,使用改命令,则会造成redis卡顿,redis 为单线程。keys 命令的时间复杂度为O(n).
    2.使用Scan命令 同keys 一样 他也提供了匹配的功能,提供了limit参数 返回结果可多可少。
    命令:scan 0 match user_* count 1000; 一共有三个参数,第一个是cursor 整数值,第二个key则为正则表达式,第三个则为limit him (limit 1000 不是限定返回结果的数量,而是限定单次服务器遍历的字典槽位数量)

    相关文章

      网友评论

          本文标题:你真的了解Redis忙?(一)

          本文链接:https://www.haomeiwen.com/subject/rmevbctx.html