八股redis基础

Article Directory
  1. 1. String
  2. 2. Hash
  3. 3. List
  4. 4. Set
  5. 5. SortedSet(ZSet)
    1. 5.1. 使用redis做延迟队列
  6. 6. GEO和BitMap
  7. 7. 新的数据类型Stream,消息队列
  • Redis Pipeline 原理及注意事项
  • 缓存穿透
  • 缓存雪崩
  • 缓存击穿
    1. 1. 持久化
    2. 2. 主从和哨兵(高可用、高并发读)
    3. 3. 分片(海量数据存储、高并发写)
    4. 4. 多级缓存
      1. 4.1. OpenResty
      2. 4.2. Redis缓存预热
    5. 5. 缓存同步
    6. 6. AOP:事务、动态代理
    7. 7. SDS动态字符串
    8. 8. IntSet
    9. 9. Dict
    10. 10. ZipList
    11. 11. QuickList
    12. 12. SkipList跳表
    13. 13. RedisObject
    14. 14. 五种数据结构是如何的
    15. 15. 内存回收
  • 有关发布订阅
  • Feed流
    1. 1. 实践:本项目中使用推
  • Redis锁从面试连环炮聊到神仙打架。
    1. 1. Kafka中,consumer 是推还是拉?
  • 在Java中发送kafka消息
  • Kafka集群消息积压问题和数据倾斜问题及处理策略
    1. Kafka消息积压的典型场景:
      1. 1. 实时/消费任务挂掉
      2. 2. Kafka分区数设置的不合理(太少)和消费者”消费能力”不足
      3. 3. Kafka消息的key不均匀,导致分区间数据不均衡
    2. 一般情况下,针对性的解决办法有以下几种:
      1. 1. 实时/消费任务挂掉导致的消费滞后
      2. 2. Kafka分区少了
      3. 3. 由于Kafka消息key设置的不合理,导致分区数据不均衡
  • 一种解决消费海量Kafka Topic时数据倾斜的方案
  • 为什么Redis的操作是原子性的,怎么保证原子性的?
    1. 为什么Redis的操作是原子性的,怎么保证原子性的?
    2. 多个命令在并发中也是原子性的吗?
  • 数据结构——Redis中的bitmap
  • Redis(Remote Dictionary Server)

    视频教程:全面透析redis底层原理+redis分布式锁+企业解决方案+redis实战

    redis是基于内存的,速度快;IO多路复用;有丰富的数据类型,合适的选择可以提高运行效率。

    String

    setnx加ex超时,配合lua,分布式锁。

    有自增自减操作(INCR -1),SETNX,将对象序列化为JSON字符串后存储

    缓存击穿中热点key加互斥锁的时候可以用SETNX来实现()

    分布式中全局唯一ID,每天一个key(方便统计)、ID构造是 时间戳 + 计数器(自增实现)

    MSET、MGET、INCRBY、SETNX

    Hash

    可重入锁,field是threadid,value是次数,配合lua。

    无序字典,里面会有对应域和值,可对单个字段做独立的CRUD

    批量:HMSET、HMGET

    List

    异步队列,生产者消费者,下的单进行消费,但是单一消费者。

    双向链表,插入和删除快

    可用BLPOP和RPUSH,或者BRPOP和LPUSH,实现阻塞队列,缺点:

    Set

    与hashset,可看作value为null的hashmap,无序、不重复,查找快,交集、并集、差集等

    比如看自己跟别人的共同关注列表

    IMG_C90E028F35B5-1

    SortedSet(ZSet)

    每一条博客下面,按照用户点赞时间排序给用户排序。

    底层实现是skiplist跳表+hash表

    根据score排序,比如用来做排行榜等,不重复,查询快,里面有一个指令是incrementScore,每次查询这个key之后都会自增1,而不是说我每次自己取出来+1再放回去。

    IMG_C1D12C84936E-1

    使用redis做延迟队列

    使用redis的zset实现简单的延时队列

    使用一个延时队列,利用redis的zset(sort set,有序不重复集合,关联分数score进行排序),将提醒时间作为分数,提取符合条件的score对应的集合发起提醒(本文所述也是围绕这个方案)

    1
    2
    3
    Boolean addFlag = redisTemplate.opsForZSet().add(QUEUE_NAME, messageStr, queueMessage.getDelayTime());

    Set<String> strings = redisTemplate.opsForZSet().rangeByScore(QUEUE_NAME, 0, System.currentTimeMillis());

    GEO和BitMap

    ==geo==获取附近商铺信息

    ==bitmap==比如每日签到,签到统计

    注意

    GEO是sortedset的实现

    bitmap其实是==string数据类型==实现

    新的数据类型Stream,消息队列

    Redis Pipeline 原理及注意事项

    image-20220909162340144 Sf400z

    缓存穿透

    拜托,面试官别问我「布隆」了

    客户端请求的,在缓存中和数据库中都不存在

    1. 缓存空对象(+TTL)
    2. 布隆过滤(BitMap实现)(多一层前置过滤,存在误判)

    缓存雪崩

    同一时间大量缓存的key同时失效 或 Redis服务宕机,大量的请求到达服务器

    针对不同的有不同的方法,给不同的key的TTL添加波动的随机值;Redis主从集群哨兵(能解决高并发读,高可用),分片集群(高并发写,海量数据存储)。

    但是如果真的出现了,也要考虑。给缓存业务添加降级限流策略(消息队列固定请求大小,削峰填谷。判失败,拒绝服务器),给业务添加多级缓存。

    缓存击穿

    热点key都会首先缓存到redis中的。

    热点key问题,被高并发访问并且缓存重建业务复杂(重建的时间内大量的访问)的key失效了。

    解决:互斥锁(SETNX(),需要TTL防止程序异常。一致性)(加锁是查询数据库的时候加锁,而且这个key最好是加适当的时间呀,如果说他是在1天内秒杀,然后提前一天上架,那么久TTL设置成2天呗?);逻辑过期(不用TTl自己维护一个expire,也要获锁新开线程。可用性,无需等待,但是期间拿到的是旧数据)

    HyperLogLog做UV统计

    pipeline批量导入数据,pipeline的多个命令之间不具备原子性,mset和pipeline这样的批处理,如果是在集群中,那么批处理命令的多个key必须落在一个插槽中。

    持久化

    RDB和AOF

    主从和哨兵(高可用、高并发读)

    IMG_0301A08F2931-1.jpg

    IMG_5AD953718C41-1.jpg

    IMG_F7B6AAA32E9D-1.jpg

    IMG_2015.jpg

    分片(海量数据存储、高并发写)

    散列插槽,数据key与插槽绑定(16384个插槽分配到不同的实例,根据有小部分计算哈希值,取余)

    多级缓存

    OpenResty
    Redis缓存预热

    缓存同步

    数据库的主从同步原理

    IMG_09705D2FEE35-1.jpg

    设置有效期外失效,同步双写强一致性(修改完数据库修改缓存形成事务),异步通知

    异步通知,第一种是基于==MQ==的异步通知,需要手写发布监听,然后更新;第二种是canal,把自己伪装成一个MYSQL的slave节点,从而坚挺binary log的变化,再把得到的变化信息通知给canal的客户端,进而完成对其他数据库的同步。所以要用canal,要先实现mysql的主从。

    IMG_33E435DAFCBC-1.jpg

    AOP:事务、动态代理

    SDS动态字符串

    redis没有直接使用c的字符串(获取长度需要遍历,非二进制安全,不可修改)

    SDS中有buf[],一保存字符串长度,申请总字节数,不同头类型大小,可以动态扩容

    IntSet

    唯一、长度可变、有序、二分查找来查询

    Dict

    dict由三部分组成:哈希表、哈希节点、字典。一个dict中有两个哈希表bictht,一个用于存储,一个用于扩容再哈希使用。哈希表中是数组结合单向链表实现解决hash冲突,类似java的hashtable,当个数到达一定的时候,会扩容收缩(伴随再hash)。

    非连续,碎片多

    ZipList

    一个特殊的“双端链表”,由一系列特殊编码的连续内存组成,用1字节或者5字节保存前一节点的长度而不是用指针记录前后节点。

    QuickList

    一个双端链表,链表中的每一个节点都是一个ziplist。(LinkedList+ZipList)使用的ziplist,解决了传统链表的内存占用问题(碎片),控制每一个zipilst大小,解决了连续内存空间申请的效率问题,中间节点可以压缩进一步节省内存。

    SkipList跳表

    链表,升序排序,节点包含多个后指针,跨度不同,前驱指针只有一个

    RedisObject

    Redis的任意数据类型的键和值都会被封装为一个RedisObject,也叫Redis对象。

    五种数据结构是如何的

    IMG_2026.jpg

    网络模型

    IMG_2047.jpg

    为什么redis单线程

    IMG_2056.jpg

    内存回收

    IMG_2060.jpg

    有关发布订阅

    首先要知道,redis也是可以做到消息队列的发布订阅的,一般的发布订阅是直接消息队列rabbitmq或者Kafka这些。

    发布订阅:发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

    IMG_774E6EEFDCFA-1

    IMG_0938744922C1-1

    redis: 轻量级,低延迟,高并发,低可靠性;
    rabbitmq:重量级,高可靠,异步,不保证实时;
    rabbitmq是一个专门的AMQP协议队列,他的优势就在于提供可靠的队列服务,并且可做到异步,而redis主要是用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。

    Feed流

    IMG_AA378D9760E3-1

    IMG_FB0211CF5CF3-1

    实践:本项目中使用推

    作者写好后,查找到他的所有粉丝,然后给所有的粉丝对应的key中增加这篇文章,当然此处使用了zset,根据文章发布时间排序。

    当然如果作者删除什么的需要处理。(我觉得可以懒处理,粉丝要看的时候从redis里面取出来然后比较存不存在,不存在的话redis删除,但是这样的话多次查询数据库还是累的)。

    jIjxBw

    Redis锁从面试连环炮聊到神仙打架。

    1. Redis 做分布式锁的时候有需要注意的问题?
    2. 如果是 Redis 是单点部署的,会带来什么问题?
    3. 那你准备怎么解决单点问题呢?
    4. 集群模式下,比如主从模式,有没有什么问题呢?
    5. 你知道 Redis 是怎么解决集群模式也不靠谱的问题的吗?
    6. 那你简单的介绍一下 Redlock 吧?
    7. 你觉得 Redlock 有什么问题呢?

    Kafka中,consumer 是推还是拉?

    customer 应该从 brokes 拉取消息还是 brokers 将消息推送到 consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消 息推送到 broker,consumer 从 broker 拉取消息

    push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定 消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的 速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了 传统的 pull 模式。

    在Java中发送kafka消息

    1
    producer.send(new ProducerRecord<>("my-replicated-topic","key"+i,"value"+i));

    Kafka集群消息积压问题和数据倾斜问题及处理策略

    Kafka消息积压的典型场景:

    1. 实时/消费任务挂掉

    比如,我们写的实时应用因为某种原因挂掉了,并且这个任务没有被监控程序监控发现通知相关负责人,负责人又没有写自动拉起任务的脚本进行重启。

    那么在我们重新启动这个实时应用进行消费之前,这段时间的消息就会被滞后处理,如果数据量很大,可就不是简单重启应用直接消费就能解决的。

    1. Kafka分区数设置的不合理(太少)和消费者”消费能力”不足

    Kafka单分区生产消息的速度qps通常很高,如果消费者因为某些原因(比如受业务逻辑复杂度影响,消费时间会有所不同),就会出现消费滞后的情况。

    此外,Kafka分区数是Kafka并行度调优的最小单元,如果Kafka分区数设置的太少,会影响Kafka consumer消费的吞吐量。

    1. Kafka消息的key不均匀,导致分区间数据不均衡

    在使用Kafka producer消息时,可以为消息指定key,但是要求key要均匀,否则会出现Kafka分区间数据不均衡。

    那么,针对上述的情况,有什么好的办法处理数据积压呢?

    一般情况下,针对性的解决办法有以下几种:

    1. 实时/消费任务挂掉导致的消费滞后

    a. 任务重新启动后直接消费最新的消息,对于”滞后”的历史数据采用离线程序进行”补漏”。

    此外,建议将任务纳入监控体系,当任务出现问题时,及时通知相关负责人处理。当然任务重启脚本也是要有的,还要求实时框架异常处理能力要强,避免数据不规范导致的不能重新拉起任务。

    b. 任务启动从上次提交offset处开始消费处理

    如果积压的数据量很大,需要增加任务的处理能力,比如增加资源,让任务能尽可能的快速消费处理,并赶上消费最新的消息

    1. Kafka分区少了

    如果数据量很大,合理的增加Kafka分区数是关键。如果利用的是Spark流和Kafka direct approach方式,也可以对KafkaRDD进行repartition重分区,增加并行度处理。

    1. 由于Kafka消息key设置的不合理,导致分区数据不均衡

    可以在Kafka producer处,给key加随机后缀,使其均衡。

    一种解决消费海量Kafka Topic时数据倾斜的方案

    为什么Redis的操作是原子性的,怎么保证原子性的?

    为什么Redis的操作是原子性的,怎么保证原子性的?

    对于Redis而言,命令的原子性指的是:
    一个操作的不可以再分,操作要么执行,要么不执行。

    Redis的操作之所以是原子性的,是因为Redis是单线程的。

    Redis本身提供的所有API都是原子操作,
    Redis中的事务其实是要保证批量操作的原子性。

    多个命令在并发中也是原子性的吗?

    不一定, 将get和set改成单命令操作,incr 。
    使用Redis的事务,或者使用Redis+Lua==的方式实现

    数据结构——Redis中的bitmap

    redis中bit映射被限制在512MB之内,所以最大是2^32位。建议每个key的位数都控制下,因为读取时候时间复杂度O(n),越大的串读的时间花销越多。

    Author: Jcwang

    Permalink: http://example.com/2022/07/15/%E5%85%AB%E8%82%A1redis%E5%9F%BA%E7%A1%80/