redis线程模型
Redis线程模型
Redis是单线程模型还是多线程模型:
1、redis6.0之前的版本是真正的单线程模型,用于网络监听、执行命令等操作都是由一个线程来完成
redis服务器 负责监听网络客户端连接和指定命令等,都是由一个线程完成操作。
2、redis6.0之后引入了多线程模型,将网络连接等操作由一个线程来完成,执行命令的数据操作仍是由一个线程来完成,这样的并发就是安全的;
redis单线程操作也是非常快的:
1、redis数据操作都是在内存层面操作,cpu不会是瓶颈问题。
2、数据结构简单,redis底层是一个全局哈希表,时间复杂度为O(1)。
3、底层使用IO多路复用和非阻塞IO方式提高了效率,使用一个线程就可以完成对多个连接的监听,从而减少线程切换开销。
4、避免上下文切换:redis是单线程模型,避免了不必要的上下文切换和多
线程竞争,省去了多线程切换带来的时间和性能上的开销。
redis持久化
redis数据默认保存到内存中,有时有必要将数据持久化到硬盘。
点赞-->redis-->定期将任务写到mysql中。
redis数据库提供了持久化机制,可以将数据持久化到硬盘上。
redis数据持久化机制:
RDB:
实现机制:在指定的时间间隔内将内存中的数据集快照写入磁盘。
redis中默认使用RDB方式进行数据持久化;
进行持久化需要一定的规则
save 20 10 就是在20秒内对10个键值进行操作,会触发持久化;
也可以在关闭redis时使用shutdown save命令持久化到rdb文件中。
数据持久化的方式:
将redis中所有的键值对备份到dump.rdb文件中
AOF:
实现机制:记录”写“操作日志,后期需要回复数据时,执行记录的文件。
开启方式:
修改 redis.conf 配置文件,开启 AOF 机制
appendon1y no
#默认是不开启 aof 模式的,改为 yes 开启.
appendfilename appendonly.aof #默认的文件名是 appendonly.aof,可
以通过 appendfilename 参数修改
AOF 同步机制
appendfsync always
#每次修改都会 sync。消耗性能
appendfsync everysec #每秒执行一次 sync,可能会丢失这 1s 的数据(默认)
重启 redis 生效
redis事务
回顾mysql事务:
满足原子性 一次执行多条sql,保证同一个事务中,多条都成功或者都失败。
redis事务相比mysql事务简单
redis执行命令是单线程的,执行单挑命令是保证原子性的。
一个客户端在执行命令时,另一个客户端不不可能插入执行。
需要一次性执行多条命令,这时为了保证隔离性(其他客户端不能从中间插入),就需要redis中的事务进行控制。
redis开启事务后,会将发送的命令放到一个队列中,当执行exe命令时,才会同意执行命令,期间与其他客户端隔绝。
但是不保证,多条命令在同一个事务中的原子性,若多条命令中如果有报错的,不影响其他几条命令的执行。
redis主从复制
主从复制:将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。
前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能
由主节点到从节点。
搭建redis集群,中间有一台作为主机,多台作为从机,主机负责接收写入命令,将数据同步到从机,之后的客户端如果读数据的话,可以从从机读取;
主从复制(redis集群)的作用:
1、数据冗余:一台主机多台从机,数据有备份;
2、负载均衡:主机负责写入,从机负责读
3、故障恢复
4、高可用集群
哨兵机制
在集群中,有一个作为主机,万一知己服务坏了,那么谁来作为主机
redis中有一个哨兵机制,可以在从机中根据某种规则重新选举一台作为主机。
哨兵模式:是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的
进程,作为进程,它会独立运行。
原理:哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。
单哨兵:
哨兵集群:
key过期策略
在设置key value时,给定了有效时间,那么时间到期后,redis在微观上采用何种策略删除key。
架构设计时需要考虑的问题:
1、立即删除:设置key时,创建一个回调函数,当key过期时,立即删除,好处是立刻释放内存;不友好的是大量key过期,瞬间导致cpu标高。
2、惰性删除:键值过期时并没有及时删除掉,而是在下一次使用时删除,不足浪费内存。
3、定时删除:每隔一段时间,对expires字典进行检查,删除里面的过期键。
立即删除和定期删除都属于主动删除。
redis使用的过期键删除策略:惰性删除加上定期删除,两者配合使用。
redis缓存穿透、缓存击穿/缓存雪崩
缓存处理流程:
前端请求,后端先从缓存中获取数据,取到直接返回结果,取不到从数据库中获取,数据库更新缓存,返回结果;数据库中没有获取到,直接返回空结果。
缓存穿透:
key对应的数据在数据库中不存在,每次针对此key的请求从缓存中获取不到,请求都会到数据库,从而可能压垮数据库。
redis没有、查询数据库,数据库也没有
解决办法:
1、将空对象设置到缓存中,下次再请求的时候,从缓存中获取。
2、对参数进行设置,不合法的参数进行拦截。
缓存击穿:
某个key对应的数据库中存在,redis中的key过期,此时有大量的请求访问,缓存过期,从后端数据库加载数据,大量请求会压垮数据库。
解决办法:
1、热点数据设置永不过期
2、加锁。
缓存雪崩:
高并发情况下,缓存失效,大量请求到后端数据库,大量的key过期
解决办法:
1、随机设置key失效时间,避免大量key集体失效;
setRedis(Key,value,time + Math.random() * 10000);
2、集群部署,将热点数据均匀分布在不同的redis库中避免key失效;
3、不设置过期时间;
4、定时任务,在缓存失效前刷新进新的缓存。
总结:
雪崩是大面积key失效;穿透是redis里面不存在缓存key;击穿是redis某一个热点key突然失效,最终受害者都是数据库。
mysql在redis的保护下,也不是绝对安全的,也可能出现问题,每一种情况至少能出一两种解决方案
对于“redis宕机,请求全部走数据库”,可以如下解决:
事发前:实现redis的高可用(主从架构+Sentinel(哨兵)),尽量避免redis挂掉。
事发中:redis真的挂了,设置本地缓存(ehcache)+限流,尽量避免数据库失效。
事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。