Redis相关知识及应用(单线程,缓存,队列,限流,分布式锁,淘汰策略,持久化策略,redis集群)

1,非关系型数据库reids

Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型不仅限于字符串,还支持字符串列表,无序不重复的字符串集合,有序不重复的字符串集合,键、值都为字符串的哈希表
redis提供五种数据类型:string,hash,list,set及zset(sorted set)
string(字符串);list(双向链表);dict(hash表);Set(集合);zset(sorted set:有序集合)

2,redis的单线程

Redis 的单线程指的是网络 IO 和键值对读写。Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。

单线程依然很快的原因
1,redis基于内存存储,内存读写速度很快
2,redis采用I/O多路复用机制(select/epoll提供了基于事件的回调机制,针对不同事件,调用相应的处理函数)
3,单线程处理避免了多线程的数据竞争,加锁,上下文切换等问题

4,reids的应用

4.1 数据缓存

缓存经常读取查询而不常修改的数据
作用:降低对数据库的请求,减轻服务器压力
缓存和数据库一致性解决方案
1,采用延时双删策略,在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。
2,异步更新缓存(基于订阅binlog的同步机制)
MySQL binlog增量订阅消费+消息队列+增量数据更新到redis
canal(阿里的一款开源框架),通过该框架可以对MySQL的binlog进行订阅,canal正是模仿了mysql的slave数据库的备份请求,消息推送工具你也可以采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis。

4.2 队列

1,List 队列(LPUSH+BRPOP实现)

不支持重复消费,宕机会造成消息丢失

2,发布/订阅模型:Pub/Sub

Redis 提供了 PUBLISH / SUBSCRIBE 命令,来完成发布、订阅的操作。

支持发布 / 订阅,支持多组生产者、消费者处理消息
消费者下线,数据会丢失
不支持数据持久化,Redis 宕机,数据也会丢失
消息堆积,缓冲区溢出,消费者会被强制踢下线,数据也会丢失
3,基于Sorted-Set的实现

zset 可能是 Redis 提供的最为特色的数据结构,它的内部实现用的是一种叫做「跳跃列表」的数据结构。

一方面它是set,保证 value 的唯一性,
一方面它可以给每个 value 一个 score,代表排序权重。
zset 中最后一个 value 被移除后,数据结构自动删除,内存被回收。
使用场景:
粉丝列表,value 值是粉丝的用户 ID,score 是关注时间
视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等
4, 趋于成熟的队列:Stream
    消息ID的序列化生成(由两部分组成:时间戳-序号)
    消息遍历
    消息的阻塞和非阻塞读取(XRED读消息时分为阻塞和非阻塞模式,使用BLOCK选项可以表示阻塞模式,需要设置阻塞时长。非阻塞模式下,读取完毕(即使没有任何消息)立即返回,而在阻塞模式下,若读取不到内容,则阻塞等待。)
    消息的分组消费(消费者组模式,consumer group)
    未完成消息的处理(Pending 列表,用于记录读取但并未处理完毕的消息)
    消息队列监控(提供了XINFO来实现对服务器信息的监控)

总结:redis做队列,本身可能会丢数据,面对消息积压,Redis 内存资源紧张。
业务场景足够简单,对于数据丢失不敏感,而且消息积压概率比较小的情况下,可以 Redis 当作队列。其他场景还是需要专业的消息队列工具如RabbitMq

4.3 分布式锁

1,基于(setNx + expire 命令/SET EX PX NX命令)+ Lua脚本 + uuid + 守护线程的分布式锁
 expire : 防止线程执行中出问题而不释放锁造成死锁问题
 SET EX PX NX命令 :相当于将setNx + expire整合为原子性操作,避免获取的不是自己的锁
 Lua脚本:可以把加锁和释放锁逻辑整合为原子性操作
 uuid :释放锁时判断,避免释放的不是自己持有的锁
 守护线程:类似于redisson的看门狗机制,定时监听是否持有锁,给快要过期的锁“续期”
2,基于setnx、get、getset的分布式锁
 (1)setnx(lockkey, 当前时间+过期超时时间(newExpireTime)) ,获取锁失败,转向步骤(2)
 (2)get(lockkey)获取存在的旧值oldExpireTime ,将其与当前的系统时间比较,小于当前系统时间,则认为这个锁已超时,可允许其他请求重新获取,转向步骤(3)
 (3)getset(lockkey, newExpireTime) 插入新请求的newExpireTime值并返回当前lockkey的值currentExpireTime
 (4)判断 currentExpireTime 与 oldExpireTime 是否相等,相等则说明当前getset设置成功,获取到了锁。不相等说明这个锁又被别的请求获取走了,那当前请求可以直接返回失败或继续重试。
 (5)在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后比较自己的处理时间和对于锁设置的超时时间,小于锁设置的超时时间则直接执行del命令释放锁(释放锁之前需要判断持有锁的线程是不是当前线程);如果大于锁设置的超时时间,则不需要再对锁进行处理

总结:此类加锁时只作用在一个Redis节点上,即使Redis通过集群保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况

3,多机实现的分布式锁Redlock+Redisson
1,按顺序向5个master节点请求加锁
2,根据设置的超时时间来判断,是不是要跳过该master节点。
3,如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功。
4,如果获取锁失败,客户端要在所有的master节点上解锁(防止漏网之鱼)!

4.4 限流

1,固定窗口(基于Redis的setnx操作)
1,setNx命令,key为请求方appId,value为限流时间内的请求次数,expire为key设置过期时间(限流的单位时间)
2,每当有请求到达,get获取value判断是否到达限定次数

缺陷:窗口时间无法滑动,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题。

2,滑动窗口(基于Redis的zset操作)
我们可以将请求打造成一个zset数组,当每一次请求进来的时候,用UUID生成value保持唯一,而score可以用当前时间戳表示。
zset数据结构也提供了range方法让我们可以轻易的获取到2个时间戳内有多少请求

缺陷:zset数据结构会越来越大,可以根据业务每隔一段时间定时清理

3,令牌桶算法(基于Redis的List结构<双向链表>)
依靠List的leftPop来获取令牌
再依靠Java的定时任务,定时往List中rightPush令牌,当然令牌也需要唯一性,
一旦需要提高速率,则按需提高放入桶中的令牌的速率即可。
每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。

5,缓存问题解决

5.1 缓存雪崩

概念:缓存同一时间批量失效,大量访问直接访问数据库
解决

缓存时做固定失效时间+随机时间段,保证所有缓存不在同一时间失效

5.2 缓存穿透

概念:无效数据的key在缓存查询不到,大量查询数据库造成数据库压力过大
解决

1,无效数据的key查询数据库查询不到也在redis中缓存起来并设置较短的过期时间
2,布隆过滤器:当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点(offset),把它们置为 1。
检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。
原理就是将存在的值建立一个高效的检索,每次缓存取值时,先走一次判空操作

5.3 缓存击穿

概念:缓存失效时,高并发访问失效缓存
解决

1,热点数据设置永不超时
2,为key加互斥锁,缓存的key不存在时,加锁去数据库取。
其他请求到来时,请求的key相同,暂停几秒再去缓存取(此时key大概率已被写入缓存),
请求的key不相同,直接数据库取

6,reids持久化策略

一、RDB(基于内存快照的二进制文件)

操作方式1:配置文件

这种快照的持久化方式会将内存数据快照保存到名字为dump.rdb的二进制文件中,可以在redis.conf文件中通过dbfilename参数进行配置:

//默认配置如下(三个策略):任何一个策略满足就会触发
// 格式:save m n  表示m秒内数据集至少有n个改动时,就会触发一次持久化。
save 900 1     // 900秒内至少有一个key发生改动就保存一次数据
save 300 10    // 300秒内至少有10个key发生改动就保存一次数据
save 60 10000  // 60秒内只有有10000个key发生改动就保存一次数据
操作方式2:手动执行save/bgsave命令
命令							save			bgsave
IO类型						同步				异步,子进程进行IO操作
是否阻塞redis客户端命令		是				否(fork子进程时会有短暂阻塞)
优点							没有额外内存消耗	不阻塞客户端命令
缺点							阻塞客户端命令	需要fork子进程,消耗内存

save与bgsave命令的主要区别在于
是否会fork一个子进程来进行持久化,通过配置文件进行持久化使用的是bgsave的方式。

二,AOF日志

1,原理

将修改的每一条指令增量记录到appendonly.aof文件中,先写入操作系统的缓存(os cache),每隔一段时间fsync到磁盘。

2,配置
//Redis的配置默认使用的是RDB持久化方式,可以通过修改配置文件的参数开启AOF功能
appendonly yes   // 默认是no

appendfsync always    // 每次有新命令追加到AOF文件时就执行一次fsync,效率最低,但最安全
appendfsync everysec  // 每秒fsync一次,足够快,并且系统故障时只会丢失1s的数据,默认这种方式
appendfsync no        // 从不sync,将数据交给操作系统来处理,效率最高,但也是最不安全
//一般都推荐使用appendfsync everysec ,这种策略可以兼顾速度和安全性。

//还可以通过下面两个参数配置重写的频率:
auto-aof-rewrite-min-size 64mb   //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto-aof-rewrite-percentage 100  //aof文件自上一次重写后文件大小增长了100%则再次触发重写

AOF还可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF,
AOF手动重写redis会fork出一个子进程去做(与bgsave命令类似),不会对redis正常命令处理有太多影响。
注意:redis自动进行的aof持久化或aof重写都是主进程完成的,并不会fork子进程来完成

RDB和AOF的区别

命令			RDB			AOF
启动优先级	低			高
体积			小			大
恢复速度		快			慢
数据安全性	容易丢数据	根据决策决定

三,混合持久化

//注意:开始混合持久化的前提是要开启AOF持久化,因为混合持久化用的aof文件。
//Redis4.0就将两者进行了结合,形成了新的持久化方式——混合持久化。可以通过如下配置开启:
aof-use-rdb-preamble yes  // 默认开启

原理:开启了混合持久化之后,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,
并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,
新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在 Redis 重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志完成数据重建,因此重启效率大幅提升。

7,redis淘汰策略

一. 8种策略

volatile-lru,	针对设置了过期时间的key,最不常用的淘汰。
allkeys-lru,	针对所有key最不常用的淘汰。
volatile-lfu,	针对设置了过期时间的key,用的次数最少的key淘汰。
allkeys-lfu,	针对所有key,用的次数最少的key淘汰。
volatile-random,从所有设置了过期时间的key中使用随机淘汰的方式进行淘汰。
allkeys-random, 针对所有的key使用随机淘汰机制进行淘汰。
volatile-ttl,	 删除生存时间最近的一个键。
noeviction(默认),不删除键,值返回错误。

算法:
    lru 		最近很好的使用的key(根据时间,最不常用的淘汰)
    lfu 		最近很好的使用的key (根据计数器,用的次数最少的key淘汰)
    random 		随机淘汰
    ttl 		快要过期的先淘汰

二,LRU和redis中的LRU

1, 标准的LRU算法
为了降低查找和删除元素的时间复杂度,一般采用Hash表和双向链表结合的数据结构,
hash表可以赋予链表快速查找到某个key是否存在链表中,同时可以快速删除、添加节点
双向链表的查找时间复杂度是O(n),删除和插入是O(1),借助HashMap结构,可以使得查找的时间复杂度变成O(1)
Hash表用来查询在链表中的数据位置,链表负责数据的插入.
2,redis中的lru算法
工作机制是:
    随机采集淘汰的key,每次随机选出5个key
    然后淘汰这5个key中最少使用的key
Redis3.0+的优化
	  维护一个大小为16的候选池,时间依次最小的放入,候选池的数据满了之后,那么时间最大的key就会被挤出候选池。
	  当执行淘汰时,直接从候选池中选取最近访问时间小的key进行淘汰。
缺陷:有可能产生误判。非热点数据由于近期被访问不会被淘汰,热点数据由于近期未被访问而淘汰  

二,LFU

LFU(Least Frequently Used),表示最近最少使用,
它和key的使用次数有关,其思想是:根据key最近被访问的频率进行淘汰,比较少访问的key优先淘汰,反之则保留。
LRU的原理是使用计数器来对key进行排序,每次key被访问时,计数器会增大,当计数器越大,意味着当前key的访问越频繁,也就是意味着它是热点数据。 
它很好的解决了LRU算法的缺陷:一个很久没有被访问的key,偶尔被访问一次,导致被误认为是热点数据的问题。

LFU维护了两个链表,横向组成的链表用来存储访问频率,
每个访问频率的节点下存储另外一个具有相同访问频率的缓存数据。具体的工作原理是:
    1,当添加元素时,找到相同访问频次的节点,然后添加到该节点的数据链表的头部。
    如果该数据链表满了,则移除链表尾部的节点当获取元素或者修改元素是,
    都会增加对应key的访问频次,并把当前节点移动到下一个频次节点。
    2,添加元素时,访问频率默认为1,随着访问次数的增加,频率不断递增。而当前被访问的元素也会随着频率增加进行移动。

8,redis集群策略

一. 主从模式

主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库
从数据库一般都是只读的,并且接收主数据库同步过来的数据
一个master可以拥有多个slave,但是一个slave只能对应一个master

主从复制工作机制
	当slave启动后,主动向master发送SYNC命令。
	master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,
	然后将保存的快照文件和缓存的命令发送给slave。
	slave接收到快照文件和命令后加载快照文件和缓存的执行命令。
	复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据一致性。
	
缺点
	一旦主库或从库宕机后,客户端需要⼿动修改IP,断开期间内redis无法进行写操作。
	另外,这种模式也⽐较难进⾏扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能⽀持特⼤数据量

二,哨兵模式

哨兵作用
	监控主从数据库是否正常运行
	master出现故障时,自动将slave转化为master
	多哨兵配置的时候,哨兵之间也会自动监控
	多个哨兵可以监控同一个redis
哨兵工作机制
	哨兵启动后,会与要监控的master建立俩条连接:
	一条连接用来订阅master的_sentinel_:hello频道与获取其他监控该master的哨兵节点信息
	另一条连接定期向master发送INFO等命令获取master本身的信息	
	定期向master和slave发送INFO命令(哨兵可以获取主从数据库的最新信息,并进行相应的操作,比如角色变更)
	定期向master和slave的_sentinel_:hello频道发送自己的信息(与同样监控这些数据库的哨兵共享自己的信息)
	定期向master、slave和其他哨兵发送PING命令(定时监控这些数据库和节点有没有停止服务)
	如果被ping的数据库或者节点超时未回复,哨兵任务其主观下线。
	如果下线的是master,哨兵会向其他哨兵点发送命令询问他们是否也认为该master主观下线,
	如果达到一定数目(即配置文件中的quorum)投票,哨兵会认为该master已经客观下线,并选举领头的哨兵节点对主从系统发起故障恢复。
	
选举采用Raft算法:
	发现master下线的哨兵节点(我们称他为A)向每个哨兵发送命令,要求对方选自己为领头哨兵
	如果目标哨兵节点没有选过其他人,则会同意选举A为领头哨兵
	如果有超过一半的哨兵同意选举A为领头,则A当选
	如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,
		此时每个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票精选,直至选举出领头哨兵
	选出领头哨兵后,领头者开始对进行故障恢复,从出现故障的master的从数据库中挑选一个来当选新的master,
选择规则:
	所有在线的slave中选择优先级最高的,优先级可以通过slave-priority配置
	如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选
	如果以上条件都一样,选取id最小的slave
	挑选出需要继任的slaver后,领头哨兵向该数据库发送命令使其升格为master,
	然后再向其他slave发送命令接受新的master,最后更新数据。
	将已经停止的旧的master更新为新的master的从数据库,使其恢复服务后以slave的身份继续运行。

总结:
	监控master和salve并和其他哨兵进行通信——>master主观下线——>询问其他哨兵
	——>master客观下线——>选举领头哨兵——>按选举规则选举一个salve
	——>领头哨兵发送命令让其升为master——>旧master更新未主master的从库

三,Cluster模式

多主多从,每台redis节点上存储不同的内容。
Redis-Cluster采用无中心结构,它的特点如下:
    所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
    节点的fail是通过集群中超过半数的节点检测失效时才生效。
    客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。
还有一个就是cluster,可以理解为是一个集群管理的插件。
当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,
然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,
通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

为了保证高可用,redis-cluster集群引入了主从模式,
一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。
当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。
如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。

总结:对于这三种模式,如果Redis要存的数据量不⼤,可以选择哨兵模式,如果Redis要存的数据量⼤,并且需要持续的扩容,那么选择Cluster模式。

收藏加关注,再来不迷路!!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis缓存是一种基于内存的高性能键值存储数据库。它常被用作缓存服务器,可以将常用的数据缓存在内存中,以提高应用程序的响应速度。 优点: 1. 快速读取:与传统的磁盘数据库相比,Redis缓存可以更快地读取数据。 2. 高并发:Redis缓存单线程模型能够避免并发问题,同时支持高并发访问。 3. 数据结构丰富:Redis缓存支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等,方便开发人员使用。 4. 分布式:Redis缓存可以实现分布式缓存,提高了系统的扩展性和容错性。 缺点: 1. 内存限制:Redis缓存存储的数据量受限于服务器的内存大小。 2. 数据一致性:Redis缓存中的数据可能会因为故障等原因丢失,需要进行备份和恢复操作。 3. 高并发写入:当Redis缓存中的数据需要频繁更新时,可能会导致性能下降。 Redis缓存支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等。其中,字符串适用于缓存简单的值或对象,哈希表适用于缓存复杂的对象,列表适用于缓存队列等数据结构,集合适用于缓存无序的元素集合,有序集合适用于缓存有序的元素集合。 Redis缓存的分布式实现可以通过一致性哈希算法等方式来实现。一致性哈希算法可以使得数据在多个节点之间均匀分布,提高系统的性能和可靠性。 为了保证Redis缓存的可靠性和数据一致性,可以使用持久化方式来将数据写入到磁盘中,以防止数据丢失。同时,可以设置主从复制,将数据复制到多个节点,提高系统的可靠性。 Redis缓存的过期策略有两种:定时过期和惰性过期。定时过期是指设置一个过期时间,在这个时间之后数据会被自动删除;惰性过期是指在访问数据时检查它是否过期,如果过期则进行删除。可以通过设置过期时间和过期策略来控制Redis缓存中数据的有效性。 Redis缓存持久化方式有两种:RDB和AOF。RDB将内存中的数据周期性地写入到磁盘中,适用于需要快速备份和恢复数据的场景;AOF则将Redis缓存的写操作记录到文件中,适用于需要保证数据一致性和可靠性的场景。 为了优化Redis缓存的性能,可以采用以下方法: 1. 合理使用数据结构,选择适合的数据类型和算法。 2. 设置合理的过期时间和过期策略,避免数据的过期和无效。 3. 使用分布式缓存,将数据分散在多个节点中,提高系统的性能和可靠性。 4. 使用连接池和异步IO等技术,避免因连接和IO造成的性能瓶颈。 为了保证Redis缓存数据库的一致性,可以使用缓存更新策略。当数据库中的数据发生变化时,可以通过订阅数据库更新事件的方式,将更新的数据同步到Redis缓存中,以保证数据的一致性。 为了实现Redis缓存的高可用性,可以使用主从复制和哨兵模式。主从复制可以将数据复制到多个节点,提高系统的容错性;哨兵模式则可以监控Redis缓存的状态,当主节点出现故障时,自动选择新的主节点,保证系统的高可用性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值