zoukankan      html  css  js  c++  java
  • redis相关

    一、redis能做什么?

      1、缓存

      2、分布式锁

      3、延迟队列

    二、redis基本数据结构?

      字符串string、列表list、字典hash、集合set、有序集合zset

      redis中所有的数据结构都是以唯一的key字符作为名称,然后通过这个key来获取相应的value数据,不同数据类型的差异就在于value的结构不同。

      1、字符串string

        redis中的字符串都是动态字符串,是可修改的,采用预分配冗余空间的方式来减少内存的频繁分配,内部为当前字符串分配的空间(capacity)是大于当前字符串的长度len的,当前字符串的长度小于1MB时,扩容

        都是翻倍现有的空间的。如果字符串长度大于1MB时,扩容一次只会扩大1MB,并且字符串的最大长度是512MB。

      2、列表list

        redis中的列表相当于java中的linkedlist,注意他是链表而不是数组。这也就也为着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位慢,时间复杂度为O(N)。列表中的每个元素都使用双向指针,

        穿起来可以同时支持前向后向遍历,当列表弹出最后一个元素之后,该数据结构就会被自动删除。redis列表常用来做异步队列使用,将需要延后处理的任务结构序列化常字符串,塞进redis列表,另一个线程从这个

        列表中轮询数据进行处理。

      3、字典hash

        redis中的字典相当于java中的hashMap,他是无序字典,内部存储了很多键值对,数据机构也是数据+链表的结构。Java中的rehash是个耗时的操作,需要一次性全部rehash。redis为了性能,采用的是渐进式rehahs策略

      4、集合set

        redis集合相当于java中的hashSet,他内部的键值对是无序的,唯一的,他内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL

      5、有序列表zset

        类似于java的Sortedset和hashmap的结合体,一方面是一个set,保证内部value的唯一性,另一方面他可以给每个value赋予一个score,代表这个value的权重,它内部实现用的是一种叫跳跃列表的数据结构。

      高级数据结构:

      heperloglog:用来解决统计问题的数据结构(不精确,标准误差在0.81%)

      BloomFilter(布隆过滤器):可以理解为不精确的set结构,当使用它的contains方法判断某个对象是否存在是,他可能会存在误判

      geohash(地理位置)

    三、分布式锁

      分布式应用在进行逻辑处理时经常会遇到并发问题。

      分布式锁本质上要实现的目标就是在redis中占一个“坑”,当别的进程也要来占时,发现已经被占用时,就只好放弃或者稍后再试。占坑一般使用的setnx(set if not exists)指令,只允许被一个客户占用,先来先用,用完了,

      在调用del指令释放坑。

      一般会有一个问题,当锁定坑后,处理业务逻辑异常,可能导致del命令没有被调用,这样就会陷入死锁,锁得不到释放,因此,一般在拿到锁后,会给锁加一个过期时间,这样即使出现异常也不会出现锁被永远占用的情况

      (上边这个解决方式仍然会有问题,当在设置过期时间时,如果因为网络问题导致expire命令没有执行,这样仍然会出现锁一直得不到释放的情况,解决方式,使用第三方库,或者使用redis2.8之后的版本,2.8之后set指令中加

      了扩展参数,可以设置过期时间,这样setnx和expire两个指令就可以一起执行了)

     分布式锁超时问题:

      redis的分布式锁不能解决超时问题,如果加锁和解锁中间的业务流程耗时过长,大于锁的过期时间时,这样就会出现问题。

    四、延迟队列

      相对于专业的Rabbitmap或者kafka等消息队列中间节,redis针对只有一组消费者的情况来说可以简化很多操作(中间件各种操作过多,代码繁杂,如果对消息可靠性要求不高的话,可以使用redis来作为消息中间件)。

      通过redis中的列表list来实现,list常用来作为异步的消息队列来使用,用rpush和lpush来操作入队,使用rpop和lpop来操作出队。它可以支持多个生产这和多个消费者并发进出消息,每个消费者拿到的消息都是不同的列表元素。

      问题:

      1、如果list空了怎么处理?

      list空了之后,调用pop指令会陷入死循环,不同的pop,并且没有数据,产生空轮询,增大cup消耗,降低redis的QPS 

        解决:可以使用sleep睡眠以下,但是该方式也不适合,另一种方式就是使用阻塞读blpop/brpop,b代表的就是blocking

      2、阻塞读之后,可能会产生空闲链接的问题?

      如果在阻塞读时,列表中一直没有数据,线程就会一直阻塞,redis的客户端链接就变成了空闲链接,闲置过久,服务器一般就会主动断开链接,减少闲置资源的占用,这时候brpop/blpop指令就会抛出异常,因此,再使用阻塞读

      时,一定要进行try/catch捕获异常,并且还要进行重试。

    五、位图

      在平时开发过程中,会有一些boolean类型的数据要存储,例如一个用户一年的签到记录,签了是1,没签是0,要记录365天,如果使用普通的key/value,没有用户就要记录365个,当用户上亿时,需要的存储空间就非常大了。

      为了解决这个问题,redis提供了位图数据结构,这样每天的签到记录只会占用365个位,也就是46个字节(一个稍长的字符串)就可以完全容纳下,位图最小单位是bit,每个bit的取值只能是0或者1

      位图不是一种特殊的数据结构,让的内容其实就是普通的字符串,也就是byte数组,我们可以使用普通的get/set来操作位图,也可以使用getbit/setbit等将位图看成数组来操作。

      位图相关指令:

      getbit/setbit/bitcount/bitpos

      bitcount:统计指定位置范围内容1的个数

      bitpos:查找指定范围内出现的第一个0或者1

    六、redis的io模型

      redis是个单线程程序

      问题:

      redis是单线程,为什么还那么快?

        redis所有的数据都在内存中,所有的运算都是内存级别的运算

      为什么能够处理那么多的并发客户端连接?

        io的多路复用,select系列的时间轮询api,非阻塞io

    KEYS pattern:查找所有符合给定模式pattern的key

      keys指令一次返回所有匹配的key

      键的数量过大则会造成服务卡顿

    *从海量key里查询出某一固定前缀的key: SCAN cursor [MATCH pattern][COUNT count]

      基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程,以0作为游标开始一次新的迭代,知道命令返回游标0完成一次遍历。不保证每次执行都返回某个给定数量的元素,支持模糊查询。一次返回的数量不可控,只能大概率符合count参数

      第一次scan:  scan 0 match k1* count 10   :从0开始迭代,符合k1前缀的字符串,数量为10.

      第二次scan:    scan 11534336 k1* count 10

         多次scan可能获取到重复的数据

     分布式锁需要解决的问题:1、互斥性 2、安全性 3、死锁 4、容错

    如何通过redis实现分布式锁:

      SETNX key value:如果key不存在,则创建并赋值。

      时间复杂度:O(1),返回值:设置成功返回1,失败返回0

       

    如何解决SETNX长期有效的问题?

      设置过期时间:EXPIER  key seconds:设置key的生存时间,当key过期时,会自动删除

      

    以上代码的风险:当设置锁成功后,在执行if判断前,服务宕机(即没有设置过期时间),产生的原因:原子性得不到满足

    redis新版本set的时候可以设置过期时间(实现了原子性)

    SET key value [EX seconds] [PX milliseconds] [NX|XX] :

      EX seconds:设置键的过期时间为seconds秒

      PX milliseconds:设置过期时间毫秒值

      NX: 只在键不存在时,才对建进行设置操作

      XX:只在键已经存在时,才对键进行设置操作

      SET:操作成功完成时,返回OK,否则返回nil

    set lockid 1234 ex 10 nx

     大量的key同时过期时注意的事项:

      在设置过期的时候,给每个key减伤随机值

    分布式锁的删除:

    ```java

    public static boolean releaseLock(String key, String uniqueId) {
    String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
    "return redis.call('del', KEYS[1]) else return 0 end";
    return jedis.eval(
    luaScript,
    Collections.singletonList(key),
    Collections.singletonList(uniqueId)
    ).equals(1L);
    }

    ```

  • 相关阅读:
    ha-wordy-Write-up
    HA: Infinity Stones-Write-up
    为什么k8s引入pod概念?
    vxlan 跨网段虚拟机迁移
    交换机配置
    Git四大组件(转)
    php-fpm
    docker容器中用户自定bridge网络与默认bridge网络之间的区别
    原型链
    'style-loader', 'css-loader'使用
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/11260622.html
Copyright © 2011-2022 走看看