一、Redis线程模型
提出问题:Redis 是单线程模型还是多线程模型?
Redis读写速度快,为什么快?
Redis6.x 版本之前是真正意义上的单线程
Redis6.x 引入了多线程,将客户端网络请求,由一些线程来负责完成
数据的读写操作仍然使用的是单线程,单线程操作不会出现并发安全问题
为什么设计成单线程模型速度也很快?
1.基于内存操作:Redis 的所有数据都在内存中,因此所有的运算都是内存级别的,所以它的性能比高.
2.数据结构简单:Redis 的数据结构是为自身专门量身打造的,而这些数据结构的查找和操作的时间复杂度都是 O(1).
3.多路复用和非阻塞 IO:Redis 使用 IO 多路复用功能来监听多个 socket连接客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了 IO 阻塞操作,从而大大地提高了 Redis 的性能.
4.避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生.
hash表可以在 O(1)的时间内计算出 hash 值并且找到对应的 entry 位置,entry
里面是一个 key 指针和 value 指针,其实还有其他信息。这也是 redis 之所以性能高
的原因之一.
二、Redis持久化
为什么redis要提出持久化概念?
因为redis数据平时存储在内存中,一旦机器故障,断电,数据可能就会丢失,
redis也提供了数据持久化机制,把内存中的数据根据一定的规则,写入到硬盘中
去,开启定时任务,每天的某一时间,把redis的数据写入到mysql中。Redis 还
为我们提供了持久化的机制,分别是RDB(Redis DataBase)和 AOF(Append Only File)。
RDB方式(Redis DataBase)
RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,保存在dump.rdb文件中。
数据集快照:内存中有许多key-value key1-value1---->dump.rdb文件
RDB方式是redis默认的持久化方式,不需要配置开启,默认是支持的.
里面有触发RDB保存快照的条件,如下配置:
save 900 1 :表示 900 秒钟内至少 1 个键被更改则进行快照。
save 300 10 :表示 300 秒内至少 10 个键被更改则进行快照。
save 60 10000 :表示 60 秒内至少 10000 个键被更改则进行快照。
在redis客户端模式中,使用shutdown save 命令在关闭redis服务时,保存快照
重新启动redis服务时,把dump.rdb文件内容还原回来.
AOF方式(Append Only File)
AOF是以记录日志的方式来记录所有写操作的命令
set name jim 执行一次 记录一次
set name tom
name : tom
appendfsync always 每次修改都会 sync 消耗性能
appendfsync everysec 每秒执行一次 sync,可能会丢失这1s 的数据(默认)
定期结合mysql方式
人为的定期将数据写入到mysql中。
三、Redis事务
redis事务,是将多条命令放入到一个队列中,保证多条命令,在同一个事务中执行,
不受其他客户端的影响.
multi 开启事务
命令1
命令2
命令3 把命令加入到一个队列中,并没有立即执行
exec 执行事务
但是事务不保证同一事物中多条命令执行的原子性,即使命令有错误也会添加到队列中,执行报错也不影响其他命令执行.
四、主从复制
Redis在大型项目(互联网电商),为了保证系统稳定,有一台服务作为主机,有多台服务作为从机,主机负责写入数据,将数据同步到多台从机,从机负责读操作,实现读写分离
即使是主机故障,也可以从多台从机中,选举出一台服务作为主机(哨兵机制),这样必将存在数据冗余和服务冗余.
负载均衡:
在主从复制的基础上,配合读写分离,可以由主节点提供写服务, 由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的 场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。
redis伪集群:
可以在一台机器上,配置多个redis端口,启动多个redis实例.
五、哨兵机制
哨兵是一个独立的任务,独立运行,定期的向集群中的redis发送信息,如果收不到信息,就表明redis 服务有问题,可以通过一套选举机制,在多台从机中选取一台作为主机,当主机故障恢复后,临时主机又 变为了从机.
六、key过期策略
在设置key时,可以为key设置一个有效时间,当时间到了后,redis有一套机制,可以来删除过期的key.
立即删除
在设置key时,创建一个回调函数,当key的时间到了之后,立即执行回调函数,删除key,不浪费内存空 间,到期即删除. 如果有大量的key同时到期,删除会占用cpu时间,所以对CPU不太友好.
惰性删除
到期后不立即删除.在下次使用此key时,判断是否到期,到期后就删除,对内存不友好,浪费内存空间
定时删除
将过期的key记录下来,定期的对过期的key的进行删除.
可以看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。每隔一段时间执 行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对 cpu 的影响。另一方面 定时删除也有效的减少了因惰性删除带来的内存浪费.
redis 使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用.
七、缓存穿透、缓存击穿、缓存雪崩
开始使用redis时,redis可以用来缓存数据,已达到对mysql的压力缓解,也不是说用了redis,就不会出现问题了,在一些极端情况下也是会出现问题的.
缓存穿透
概念:
有一个数据,在数据库中本身就不存在,这样redis中也就没有了,那么每次查询的请求依然会到达 mysql数据库(黑客可能利用此漏洞去攻击,把数据库弄跨)
解决办法:
1.将这个空对象设置到缓存里面去,下次再请求时,就可以从缓存里边获取了,这种情况我们一般会为 空对象设置一个较短的过期时间.
2.对参数进行校验,不合法的参数进行拦截
缓存击穿
概念:
数据库中有数据,在某一时间节点上,某个热点key到期了,此时刚好有大量的请求到达,如果没有任 何的限制,同时查询redis,但是redis中没有(过期了),请求就会到达mysql,导致mysql被压垮
解决办法:
1.将热点key设置为永不过期(比如秒杀活动:让key在活动结束后再过期)
2.加锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请 求上使用一个互斥锁来锁住它,其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数 据,然后将数据放到 redis 缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存.
缓存雪崩
概念:
在高并发状态下,大量的key过期或者缓存出现故障,导致大量请求到达mysql,导致mysql垮掉.
解决办法:
1.设置key的失效时间随机,不要同时失效.
2.把热点key放在不同的从机
3.设置不过期
4.定时任务,将快过期的key,重新放入到缓存