Redis 相关问题

Redis 线程模型

Redis 是单线程模型还是多线程模型

Redis6.x 之前是真正意义上的单线程, 对外提供的键值存储服务的主要流程是单线程,也就是网络 IO 和数据读写是由单个线程来完成的.

Redis6.x 引入的多线程指的是网络请求过程采用了多线程,而键值对读写命令仍然是单线程处理的,所以 Redis 依然是并发安全的,也就是只有网络请求模块和数据操作模块是单线程的,而其它的持久化、集群数据同步等,其实是由额外的线程执行的.

为什么设计为单线程模型速度也很快

由于 Redis 是基于内存的操作,查找和操作的时间复杂度都是 0(1),因此 CPU并不是 Redis 的瓶颈。Redis 的瓶颈很可能是机器内存或网络带宽的大小。既然单线程易于实现并且 CPU 不会成为瓶颈,那采用单线程解决方案是合乎逻辑的

了。

1.基于内存操作:Redis 的所有数据都在内存中,因此所有的运算都是内存级别的,所以它的性能比较高.

2.数据结构简单:Redis 的数据结构是为自身专门量身打造的,而这些数据结构的查找和操作的时间复杂度都是 O(1).

3.多路复用和非阻塞 I/O:Redis 使用 I/O 多路复用功能来监听多个 socket连接客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了 I/O 阻塞操作,从而大大地提高了 Redis 的性能.

4.避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生.

全局 hash 表

hash可以在O(1)的时间内计算出hash值并且找到对应的entry位置,entry里面是一个一个key指针和value指针,其实还有其他信息。这也是redis之所以性能高的原因之一.

Redis持久化

Redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失。Redis还为我们提供了持久化的机制,分别是RDB(RedisDataBase)和AOF(AppendOnlyFile)。

RDB方式

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。在我们安装了redis之后,所有的配置都是在redis.conf文件中,里面保存了RDB和AOF两种持久化机制的各种配置。当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。

触发快照的时机

save的规则满足的情况下,会自动触发rdb规则

redis.conf

save:这里是用来配置触发 Redis 的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如"save m n"。表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。

如下配置: save 900 1 :表示 900 秒钟内至少 1 个键被更改则进行快照。

save 300 10 :表示 300 秒内至少 10 个键被更改则进行快照。

save 60 10000 :表示 60 秒内至少 10000 个键被更改则进行快照。

如果不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能.

flushall 命令,情况所有 redis 数据,生成一个空的 rdb 文件(无意义)

退出 redis,也会产生 rdb 文件, 命令: shutdown save

关机后下次启动 redis 会自动将文件中的数据还原到内存中.

AOF 方式

以日志的形式来记录每个写操作,将 Redis 执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

开启方式

修改 redis.conf 配置文件,开启 AOF 机制

appendon1y no

#默认是不开启 aof 模式的,改为 yes 开启.

appendfilename appendonly.aof #默认的文件名是 appendonly.aof,可

以通过 appendfilename 参数修改

AOF 同步机制

appendfsync always

#每次修改都会 sync。消耗性能

appendfsync everysec #每秒执行一次 sync,可能会丢失这 1s 的数据(默认)

重启 redis 生效

Redis 事务

Redis 事务本质是一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行.

所有的命令在事务中,并没有直接被执行.

只有发起执行 exec 命令的时候才会执行.

事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

但是事务不保证同一事物中多条命令执行的原子性,即使命令有错误也会添加到队列中,执行报错也不影响其他命令执行.

redis 的事务操作:

开启事务(multi)

执行命令, 命令入队(.....)

执行前可以放弃事务(discard)

执行事务(exec)

实例:

multi 开启事务

set a aa 添加命令

set b bb 添加命令

incr b 添加命令

exec 执行事务

主从复制

主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。

前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

使用一个 Redis 实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取。也就是说,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。

主从复制的作用主要包括:

1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

2.故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。

4.高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。

主从复制配置

主从复制时只需要配置从库即可,其默认为主库模式.

打开 redis 客户端登录,使用命令 info replication 查看.

主从复制可以搭建真集群,也可以搭建伪集群.真集群就是有多台主机,每台主机安装一个 redis.伪集群就是在一台主机上复制多份配置,修改其端口,运行多个redis 实例.配置方式两者相同.

主从配置实例

复制多份配置文件,一主二从

主机配置

bind 0.0.0.0 #任何 ip 都可以访问

daemonize yes 后台运行

pidfile /var/run/redis_6379.pid #进程号文件

logfile "6379.log" #日志文件 注意文件名修改只是为了区分

dbfilename dump6379.rdb #数据文件

requirepass root #主机密码

从机配置

#bind 注释

daemonize yes 后台运行

pidfile /var/run/redis_6380.pid #进程号文件

logfile "6380.log" #日志文件 注意文件名修改只是为了区分

dbfilename dump6380.rdb #数据文件

replicaof <masterip>主机 ip <masterport>主机端口

masterauth <master-password>主机密码

从机进入客户端模式 ./redis-cli -p 从机端口

进入客户端,使用 info replication 命令查看模式

主机

从机

测试主写从读

主机断开连接,从机依旧连接到主机的,但是没有写操作,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息!

哨兵机制

概述

哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。

单哨兵

哨兵集群

Key 过期策略

1. 立即删除。

在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。

但是立即删除对 cpu 是最不友好的。因为删除操作会占用 cpu 的时间.

2. 惰性删除。

惰性删除是指,某个键值过期后,此键值不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点很明显:浪费内存。dict 字典和 expires 字典都要保存这个键值的信息。

3. 定时删除。

每隔一段时间,对 expires 字典进行检查,删除里面的过期键。可以看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对 cpu 的影响。另一方面定时删除也有效的减少了因惰性删除带来的内存浪费。

redis 使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。

缓存穿透、缓存击穿、缓存雪崩

缓存处理流程

前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。

缓存穿透

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

数据库没有,缓存没有.

解决办法:

1.将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。这种情况我们一般会将空对象设置一个较短的过期时间.

2.对参数进行校验,不合法参数进行拦截.

缓存击穿

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

解决办法:

1.热点数据设置永不过期

2.加锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到 redis 缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.

缓存雪崩

缓存雪崩是指,在高并发情况下,大量的缓存失效,或者缓存层出现故障。于是所有的请求都会达到数据库,数据库的调用量会暴增,造成数据库也会挂掉的情况。

解决方法:

1.随机设置 key 失效时间,避免大量 key 集体失效。setRedis(Key,value,time + Math.random() * 10000);

2.若是集群部署,可将热点数据均匀分布在不同的 Redis 库中也能够避免 key全部失效问题

3.不设置过期时间

4.跑定时任务,在缓存失效前刷进新的缓存

总结

雪崩是大面积的key缓存失效;穿透是redis里不存在这个缓存key;击穿是redis某一个热点 key 突然失效,最终的受害者都是数据库。

对于“Redis 宕机,请求全部走数据库”这种情况,我们可以有以下的思路:

事发前:实现 Redis 的高可用(主从架构+Sentinel(哨兵),尽量避免 Redis挂掉这种情况发生。

事发中:万一 Redis 真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)

事发后:redis 持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。

猜你喜欢

转载自blog.csdn.net/m0_56492261/article/details/129690175