今日任务: 解决以下问题
1、事务本质
2、持久化方案,原理,有什么问题
3、两种持久化机制对比
4、高可用-主从复制的原理
5、主从存在问题
6、哨兵模式原理
7、经典主从集群解决了什么问题,还存在什么问题
8、高可扩-分片集群的原理
9、异常测试
10、如何实现redis动态的扩缩容
11、redis5种数据类型的应用场景

* 项目中如何使用的?//TODO

1、事务本质

1.1事务依靠队列实现

Redis事务是基于队列实现的,创建一个事务队列,然后将事务操作都放入队列中,最后依次执行。
image.png

1.2 redis事务弱事务,对于错误时的处方式

当存在运行时出错的指令时,直接全部不执行
存在一条正确指令,两条语法错误指令, 当执行exec后,会直接返回错误,正确的命令也不会执行。
执行错误:命令在运行过程中出现错误
通过上面事务执行可以看到,语法本身是没有问题的,所以运行之前redis无法发现错误,但是在执行时出现了错误,因此只会错误的命令不执行, 而正确的命令仍然能够正常执行
原因:redis是弱的一致性,如果需要强的事务,可以使用mysql

1.3springBoot开启事务,spring的事务Transactional对redis的事务毫无关系

image.png
spring的事务Transactional对redis的事务毫无关系,因为redis事务只是在队列中实现的事务而保持的一致性

2、持久化方案RDB,AOF原理,有什么问题

2.1 RDB:

2.1.1 RDB的原理

原理:
Redis将数据保存在内存中。一旦服务器宕机重启,内存中的数据就会丢失。当出现这种情况后,为了能够让Redis进行数据恢复,因此Redis提供了持久化机制,将内存中的数据保存到磁盘中,避免数据意外丢失。

2.1.2 RDB快照存储方式

RDB:RDB快照
默认的存储方式
RDB(Redis DataBase)是Redis默认存储方式。其基于快照思想,当符合一定条件(手动或自动触发)时,Redis会将这一刻的内存数据进行快照并保存在磁盘上,产生一个经过压缩的二进制文件,文件后缀名.rdb
image.png
因为RDB文件是保存在磁盘上的,因此即使Redis进程退出,甚至服务器宕机重启。只要RDB文件存在,就可以利用它来还原Redis数据。

2.1.3 RDB执行的save(必须等待执行完毕)跟bsave方式(堵塞队列)

在redis客户端执行save或bgsave命令,手动触发RDB快照。

  • save:同步处理,阻塞Redis服务进程,服务器不会处理任何命令,直到RDB文件保存完毕。
  • bgsave:会fork一个和主线程一致的子线程负责操作RDB文件,不会阻塞Redis服务进程,操作RDB文件的同时仍然可以处理命令。、

如图:
image.png
具体的步骤:
1)Redis服务进程判断,当前是否有子线程在执行save或bgsave。
2)如果有,则直接返回,不做任何处理。
3)如果没有,则以阻塞式创建子线程,在创建子线程期间,Redis不处理任何命令。
4)创建完子线程后,取消阻塞,Redis服务继续响应其他命令。
5)同时基于子线程操作RDB文件,将此刻数据保存到磁盘。

2.1.3RDB的优缺点:

优点:

  • 基于二进制文件完成数据备份,占用空间少,便于文件传输。
  • 能够自定义规则,根据Redis繁忙状态进行数据备份。

缺点:

  • 无法保证数据完整性,会丢失最后一次快照后的所有数据。
  • bgsave执行每次执行都会阻塞Redis服务进程创建子线程,频繁执行影响系统吞吐率。

2.1.3

image.png

2.2 AOF:

2.2.1 AOF的原理

AOF(append only file)是Redis提供了另外一种持久化机制。与RDB记录数据不同,当开启AOF持久化后,Redis会将客户端发送的所有更改数据的命令,记录到磁盘中的AOF文件。 这样的话,当Redis重启后,通过读取AOF文件,按顺序获取到记录的数据修改命令,即可完成数据恢复。

image.png2.2.2 AOF需要手动开启:默认是不开启的

image.png

2.2.3 AOF的触发方式有三种

对于AOF的触发方式有三种:alwayseverysecno默认使用everysec。可以通过redis.conf中appendfsync属性进行配置。
image.png
通过文件查看,可以看到,在aof文件中,记录了对redis操作的所有写命令,读命令并不会记录

2.3.4 执行原理(图解)

image.png
1)客户端向Redis发送写命令。
2)Redis将接收到的写命令保存到缓冲文件aof_buf的末尾这个过程是命令追加。
3)redis将缓冲区文件内容写入到AOF文件,这个过程是文件写入
4)redis根据策略将AOF文件保存到磁盘,这个过程是文件同步。
5)何时将AOF文件同步到磁盘的策略依据就是redis.conf文件中appendfsync属性值:alwayseverysecno

2.3.5 always everysec no三种方式的理解与优缺点

  • always:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中,并将AOF文件同步到磁盘。该方式效率最低,安全性最高。
  • everysec:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中。 并且每隔一秒会由子线程将AOF文件同步到磁盘。该方式兼备了效率与安全,即使出现宕机重启,也只会丢失不超过两秒的数据。
  • no:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中,但并不对AOF文件进行同步磁盘。 同步操作交由操作系统完成(每30秒一次),该方式最快,但最不安全。

image.png
always最多丢失一个命令,但是效率比较低,大量的从AOF文件同步到硬盘
一般使用会选择everysec方式,最多丢失2秒的命令

2.3 AOF的重写优化

2.3.1 AOF为什么要进行重写优化

AOF会将对Redis操作的所有写命令都记录下来,随着服务器的运行,AOF文件内保存的内容会越来越多。这样就会造成两个比较严重的问题:占用大量存储空间数据还原花费的时间多

2.3.2 如何解决AOF文件内容多的问题

为了解决AOF文件巨大的问题,Redis提供了AOF文件重写功能。 当AOF文件体积超过阈值时,则会触发AOF文件重写,Redis会开启子线程创建一个新的AOF文件替代现有AOF文件。 新的AOF文件不会包含任何浪费空间的冗余命令,只存在恢复当前Redis状态的最小命令集合。
重写前后对比:(变成二进制文件)
image.png

3、两种持久化机制对比*

  1. RDB默认开启,AOF需手动开启。
  2. RDB性能优于AOF。
  3. AOF安全性优于RDB。
  4. AOF优先级高于RDB(混合模式)。
  5. RDB存储某个时刻的数据快照,AOF存储命令。
  6. RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF默认使用everysec,每秒保存一次,最多丢失两秒以内的数据、

    4. 生产环境下持久化实践(RDB,AOF的选择)

  7. 如当前只追求高性能,不关注数据安全性,则关闭RDB和AOF,如redis宕机重启,直接从数据源恢复数据。

  8. 如需较高性能且关注数据安全性,则开启RDB,并定制触发规则。当开启RDB后发现,Redis数据量过多,服务线程被频繁阻塞,造成系统性能严重下降,则开启AOF。
  9. 如更关注数据安全性,则开启AOF。

image.png
image.png

5 key过期删除策略

看到过期删除策略,大家现在脑袋里面可能会想为什么要学这个? 这有什么好说的,之前不是已经学过EXPIRE命令了, 给key设置过期时间,到期了不就删除了吗?
大家这个想法是没有任何问题的, 但是,绝对没有大家想象的那么简单。我们现在思考一个问题:如果一个key过期了,那么它实际是在什么时候被删除的呢?
可能很多同学的答案是: 到期了就直接被删除了。 但是这里告诉大家,这个答案是错误的。
这就需要给大家介绍下,Redis中对于过期键的过期删除策略:

  • 定时删除
  • 惰性删除
  • 定期删除

下面我就来逐一学习这三种过期删除策略的工作机制。

5.1 定时删除

它会在设置键的过期时间的同时,创建一个定时器, 当键到了过期时间,定时器会立即对键进行删除。 这个策略能够保证过期键的尽快删除,快速释放内存空间。
image.png
但是有得必有失。 Redis的操作频率是非常高的。绝大多数的键都是携带过期时间的,这样就会造成出现大量定时器执行,严重降低系统性能。
总的来说:该策略对内存空间足够友好, 但对CPU非常不友好,会拉低系统性能,因此不建议使用。

5.2 惰性删除

为了解决定时删除会占用大量CPU资源的问题, 因此产生了惰性删除。
它不持续关注key的过期时间, 而是在获取key时,才会检查key是否过期,如果过期则删除该key。简单来说就是:平时我不关注你,我用到你了,我才关注你在不在。
image.png
根据惰性删除来说,大家感觉它存在什么问题呢?
虽然它解决了定时删除会占用大量CPU资源的问题, 但是它又会造成内存空间的浪费。假设Redis中现在存在大量过期key,而这些过期key如果都不被使用,它们就会保留在redis中,造成内存空间一直被占用。
总的来说:惰性删除对CPU足够友好,但是对内存空间非常不友好,会造成大量内存空间的浪费

5.3 定期删除

根据刚才的学习大家可以发现,不管是定时删除还是惰性删除优缺点都非常明显:

  • 定时删除对内存空间友好,对CPU不友好。
  • 惰性删除对CPU友好,对内存空间不友好。

那现在有没有一种策略,能够平衡这两者的优缺点呢? 因此出现了定期删除。

5.3.1 工作机制

定期删除,顾名思义,就是每隔一段时间进行一次删除。 那么大家想一下,应该隔多久删一次? 一次又删除多少过期key呢?

  • 如果删除操作执行次数过多执行时间过长,就会导致和定时删除同样的问题:占用大量CPU资源去进行删除操作
  • 如果删除操作执行次数过少执行时间过短,就会导致和惰性删除同样的问题:内存资源被持续占用,得不到释放

所以定期删除最关键的就在于执行时长和频率的设置。
image.png

  • 默认每秒运行10次会对具有过期时间的key进行一次扫描,但是并不会扫描全部的key,因为这样会大大延长扫描时间。
  • 每次默认只会随机扫描20个key,同时删除这20个key中,已经过期的key。
  • 如果这20个key中过期key的比例达超过25%,则继续扫描。

    5.3.2 参数配置

    默认每秒扫描10次,对于这个参数能不能手动调整呢?
    当然是可以的,只需要修改redis.conf中的hz参数即可。
    image.png
    对于hz参数,官方建议不要超过100,否则会对CPU造成比较大的压力。

    6 内存淘汰策略

    6.1 为什么需要内存淘汰策略

    学习完过期删除策略后, 大家思考两个问题:

  • 通过惰性+定期删除,能不能百分百避免过期key没有被删除的情况?

  • 当大量插入到Redis,但内存空间不足时,Redis会如何处理呢?

举个例子:
有一些已经过期的key,定期扫描一直都没有扫描到它,而且这些key也一直没有被使用。 那么它们就会一直在内存中存在。同时继续向Redis不断插入新数据,最终造成内存空间不足的问题。
对于这种问题的解决,就用到了内存淘汰策略

6.2 最大内存参数配置

那么现在有的同学可能会想,那我把内存空间设置的很大不就可以了。 ok,你可以把它设置的大一些,但是怎么设置呢?
修改redis.conf中的maxmemory 来设置最大内存。
image.png
在64位操作系统中,如果未设置或设置0,代表无限制。而在32位系统中,默认内存大小为3GB。但是实际生产环境下,一般会设置物理内存的四分之三左右。

6.3 策略详解

当客户端执行命令,添加数据时,Redis会检查内存空间大小,如超过最大内存,则触发内存淘汰策略
image.png
在Redis中默认提供了三类八种淘汰策略。
image.png
对于这些策略各自的含义,我们还需要一点前置知识的铺垫,这里我们可以看到两个名称:lrulfu,他俩是什么意思呢?
他们的学名叫做:数据驱逐策略。 其实所谓的驱逐就是将数据从内存中删除掉

  • lru:Least Recently Used,它是以时间为基准,删除最近最久未被使用的key。
  • lfu:Least Frequently Used,它是以频次为基准,删除最近最少未被使用的key。

那理解了lru和lfu之后,我们再回来看这三类八种内存淘汰策略各自的机制。
image.png
知识小贴士:
对于LRUTTL相关策略,每次触发时,redis会默认从5个key中一个key符合条件的key进行删除。如果要修改的话,可以修改redis.confmaxmemory-samples属性值
image.png

6.4 生产环境下的策略设置&选择

刚才已经为大家介绍完了redis中的八种内存淘汰策略,那么此时大家可能在想,这些策略我应该如何设置?何时来选择哪种策略使用?
对于这两个问题,来给大家做一个一一解答:

6.4.1 策略设置

redis默认使用noeviction,我们可以通过修改redis.confmaxmemory-policy属性值设置不同的内存淘汰策略。
image.png

6.4.2 不同策略的使用场景

1、Redis只做缓存,不做DB持久化,使用allkeys。如状态性信息,经常被访问,但数据库不会修改。
2、同时用于缓存和DB持久化,使用volatile。如商品详情页。
3、存在冷热数据区分,则选择LRU或LFU。如热点新闻,热搜话题等。
4、每个key被访问概率基本相同,选择使用random。如企业内部系统,访问量不大,删除谁对数据库也不造成太大压力。
5、根据超时时间长久淘汰数据,选择选用ttl。如微信过期好友请求。

7 Redis6.X新特性

新版Redis6特性讲解

  • 支持多线程
  • redis6多线程只是用来处理网络数据的读写和协议解析上,底层数据操作还是单线程
  • 执行命令仍然是单线程,之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH/LPOP 等等的并发问题
  • 默认不开启1 2 io-threads-do-reads yes io-threads 线程数
  • 官方建议 ( 线程数小于机器核数 )
    • 4 核的机器建议设置为 2 或 3 个线程
    • 8 核的建议设置为 4或6个线程,
  • 开启多线程后,是否会存在线程并发安全问题?
    • 不会有安全问题,Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。
  • 引入了 ACL(Access Control List)
  • 之前的redis没有用户的概念,redis6引入了acl
  • 可以给每个用户分配不同的权限来控制权限
  • 通过限制对命令和密钥的访问来提高安全性,以使不受信任的客户端无法访问
  • 提高操作安全性,以防止由于软件错误或人为错误而导致进程或人员访问 Redis,从而损坏数据或配置
  • 文档:https://redis.io/topics/acl
  • 常用命令1 2 3 4 5 6 7 8 + 将命令添加到用户可以调用的命令列表中,如+@hash - 将命令从用户可以调用的命令列表中移除 #切换默认用户 auth default 123456 #例子 密码 123 ,全部key,全部权限 acl setuser jack on >123 ~ +@all #例子 密码 123 ,全部key,get权限 acl setuser jack on >123 ~

    • acl list 当前启用的 ACL 规则
    • acl cat 支持的权限分类列表
    • acl cat hash 返回指定类别中的命令
    • acl setuser 创建和修改用户命令
    • acl deluser 删除用户命令 | 参 数 | 说明 | | —- | —- | | user | 用户 | | default | 表示默认用户名,或则自己定义的用户名 | | on | 表示是否启用该用户,默认为off(禁用) | | #… | 表示用户密码,nopass表示不需要密码 | | ~* | 表示可以访问的Key(正则匹配) | | +@ | 表示用户的权限,“+”表示授权权限,有权限操作或访问,“-”表示还是没有权限; @为权限分类,可以通过 ACL CAT 查询支持的分类。+@all 表示所有权限,nocommands 表示不给与任何命令的操作权限 |
  • client side caching客户端缓存

  • 类似浏览器缓存一样
    • 在服务器端更新了静态文件(如css、js、图片),能够在客户端得到及时的更新,但又不想让浏览器每次请求都从服务器端获取静态资源
    • 类似前端的-Expires、Last-Modified、Etag缓存控制
  • 文档:https://redis.io/topics/client-side-caching
  • 详细: 分为两种模式redis在服务端记录访问的连接和相关的key, 当key有变化时通知相应的应用
    应用收到请求后自行处理有变化的key, 进而实现client cache与redis的一致
    这需要客户端实现,目前lettuce对其进行了支持
  • 默认模式
    • Server 端全局唯一的表(Invalidation Table)记录每个Client访问的Key,当发生变更时,向client推送数据过期消息。
    • 优缺点
    • 优点:只对Client发送其访问过的被修改的数据
    • 缺点:Server端需要额外存储较大的数据量。
  • 广播模式
    • 客户端订阅key前缀的广播,服务端记录key前缀与client的对应关系。当相匹配的key发生变化时通知client。
    • 优缺点
    • 优点:服务端记录信息比较少
    • 缺点:client会收到自己未访问过的key的失效通知