[Redis]

1.redis 简介

  • Redis是使用C语言开发的数据库,但Redis的数据是存在内存中的(内存数据库),读写速度非常快,Redis被广泛应用于缓存方向,此外,还经常用来做分布式锁和消息队列。
  • Redis提供了多种数据类型来支持不同的业务场景,Redis支持事务、持久化、Lua脚本、多种集群方案。

Q1:为什么要⽤ redis/为什么要⽤缓存

主要从“高性能”和“高并发”这两点来看待这个问题

高性能: 假如⽤户第⼀次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该⽤户访问的数据存在缓存中,这样下⼀次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接 操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

⾼并发: 直接操作缓存能够承受的请求是远远⼤于直接访问数据库的,所以我们可以考虑把数据库中的部分数据 转移到缓存中去,这样⽤户的⼀部分请求会直接到缓存这⾥⽽不⽤经过数据库。

Q2:为什么要⽤ redis ⽽不⽤ map/guava 做缓存?

缓存分为本地缓存和分布式缓存。以 Java 为例,使⽤⾃带的 map 或者 guava 实现的是本地缓存,最 主要的特点是轻量以及快速,⽣命周期随着 jvm 的销毁⽽结束,并且在多实例的情况下,每个实例都 需要各⾃保存⼀份缓存,缓存不具有⼀致性。 使⽤ redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共⽤⼀份缓存数据,缓存具有⼀致性。缺点是需要保持 redis 或 memcached服务的⾼可⽤,整个程序架构上更为复杂。

2.Redis的数据结构

  • string
  • hash
  • list
  • set
  • zset 有序集合

Redis 有常用操作:

redis-cli -h 127.0.0.1 -p 6379

keys *  # 获取所有的key
select 0  # 选择第一个库
move myString 1  # 将当前的数据库key移动到某个数据库,目标库有,则不能移动
flush db  # 清除指定库
randomkey  # 随机key
type key  # 类型
    
set key1 value1   # 设置key
get key1  # 获取key
mset key1 value1 key2 value2 key3 value3
mget key1 key2 key3
del key1  # 删除key
exists key  # 判断是否存在key
expire key 10  # 10s 过期
pexpire key  # 1000 毫秒
persist key  # 删除过期时间

subscribe chat1 # 订阅频道
publish chat1 "hell0 ni hao"  # 发布消息
pubsub channels  # 查看频道
pubsub numsub chat1  # 查看某个频道的订阅者数量
unsubscrible chat1  # 退订指定频道 或 punsubscribe java.*
psubscribe java.*  # 订阅一组频道

3.redis 和 memcached 的区别

  1. redis⽀持更丰富的数据类型(⽀持更复杂的应⽤场景):Redis不仅仅⽀持简单的k/v类型的数 据,同时还提供list,set,zset,hash等数据结构的存储。memcache⽀持简单的数据类型, String。
  2. Redis⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使⽤,⽽Memecache把数据全部存在内存之中。
  3. 集群模式:memcached没有原⽣的集群模式,需要依靠客户端来实现往集群中分⽚写⼊数据;但 是 redis ⽬前是原⽣⽀持 cluster 模式的.
  4. Memcached是多线程,⾮阻塞IO复⽤的⽹络模型;Redis使⽤单线程的多路 IO 复⽤模型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3zGMGyj4-1650892018987)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20220425201548881.png)]

4.redis 的线程模型

redis 内部使⽤⽂件事件处理器 file event handler ,这个⽂件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采⽤ IO 多路复用机制同时监听多个 socket,根据 socket 上的事件 来选择对应的事件处理器进⾏处理。 ⽂件事件处理器的结构包含 4 个部分: 多个 socket IO多路复⽤程序⽂件事件分派器事件处理器(连接应答处理器、命令请求处理器、命令回复处理器) 多个 socket 可能会并发产⽣不同的操作,每个操作对应不同的⽂件事件,但是 IO 多路复⽤程序会监听多个 socket,会将 socket 产⽣的事件放⼊队列中排队,事件分派器每次从队列中取出⼀个事件, 把该事件交给对应的事件处理器进⾏处理。

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据 套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。**虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字,**文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。

  • Redis单线程为什么快?

1)纯内存操作
2)核心是基于非阻塞的IO多路复机制来处理客户端的Socket
3)单线程避免了多线程频繁的上下文切换带来的性能问题

5.redis 持久化机制

怎么保证 redis 挂掉之后再重启数据可以进⾏恢复?

Redis不同于Memcached的很重⼀点就是,Redis⽀持持久化,⽽且⽀持两种不同的持久化操作。Redis的 ⼀种持久化⽅式叫快照(snapshotting,RDB),另⼀种⽅式是只追加⽂件(append-only file,AOF)。

RDB

RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式。也就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb。

RDB 支持 同步(save 命令)、后台异步(bgsave)以及自动配置三种方式触发。

优点

  • RDB 文件紧凑,全量备份,非常适合用于进行备份和灾难恢复

  • 生成 RDB 文件时支持异步处理,主进程不需要进行任何磁盘IO操作

  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快

    缺点

  • RDB 快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。且在快照持久化期间修改的数据不会被保存,可能丢失数据。

快照持久化是Redis默认采⽤的持久化⽅式。

AOF

随着时间推移,AOF 持久化文件也会变的越来越大。为了解决此问题,Redis 提供了 bgrewriteaof 命令,作用是 fork 出一条新进程将内存中的数据以命令的方式保存到临时文件中,完成对AOF 文件的重写

AOF(append-only file)持久化与快照持久化相⽐,AOF持久化 的实时性更好,因此已成为主流的持久化⽅案。默认情况下Redis没有 开启AOF(append only file)⽅式的持久化.

在Redis的配置⽂件中存在三种不同的 AOF 持久化⽅式,它们分别是:

save 900 1 #在900秒(15分钟)之后,如果⾄少有1个key发⽣变化, Redis就会⾃动触发BGSAVE命令创建快照。

save 300 10 #在300秒(5分钟)之后,如果⾄少有10个key发⽣变化, Redis就会⾃动触发BGSAVE命令创建快照。

save 60 10000 #在60秒(1分钟)之后,如果⾄少有10000个key发⽣变化, Redis就会⾃动触发BGSAVE命令创建快照。

AOF 也有三种触发方式:1)每修改同步 always 2)每秒同步 everysec 3)不同no:从不同步。

优点

  • AOF 可以更好的保护数据不丢失,一般 AOF 隔 1 秒通过一个后台线程执行一次 fsync 操作
  • AOF 日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损
  • AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写
  • AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复

缺点

  • 对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大
  • AOF开启后,支持的写 QPS 会比RDB支持的写 QPS 低,因为 AOF 一般会配置成每秒 fsync 一次日志文件,当然,每秒一次 fsync,性能也还是很高的

Redis 4.0对于持久化机制的优化

Redis 4.0 开始⽀持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb preamble 开启)。 如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF ⽂件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF ⾥⾯的 RDB 部分是压缩格式不再是 AOF 格式,可读性差。 补充内容:AOF 重写 AOF重写可以产⽣⼀个新的AOF⽂件,这个新的AOF⽂件和原有的AOF⽂件所保存的数据库状态⼀样,但体积更⼩。

6. redis 设置过期时间

Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置⼀个过期时间。作为⼀个 缓存数据库,这是⾮常实⽤的。如我们⼀般项⽬中的 token 或者⼀些登录信息,尤其是短信验证码都 是有时间限制的,按照传统的数据库处理⽅式,⼀般都是⾃⼰判断过期,这样⽆疑会严重影响项⽬性 能。 我们 set key 的时候,都可以给⼀个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。 如果假设你设置了⼀批 key 只能存活1个⼩时,那么接下来1⼩时后,redis是怎么对这批key进⾏删除 的?

定期删除+惰性删除。

定期删除:redis默认是每隔 100ms 就随机抽取⼀些设置了过期时间的key,检查其是否过期, 如果过期就删除。注意这⾥是随机抽取的。为什么要随机呢?你想⼀想假如 redis 存了⼏⼗万 个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很⼤的负载!

惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删 除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存⾥,除⾮你的系统去查⼀下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈! 但是仅仅通过设置过期时间还是有问题的。

我们想⼀下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没⾛惰性删除,此时会怎么样?如果⼤量过期key堆积在内存⾥,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。

7.redis 内存淘汰机制

MySQL⾥有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?

redis 提供 6种数据淘汰策略:

  1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使⽤的数据淘汰

  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘 汰

  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

  4. allkeys-lru:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的key(这个是 最常⽤的)

  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  6. no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。 这个应该没⼈使⽤吧!

    4.0版本后增加以下两种:

  7. volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使⽤的数据 淘汰

  8. allkeys-lfu:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最不经常使⽤的key

8.缓存雪崩和缓存穿透问题

缓存雪崩

简介:缓存同⼀时间⼤⾯积的失效,所以,后⾯的请求都会落到数据库上,造成数据库短时间内承受⼤量请求⽽崩掉。 有哪些解决办法?

事前:尽量保证整个 redis 集群的⾼可⽤性,发现机器宕机尽快补上。选择合适的内存淘汰策 略。

事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉

事后:利⽤ redis 持久化机制保存的数据尽快恢复缓存

缓存穿透?

缓存穿透说简单点就是⼤量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这⼀层。举个例⼦:某个⿊客故意制造我们缓存中不存在的 key 发起⼤量请求,导致⼤量请求落到数据库。

正常缓存处理流程: 缓存穿透情况处理流程: ⼀般MySQL 默认的最⼤连接数在 150 左右,这个可以通过 show variables like ‘%max_connections%’; 命令来查看。最⼤连接数⼀个还只是⼀个指标,cpu,内存,磁盘,⽹络等 ⽆⼒条件都是其运⾏指标,这些指标都会限制其并发能⼒!所以,⼀般 3000 个并发请求就能打死⼤部 分数据库了。

哪些解决办法? 最基本的就是⾸先做好参数校验,⼀些不合法的参数请求直接抛出异常信息返回给客户端。⽐如查询的数据库 id 不能⼩于 0、传⼊的邮箱格式不对的时候直接返回错误消息给客户端等等。

1)缓存无效 key : 如果缓存和数据库都查不到某个 key 的数据就写⼀个到 redis 中去并设置过期时 间,具体命令如下: SET key value EX 10086 。这种⽅式可以解决请求的 key 变化不频繁的情 况,如果黑客恶意攻击,每次构建不同的请求key,会导致 redis 中缓存⼤量⽆效的 key 。很明显, 这种⽅案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短⼀点比如 1 分钟。 另外,这里多说⼀嘴,⼀般情况下我们是这样设计 key 的: 表名:列名:主键名:主键值 。

2)布隆过滤器:布隆过滤器是⼀个⾮常神奇的数据结构,通过它我们可以⾮常⽅便地判断⼀个给定数 据是否存在与海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“⼈”。具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当⽤户请求过来,我会先判断⽤户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会⾛下⾯的流程。

参考博客:https://blog.csdn.net/adminpd/article/details/122934938

以及JavaGuide总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值