Loading

Redis知识点分析

Redis

前言

之前一直想要去自己总结一下redis的用法以及面试的高频问题
如下是一些基础的redis的常见面试题

image
image

redis为什么这么快?

# 1、redis是基于内存处理,处理速度大大的加强。
# 2、redis是采用单线程模型:在处理读写指令的时候使用单线程,减少了创建多个线程时的开销,减少了上下文切换时对内存的开销,避免了多线程时会产生的数据和锁的问题。
# 3、redis采用I/O多路复用模型:网络I/O或者本地I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。即多个tcp连接一次性注册到到复用器上,将其放入到阻塞队列中,最后通过单线程进行轮询处理达到就绪状态的事件。
# 4、高效的数据结构:String、List、Hash、Set、SortedSet。

redis 6.0的“多线程”?

# 1、redis6.0的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。

1、redis的数据类型

  • String(字符串)
# 常见指令
set k1 mainwod
get k1
del k1 k2 (删除key)
setrange k1 4 renxu (替换下标4-最后的值)
getrange k1 0 5 (获取k1的1-6个字符)
setnx k1 renxu (key不存在时可以设置值)
setex k1 10 mainwoods (对key设置失效时长/秒)
psetex k1 1000 renxu (同上,毫秒)
strlen k1 (获取k1的长度)
mset k1 mainwood k2 minwoods (批量设置key值)
msetnx k1 renxu k3 xiaowang (批量设置key值,在key都不存在的时候才能成功)
mget k1 k2 (批量获取多个key)
incr k1 (加一)
decr k1 (减一)
incrby k1 5 (加指定整数)
decrby k1 5 (减指定整数)
incrbyfloat k1 0.6 (加指定小数)
append k1 renxu (追加指定值)
  • Hash(哈希)
# 常见指令
hset k1 name renxu title mainwood (给k1设置name和title给定的值)
hmset k1 name renxu title mainwood (同上)
hget k1 name (获取key中的单个属性的值)
hmget k1 name age (获取key中多个属性的值)
hgetall k1 (获取key中的所有属性和值)
hdel k1 name (删除key中的某个属性)
hexists k1 name (判断key中某个属性是否存在)
hkeys k1 (获取key的所有属性)
hvals k1 (获取key的所有属性的值)
hsetnx k1 name xx (设置key中属性的值/不存在时)

  • List(列表)
# 常见指令
lpush k1 ww xx rr (从左插入在列表头部)
rpush k1 zz (从右插入在列表尾部)
lpushx k1 cc (从左插入列表头部,但是key要存在)
rpushx k1 dd(从右插入列表尾部,但是key要存在)
lpop k1(移除第一个元素)
rpop k1(移除最后一个元素)
lange k1 0 10 (获取key中指定数量的值)
llen k1 (获取key长度)

  • Set(集合)
# 指令
sadd k1 ww ss xx dd (向集合添加数据)
scard k1 (获取集合长度)
sdiff k1 k2 (返回第一个集合与其他集合之间的差异。)
sdiffstore k3 k1 k2 (返回给定所有集合的交集并存储在 k3 中)
smembers k3 (查看集合成员)
sismember k1 ss (判断集合中是否存在某元素)
spop k1 (随机删除一个元素)
srem k1 ss 1 (删除具体元素)

  • Sort Set(有序集合)
# 常见指令
zadd k1 1 ww 2 xx 3 cc 4 rr (添加元素与排序)
zcard k1 (查看集合数量)
zcount k1 2 3 (查看指定分数区间的元素数量)
zincrby k1 2 ww (有序集合中对指定成员的分数加上增量2)
zrange k1 0 -1 (查看指定下表的元素)
zrem k1 cc ww (删除指定元素)
zrank k1 rr (查看元素下标)
zscore k1 rr (查看指定元素的分数)
zremrangebyrank k1 2 4 (删除指定下表区间内元素)
zremrangebyscore k1 2 4 (删除指定分数的区间内的元素)

  • 各种类型应用场景

大厂数据类型使用
• String:缓存、限流、计数器、分布式锁、分布式Session
• Hash:存储用户信息、用户主页访问量、组合查询
• List:微博关注人时间轴列表、简单队列
• Set:赞、踩、标签、好友关系
• Zset:排行榜

类型 应用场景
String 平时一些简单的说数据格式,如:缓存、访问量统计等一些系统中静态数据等。
Hash 存储一些对象型信息,如:存储用户信息等。
List 存储一些列表型信息,如发布的文章列表等。
Set 存储一些交集,差集,合集等一系列列表数据,如:共同好友,共同点赞文章等。
SortSet 存储一些具有排序字段的列表,如:点赞文章排行榜等。

2、redis持久化

两种持久化方式:

​ RDB,AOF两种方式,默认以RDB方式记进行持久化

  • RDB

    RDB:将数据以二进制文件快照的形式存到磁盘上。
    两种触发机制:
    	save:手动触发机制,通过save指令来触发,是线程阻塞的。
    	bgsave:执行bgsave命令会派生出一个子进程(而不是线程),由子进程进行RDB文件创建,而父进程继续处理命令。
    	注意:
    		1、在bgsave命令执行的时候,为了避免父进程与子进程同时执行两个rdbSave的调用而产生竞争条件,客户端发送的save命令会被服务器拒绝。
    		2、如果bgsave命令正在执行,bgrewriteaof(aof重写)命令会被延迟到bgsave命令之后执行,如果bgrewriteaof命令正在执行,那么客户端发送的bgsave命令会被服务器拒绝。
    		3、虽然bgsave命令是由子进程进行RDB文件的生成,但是fork()创建子进程的时候会阻塞父进程
    

image

  • 1、触发配置

    # 1、文件配置
    save 900 1  #服务器在900秒内对数据库进行了至少1次修改
    save 300 10 #服务器在300秒内对数据库进行了至少10次修改
    save 60  10000 #服务器在60秒内对数据库进行了至少10000次修改
    
  • 2、文件载入

    1、在Redis启动的时候,只要检测到RDB文件的存在,就会自动加载RDB文件,如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态.
    2、只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态
    
  • 3、内存快照问题

    1、在生成快照的时候进行数据的修改:fork+cow(copy-on-write)
    	Redis通过Copy-On-Write技术,即,主进程进行bgsave的时候,会fork一个子进程,此时与主进程共享一个物理内存,子进程运行后,开始读取主进程内存中的数据,将其写入RDB文件中,若主进程只进行读操作则无影响;当主进程进行修改操作时,此时主进程会为子进程分配一块内存空间去复制存放主进程修改的数据,最后子进程再将这块内存空间里的数据写入到RDB中。
    2、频繁进行快照的操作:
    	通过AOF持久化操作
    	
    
  • AOF

    通过记录Redis服务器操作的写指令来记录数据库的状态,即每执行操作指令,就会将该指令存入日志文件中。
    注意:
      1、AOF日志是主进程进行操作,当指令过多或过长,可能会产生阻塞导致速度变慢
      2、不能直接将Redis作为数据库来使用:当命令执行完时,日志还未写入时突然宕机,此时数据是无法恢复的
    

image

  • 1、AOF缓冲区

    当Redis服务器进行持久化的时候,先将日志记录存放在AOF缓冲区,在通过具体的会写策略写入到磁盘中。
    三种会写策略:
    	Always(同步写回): Redis服务器执行完命令后,同步将日志文件写入到磁盘中
    	Everysec(每秒写回): Redis服务器执行完命令后,同步将日志文件写入到磁盘中,同步文件操作由专门线程每秒调用一次
    	No(操作系统自动写回): 命令写入到AOF缓冲区后调用系统write操作,不对AOF文件做同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒
    	优缺点
    	Always(同步写回)    	可靠性高、数据基本不丢失   	 		性能较差
    	Everysec(每秒写回)		性能适中						 宕机时丢失1秒内的数据
    	No(操作系统自动写回)	  性能好						   宕机时丢失数据较多
    
  • 2、AOF重写机制

image

1、AOF重写机制:当对同一个key进行多个修改操作的时候,最终只会保存最新的一天修改记录。
2、过程:Redis服务器进行持久化时,先将日志记录存放在AOF缓存区中;此时AOF执行重写,会fork出一个子进程进行AOF重写,若此时主进程继续接收客户端命令调用的话,就会将新指令放入到AOF重写缓冲区中,当子进程AOF重写结束后会给主进程发送信号,主进程会调用信号回调函数,将重写缓冲区的指令追加到AOF文件中,最后将原来的AOF文件替换掉,实现数据的一致。
  • 3、AOF文件的恢复:Redis启动时会记载AOF文件中的指令

  • 如何进行持久化的选择

1、如果你的业务场景需要很高的性能,或者宕机之后能够尽快的恢复,而对数据完整性的要求不是那么高,那么可以采用RDB持久化的方式。
2、如果你的业务场景对数据完整性的要求很高,那么可以采用AOF的持久化方式,而至于采用那种回写策略,则取决于你对数据完整性的要求程度。
3、如果你的业务场景既要兼顾性能,又注重数据完整性,那么可以采用混合持久化的方式。
4、如果你对数据丢失无所谓,追求性能最大化的情况下,甚至可以禁用持久化。

3、redis过期键删除策略

  • 定时删除(主动删除)

    在建设置过期时间的同时,创建一个定时器,在键过期的时候执行定时器去删除过期键
    
  • 惰性删除(被动删除)

    放任过期键不管,当获取键时,系统会判断键是否过期,若过期则删除,未过期则返回
    
  • 定期删除(主动删除)

    固定时间段程序对数据进行一次检查,删除过期键
    

    1、定时删除策略

    定时删除策略通过使用定时器,定时删除策略可以保证过期键尽可能快地被删除,并释放过期键占用的内存,若创建了大量的定时器来删除过期键,会造成占用大量的CPU。

    • 优点:对内存非常友好
    • 缺点:对CPU时间非常不友好

    2、惰性删除策略

    惰性删除策略只会在获取键时才对键进行过期检查,不会在删除其它无关的过期键花费过多的CPU时间,但是会一直存放在内存中,造成内存的浪费。

    • 优点:对CPU时间非常友好
    • 缺点:对内存非常不友好

    image

    3、定期删除策略

    定期删除策略是定时删除策略和惰性删除策略的一种整合折中方案。

    定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,同时,通过定期删除过期键,也有效地减少了因为过期键而带来的内存浪费

    当调用activeExpireCycle函数去执行定期删除策略:

    ​ 都从一定数量的数据库中随机取出一定数量的键进行检查,并删除其中的过期键,比如先从0号数据库开始检查,下次函数运行时,可能就是从1号数据库开始检查,直到15号数据库检查完毕,又重新从0号数据库开始检查,这样可以保证每个数据库都被检查到。

    具体删除键算法为LRU算法(Least Recently Used 最近最少使用)

    RDB

    1、生成RDB文件时,程序对数据库键检查,过期则不添加
    2、载入RDB时,
    	主库:未过期的键进行加载,过期键忽略
    	从库:加载所有键
    

    AOF

    主库:程序会对数据库中的键进行检查,过期键不进行写入文件,发送del删除指令给从库
    从库:从库接收del指令会对过期键进行删除
    

4、redis内存

image

  • 自身内存:主进程和子进程占用内存
  • 对象内存:存放数据所占内存
  • 缓存区内存:
    • 客户端缓冲区:输入缓冲区会先把客户端发送过来的命令暂存起来,Redis 主线程再从输入缓冲区中读取命令,进行处理。当在处理完数据后,会把结果写入到输出缓冲区,再通过输出缓冲区返回给客户端。
    • AOF缓冲区:包括AOF缓冲区、AOF重写缓冲区
    • 复制积压缓冲区:主向从传输RDB时,存放新写入的指令,完成RDB后继续执行指令
  • 内存碎片:内存碎片主要是由于操作系统的内存分配机制和Redis内存分配器的分配策略所决定的
  • 子进程消耗内存:子进程持久化的过程中耗费的内存

5、redis线程模型

文件事件处理器
# 结构
## 1、多个套接字
## 2、IO多路复用
## 3、文件事件分派器(用于将套接字与对应的事件处理器进行关联)
## 4、事件处理器(命令请求处理器,命令回复处理器,连接应答处理器等等)

Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器执行队列的指令是单线程的,所以Redis才叫单线程模型。

  • 文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。

6、redis事务

事务概述(ACID)

  • 原子性:要么都成功要么都失败
  • 一致性:事务前后数据完整性保持一致
  • 隔离性:多事务之间互不干扰
  • 持久性:事务提交后不支持回滚
6-1、Redis事务
WATCH 监听
1、MULTI 开启事务
2、指令入队
3、EXEC 执行事务
DISCARD 撤销
6-2、Redis事务常见问题
1、Redis事务不是原子性,且不支持回滚,事务中任意命令执行失败,其余的命令仍会被执行。
2、Redis事务是隔离性的
3、如何保证事务的原子性?
	1、基于Lua脚本,Redis可以保证脚本内的命令一次性、按顺序地执行,其同时也不提供事务运行错误的回滚,执行过程中如果部分命令运行错误,剩下的命令还是会继续运行完
	2、基于中间标记变量,通过另外的标记变量来标识事务是否执行完成,读取数据时先读取该标记变量判断是否事务执行完成。但这样会需要额外写代码实现,比较繁琐

7、redis集群方案

  • 主从复制模式
  • 哨兵模式
  • Cluster模式
7-1、主从复制模式

主从复制模式包含1个主数据库(master)+一个或多个从数据库(slave),客户端通过对主服务器进行写操作,通过从服务器实现读操作。

image

原理:

1、从节点上线后,发送SYNC指令给主节点,此时主节点执行bgsave开始生成RDB文件实现持久化,生成RDB文件之后开始同步给从节点(初次进行全量复制),此时从节点先将原始数据清掉,此时主节点会为每个从节点开辟一块复制缓冲区,同步过程中再接收到的指令就会存放在复制缓冲区,当完成同步后,主数据库会将复制缓存区里的指令写入到RDB文件中来保证主从数据一致性
2、当进行后面增量数据同步时,在同步的过程中,或者主从数据库连接断掉时,主节点会将接收到的指令存放到复制积压缓冲区中,同步完成后或者连接后加载复制积压缓冲区的指令。

优缺点:

优点:
	1、实现了读写分离
	2、主从之间同步时非阻塞
缺点:
	1、不具备自动容错与恢复功能,要手动恢复
	2、难以在线扩容,容量受单机配置局限
	3、主数据库宕机,数据未同步完,切换IP会造成数据不一致
7-2、哨兵模式(哨兵+主从复制)

原理:

是主从复制模式的升级版,增加了哨兵对主从复制的集群监控、消息通知、故障转移、配置中心
  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

image

7-3、Cluster 方案(集群)
分片集群:目前用的比较少
原理:主要是针对海量的数据的分布式解决方案。
	redis cluster是Redis的分布式解决方案,在3.0版本推出后有效地解决了redis分布式方面的需求
  自动将数据进行分片,每个master上放一部分数据
  提供内置的高可用支持,部分master不可用时,还是可以继续工作的
  支撑N个redis master node,每个master node都可以挂载多个slave node
  高可用,因为每个master都有salve节点,那么如果mater挂掉,redis cluster这套机制,就会自动将某个		slave切换成master

8、redis分布式问题

9、redis缓存异常

  • 缓存雪崩

    产生原因:
    	指某一时刻,突然缓存大面积的失效,导致大量的请求直接访问到数据库上,而导致的数据库崩溃。
    解决方案:
    	1、将过期时间设置为随机,防止同一时间大量的缓存失效
    	2、当并发量不是太大的时候可以加锁排队
    	3、给缓存添加相应的缓存标记,记录缓存是否失效,及时的去更新缓存
    
  • 缓存穿透

    产生原因:
    	指缓存和数据库中都没有该数据,一时间接收大量的请求导致数据库的崩溃。
    解决方案:
    	1、接口层添加校验,对没有的数据进行拦截
    	2、在缓存层添加key-null键值对,设置一定的过期时间,防止暴力攻击
    	3、使用布隆拦截器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
    
  • 缓存击穿

    产生原因:
    	指某一时刻,某条缓存过期失效,正好又有大量的请求,此时缓存中无该数据就会访问数据库,大量的请求会导致数据库的崩溃。
    解决方案:
    	1、设置热点键永不失效
    	2、加互斥锁
    

    缓存雪崩、缓存穿透、缓存击穿三者区分

    雪崩:是大量的缓存同时失效,大量请求打到数据库上

    击穿:某一条缓存失效,大量请求打到数据库上

    穿透:缓存和数据库中不存在数据,恶意攻击导致数据库的崩溃

  • 缓存预热

    缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
    
    如何实现:
    	1、增加缓存刷新页面,上线手工操作
    	2、数据不大时,可以在项目启动时进行加载
    	3、定时刷新缓存
    
  • 缓存降级

    当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
    缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
    在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
    一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
    警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
    错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
    严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
    
    服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
    
  • 热点数据和冷数据

    热点数据,缓存才有价值
    
    对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存
    
    对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
    
    数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
    
    那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
    
  • 缓存热点key

    造成原因:
        缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
    解决方案:
    	对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询
    

10、redis分区

11、redis其他问题

posted @ 2021-07-09 20:56  mainwoodの慢屋  阅读(48)  评论(0编辑  收藏  举报