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);
    }

    ```

  • 相关阅读:
    在日本被禁止的コンプガチャ設計
    Starling常见问题解决办法
    Flixel引擎学习笔记
    SQLSERVER中修复状态为Suspect的数据库
    T4 (Text Template Transformation Toolkit)实现简单实体代码生成
    创建Linking Server in SQL SERVER 2008
    Linq to Sql 与Linq to Entities 生成的SQL Script与分页实现
    Linq to Entity 的T4 模板生成代码
    在VisualStudio2008 SP1中调试.net framework 源代码
    使用HttpModules实现Asp.net离线应用程序
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/11260622.html
Copyright © 2011-2022 走看看