Redis ~

180 阅读7分钟

如何保证缓存和DB一致性?

  • 首先我们对数据主要两类操作: 读或写
  • 存在两种问题:
    • 一种是执行顺序问题: 先更新缓存还是更新数据库
    • 一种是更新缓存策略问题,是进行缓存的淘汰还是进行缓存的修改。
  • 更新cache和淘汰cache
    • 淘汰cache的话操作简单,下一次查询在缓存中查不到,重新读取数据库加载缓存
    • 更新cache
      • 缺点:更新cache消耗大, 更新操作复杂时候,涉及其他因素 需要跟DB进行多次交互,还不如直接淘汰cache另外如果是高并发下,更新cache很可能会出现数据不一致的问题,A线程更新DB,A网络延迟,B更新DB,B更新缓存,A更新缓存,最终读取到的是A的缓存,那么数据可能就不一致。
    • 推荐使用淘汰缓存的策略。
  • 两种方案:
    • 淘汰缓存,更新DB
      • 数据不一致的情况:并发较大的情况下:A先淘汰缓存,在更新DB,B获取DB,此时还未更新,获取后的数据存入缓存,A在更新到DB,此时缓存与DB存在不一致的情况,此时A在进行一次缓存删除即可解决这种问题。(延迟删除)
    • 更新DB,淘汰缓存:
      • 数据不一致情况:并发超大情况下: 线程成A读DB-未放入缓存,线程B更新DB,存入缓存,A在把缓存放入进去,此时可能会造成DB与缓存不一致的情况 (发生概率比较小 读操作一般比写快),只需要在进行一次延时删除。即可避免这种问题。更新缓存时可以开启一个异步线程,休眠读的时间,然后在进行更新

Redis热点key,大Key如何解决?

  • 热key: 流量过于集中,打到物理网卡上线,从而导致这台redis服务器宕机,key的请求可能会直接全部打到DB,导致服务不可用。
  • 如何发现热key?
    • 首先根据业务来判断哪些是热key,比如商品做个秒杀,那这个商品的key就可以判断出是热key
    • 在客户端进行收集,可以通过消息的方式等进行统计。
    • redis 自带热点key发现功能
    • 做一个代理层,在代理层进行redis 数据上报
  • 如何解决热点key
    • 利用二级缓存
    • 备份热key
      • 将热点数据key+随机数,该key会hash到不同的redis服务中,从而降低单台redis服务的压力。 (redis集群槽位相关知识...)

Redis 大key问题讨论与解决方案?

  • 单个简单的key的value很大

  • hash,set,zset,list 中存储过多的元素

  • 一个集群存储了上亿的key

  • 因为redis是单线程操作,大key由于本身空间太大,读取,删除,以及自动过期的时候灰枣成redis实例的qps突降或者突升,造成主从复制异常,redis服务阻塞无法响应请求。

  • 解决bigKey常用的方法

    • 可以将对象拆分成几个 key-Value,使用multiGet,将操作压力平摊到多个Redis示例中,降低对单个Redis的IO影响
    • 将对象存储在一个hash中,每次获取部分部分value
    • 减少key的个数,服务端会建立一些redis插槽与key的映射关系,key过多的情况下也会浪费巨大空间。
    • 或者使用mongoDB

Redis 数据过期的清除策略?

  • redis过期时间判断?
    • redis内部,每当我们设置一个键的过期时间时候,redis会将该键带上过期时间存放到一个过期字典中,当我们查询一个键时,redis便首先检查该键是否存在过期字典中,如果存在那么就获取其过期时间,然后将过期时间和当前系统时间进行对比,比系统时间大,那么就没有过期,否则过期
  • 过期删除策略? -www.cnblogs.com/ysocean/p/1…
    • 定时删除
      • 在设置某个key的时候,创建一个定时器,让定时器在该过期时间到来时,立即执行对其删除操作。
    • 惰性删除
      • 当使用该key时候,我们检查其是否过期,如果过期了那么就将他删除,否则返回key。
    • 定期删除
      • 每隔一端时间,就对一些key进行检查,删除里面过期的key。
    • redis 过期策略: 采用组合模式:
      • 惰性删除 + 定期删除 : 配合使用, 但是还是存在一些问题: 对于某些永远使用不到的键,并且多次定期删除也没有选定到并删除,那么这些键会一直留在内存中,又或者在Redis中存入了大量的键,这些操作可能会导致Redis内存不够用,这时候就需要Redis的内存淘汰策略:
      • 内存淘汰策略:
        • volatile-lru LRU移除设置过 过期时间的key
        • allkeys-lru LRU移除任何key
        • volatile-random 移除设置过 过期时间的随机key
        • allkeys-random 无差别的随机移除
        • volatile-ttl 移除即将过期的key
        • noeviction - 不移除任何key 只返回一个错误提示

Redis单线程模型?为什么是单线程的?

  • Redis是基于内存的操作,CPU不是Redis的瓶颈,是机器内存的大小或者网络的带宽.
  • 单线程的情况不需要考虑各种锁的问题
  • IO多路复用技术:
    • redis 采用网络IO多路复用技术,保证在多连接的时候,提升系统的吞吐量,多路指的是多个socket连接,复用指的是复用一个线程,多路复用主要有三种技术 select,poll,epoll, epoll 是目前最新的最好的技术。 epoll:zhuanlan.zhihu.com/p/63179839
    • 总结:
      • 1.redis是纯内存数据库,主要就是存取操作,时间主要就是集中在IO上.

      • 2.IO,redis使用的非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开关读写都转换成了事件,减少了线程切换时上下文的切换和竞争。

      • 3.数据结构 全程使用hash结构,读写速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对端数据进行压缩存储,在如,跳表,使用有序的数据结构加快读取的速度。

      • 4.redis采用自己实现的事件分离器。

Redis基本数据结构

  • 字符串
    • sds 动态字符串,不同长度的字符串可以使用不同的结构体来进行表示,sds会保存字符串实际长度以及剩余可用长度,当追加时,如果长度小于等于分配空间,那么不进行扩容,否则计算字符串新长度,然后分配2倍长度所需的空间。 更新free。
  • 列表
    • 相当于java中的linkedlist,是链表不是数组,以为这list的插入和删除操作非常快,时间复杂度为O(1),可以实现栈 先进后出以及队列 先进先出
      • rpush 链表尾部压入
      • rpop 链表尾部弹出
      • lpush 填表头部压入
      • lpop 链表头部弹出
      • lrange 0 -1
  • hash 字典
    • www.wmyskxz.com/2020/02/28/…**
    • 相当于hashMap, 数组+链表的连地址来解决 哈希冲突,扩容时候使用渐进式rehash
    • 扩缩容条件:hash表中的元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是原数组大小的2倍。 也会去进行缩容。
  • set 集合
    • 通过hash表来实现的,value都为null
  • zset 有序集合
    • 通过压缩列表(集合元素比较少时) or 通过字典以及跳跃表来进行实现(集合元素比较多的时候)
    • 跳跃表:有序链表 + 分层节点 每个节点会随机生成一个层数,在插入的时候会维护这个节点的层数,等其查找时先查找高层的,最终一层一层找,最终回到第一层链表的位置进行查询。

Redis 中的sortSet

  • 当数据较少的时候,sortSet是由一个ziplist来实现的.

  • 当数据多的时候,sortedSet是由一个叫zset的数据结构来实现的,这个zset包含一个dict+skiplist,dict用来查询数据到分数之间的关系,skiplist用来根据分数查询数据, (可能是范围查询),