Redis 进阶

962 阅读11分钟

redis 核心

数据类型

类型存储的值读写能力
String字符串,整数,浮点数,二进制1.对整个字符串或字符串中的一部分执行操作
2.对整数或浮点数进行自增或自减
List链表上每一个节点都包含了一个字符串1.从链表两端进出元素
2.对链表进行修剪
3.读取单个或多个元素
4.根据值查找或移除元素
Set字符串的无序集合,且唯一1.添加,获取,删除,查找元素
2.交集,并集,差集
3.随机获取元素
Hash键值对无序散列集1.添加,获取,删除单个键值对
2.获取所有键值对
ZSet字符串和浮点数之间的有序映射1.添加,获取,删除单个元素

内存淘汰策略

  • volatile-lru:从已经设置过期时间的数据集中,挑选最近最少使用的数据淘汰
  • volatile-ttl:从已经设置过期时间的数据集中,挑选即将要过期的数据淘汰
  • volatile-random:从已经设置过期时间的数据集中,随机挑选数据淘汰
  • allkeys-lru:从所有的数据集中,挑选最近最少使用的数据淘汰
  • allkeys-random:从所有的数据集中,随机挑选数据淘汰
  • no-enviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键

过期策略

redis 为什么会有一个内存淘汰策略呢?

因为redis使用的过期策略是 定期删除 + 惰性删除

定期删除

redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。从过期字典中随机 20 个 key;删除这 20 个 key 中已经过期的 key; 如果过期的 key 比率超过 1/4,那就重复步骤 1; redis默认是每隔 100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!

总结:定时随机的去删除过期的20个key

惰性删除

所谓惰性策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。

定期删除可能会导致很多过期key到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,即当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西.

定期删除是集中处理,惰性删除是零散处理。

为什么要采用定期删除+惰性删除2种策略呢?

如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了.

但是问题是,定期删除可能会导致很多过期key到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。

并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下

通过上述两种手段结合起来,保证过期的key一定会被干掉。

所以说用了上述2种策略后,上面说的内存淘汰机制这种现象就不难解释了:数据明明都过期了,但是还占有着内存

持久化

什么是持久化?

持久化(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。 持久化Redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。

redis 持久化方式?

  • RDB---数据快照(默认)
  • AOF---日志追加

RDB

RDB 持久化原理?

通过bgsave命令触发,然后父进程执行fork操作创建子进程,子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换(定时一次性将所有数据进行快照成一份副本存储在硬盘中)

RDB 优缺点?

优点: 是一个紧凑压缩的二进制文件,非常适合备份和灾难恢复,Redis加载RDB恢复数据远远快于AOF的方式。

缺点: 由于每次生成RDB开销较大,非实时持久化,当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

RDB 配置:
save 900 1              在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10             在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000           在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。

rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,
但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

dbfilename :设置快照的文件名,默认是 dump.rdb

dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名

关闭RDB:
    1. redis-cli命令:config set save ""
    2. 注释掉所有的 save  并开启 save ""

AOF

AOF 持久化原理?

开启后,Redis每执行一个修改数据的命令,都会把这个命令添加到AOF文件中。

AOF 优缺点?

优点: 实时持久化, AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写

缺点: 所以AOF文件体积逐渐变大,需要定期执行重写操作来降低文件体积,加载慢

AOF 配置:
appendfsync always       每次有数据修改发生时都会写入AOF文件。

appendfsync everysec     每秒钟同步一次,该策略为AOF的缺省策略。

appendfsync no           从不同步。高效但是数据不会被持久化。

appendonly yes|no	    开启 关闭

appendfilename xxx	    文件名

RDB 和 AOF 比较

命令RDBAOF
启动优先级
体积
恢复速度
数据安全性丢数据根据策略决定
轻重
占用存储空间
存储速度
资源消耗

缓存问题

认识

缓存穿透: key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

缓存击穿: key对应的数据源存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

缓存雪崩: 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库

解决方案:

缓存穿透:

  • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力.
  • 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
  • 不管数据是否为空都进行缓存,为空可以设置很短的过期时间。

缓存击穿:

  • 设置热点数据永远不过期,或者定时去更新热点数据
  • 加锁,例如:使用互斥锁(mutex key)

缓存雪崩:

  • reids 高可用 ;
  • 通过加锁或者队列来限流并降级以达到降低DB压力(隔离组件)
  • 数据预热,或热点数据永不过期
  • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

redis 高级使用

  • 分布式锁
  • 接口限流
  • 共享session
  • 接口幂等性
  • 事务
  • 订阅发布

redis 集群

主从模式

主从复制原理:

  • 从服务器连接主服务器,发送SYNC命令; 
  • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 
  • 主服务器bgsave执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令; 
  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照; 
  • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令; 
  • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)
  • 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)

主从复制优缺点:

优点:
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
  • 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成
  • Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
  • Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
  • Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
缺点:
  • Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

哨兵模式

哨兵的主要功能:

  • 监控所有节点数据库是否在正常运行
  • master数据库出现故障时,可以自动通过投票机制,从slave节点中选举新的master,实现将从数据库转换为主数据库的自动切换。

哨兵模式的工作方式:

  • 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)
  • 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
  • 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)
  • 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
  • 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
  • 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。

哨兵模式的优缺点:

优点:
  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  • 主从可以自动切换,系统更健壮,可用性更高。
缺点:
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂
  • 当主服务器宕机后,从服务器切换成主服务器的那段时间,服务是不能用的。

cluster 模式

redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。

Redis-Cluster采用无中心结构,它的特点如下:

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

Cluster 模式的工作方式:

在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。

redis 哨兵模式实战

  1. 在从库的redis.windows.conf文件中配置slaveof 127.0.0.1 6380(主库的ip+port),如果主库有密码则配置masterauth ***

  2. 每一个redis目录中新建一份sentinel.conf 文件(port 不一致,其它保持一致,如果有该文件则不需要新建),内容为:

  port 27001
  sentinel monitor mymaster 127.0.0.1 7001 2
  sentinel down-after-milliseconds mymaster 5000
  sentinel config-epoch mymaster 14
  sentinel leader-epoch mymaster 2
  
    参数说明:
  (1)sentinel monitor <master-name> <ip> <redis-port> <quorum>
   master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
   quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
  例:sentinel monitor mymaster 127.0.0.1 6379 2
  (2) sentinel down-after-milliseconds mymaster 30000
  指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
  (3) sentinel config-epoch mymaster 14
  执行故障转移时,从节点需要等待的时间
  (4) sentinel leader-epoch mymaster 2 
  同时一时间最多18个slave可同时更新配置,
  (5) sentinel auth-pass mymaster  123456
  设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
  1. 启动脚本(redis 和 哨兵sentinel.conf都要启动) 编写bat启动脚本:
Redis :
  cd 7001
  title redis-7001
  redis-server.exe redis.windows.conf
sentinel:
  cd 7001
  title sentinel-7001
  redis-server.exe sentinel.conf –sentinel
  1. 全部启动然后开始测试
  2. 查看结果
查看redis状态: 
cmd 到redis目录 -> redis-cli.exe -p 7001 如有密码,则 auth 密码 -> info replication 查看

查看哨兵sentinel状态:
cmd 到redis目录 ->redis-cli.exe -p 哨兵配置端口 -> 输入info sentinel
  1. 可以关闭主库测试一下从库是否自动升成主库
  2. Springboot 连接哨兵模式,其他操作保持一致
spring:
  redis:
    sentinel:
    	master: mymaster  # 主节点名称
        nodes: localhost:27001,localhost:27002,localhost:27003  # 哨兵ip+port