Redis

https://blog.csdn.net/weixin_40205234/article/details/124614720

Redis概述

1.什么是Redis

  • C语言编写的,开源高性能K-V非关系型缓存(内存+磁盘)数据库
  • 基于内存(速度快),同时支持磁盘(持久化)
  • Redis操作是原子性的

2.Redis版本

  • 版本号第二位为奇数非稳定版
  • 版本号第二位为偶数稳定版

3.Redis文件结构

  • redis-server:配置文件
  • redis-cli:连接redis主机
redis -cli -h 主机IP -p 6379 -a 密码
  • redis-benchmark:性能检测
  • redis-check-aof:AOF持久化文件检测及修复工具
  • redis-check-dump持久化文件检测及修复工具
  • redis-sentinel:启动哨兵
  • redis-trib:cluster集群构建工具

4.Redis支持的数据类型

  • String:可存字符串,整型与浮点型,可自增自减,支持二进制位操作,适用共享session,分布式锁,计数器,限流等
  • List:适用粉丝列表,评论列表之类的
    在这里插入图片描述
  • Set:无序且唯一,支持交差并,适用用户标签,随机抽奖等
  • ZSet:有序且唯一,适用于寻找Top,排行榜等
  • Hash:结构化数据,比如对象,适用缓存用户信息等

详细讲解Redis为什么快

1.基于内存

  • 对基于磁盘的数据库来说,数据将被读到内存中,这将受到磁盘IO的限制
  • 对基于内存的数据库来说,数据本身就存于内存,减少磁盘IO开销

2.单线程

  • 单线程不需要考虑加锁带来的问题与开销,如死锁
  • 单线程读写都在一个CPU,避免不必要的上下文切换,但这样的话如果想将闲置CPU使用起来怎么办呢?可以多启动Redis进程,因为Redis不是关系型数据库,不存在数据间的约束,只需要分得清哪些key存于哪个Redis进程即可

官方解释

  • 因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽,既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案

  • 为什么Redis 6.0 之后改多线程
  • Redis几乎不存在CPU成为瓶颈的情况,主要受限于内存和网络
  • Redis使用多线程并非是完全摒弃单线程,Redis还是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程
  • 这样做的目的是因为Redis的性能瓶颈在于网络IO而非CPU,使用多线程能提升IO读写的效率,从而整体提高redis的性能

3.IO多路复用技术

  • 保证多连接时系统高吞吐
  • 多路:多个客户端连接Redis
  • 复用:共用同一个线程
  • 过程大概是客户端发来请求,将这些请求放在一个队列中,Redis服务器从队列中取这些请求处理
  • 多路复用技术主要有三种:select,poll,epoll

4.虚拟内存机制

  • 虚拟内存机制是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)
  • 通过虚拟内存机制实现冷热数据分离,避免因为内存不足而造成访问速度下降的问题

5.高效的数据结构

  • 不同的数据结构设计,使得存储时间复杂度降低
    在这里插入图片描述
  • String:内部存储使用SDS封装
    在这里插入图片描述
  • 字典:字典就是哈希表,通过key就可以直接获取到对应的value,哈希表的特性是O(1)时间复杂度就可以获得对应的值
  • 跳跃表:Redis特有的数据结构,在链表的基础上增加多级索引提升查找效率
    在这里插入图片描述

6.合理的数据编码

  • String
1.数字用int类型的编码
2.非数字小于等于39字节字符串使用embstr,大于39个字节使用raw编码
  • List:列表元素个数小于512,且列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
  • Hash:哈希类型元素个数小于512个,且每个值小于64字节,使用ziplist编码,否则使用hashtable编码
  • Set:集合元素都是整数,且元素个数小于512个,使用intset编码,否则使用hashtable编码
  • Zset:有序集合的元素个数小于128,且每个元素的值小于64字节,使用ziplist编码,否则使用skiplist

详细讲解缓存系列问题

1.缓存穿透

  • 常见的缓存方式是请求时,先查缓存,缓存有则命中,否则去数据库查,数据库有则将值更新到缓存后再返回
  • 缓存穿透指查一个不存在的值时,每次都要查数据库,这会给数据库带来压力,即请求每次都会穿透到数据库
  • 缓存穿透一般发生于一下场景
1.业务不合理的设计:比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护
2.业务/运维/开发失误的操作:比如缓存和数据库的数据都被误删除
3.黑客非法请求攻击:比如黑客故意捏造大量非法请求,以读取不存在的业务数据
  • 如何避免缓存穿透
1.非法请求:在API入口对参数进行校验,过滤非法值
2.查询数据库为空:给缓存设置个空值或默认值,但有写请求的话需要更新缓存,以保证缓存一致性
	最后给缓存设置适当的过期时间
3**.使用布隆过滤器快速判断数据是否存在,即查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查
	布隆过滤器原理:由初始值为0的位图数组和N个哈希函数组成
		对key进行N个hash算法获取N个值,在比特数组中将这N个值散列后设定为1,然后查的时候如果特定的这几个位置都为1,那么布隆过滤器判断该key存在

2.缓存雪崩

  • 缓存雪崩缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至宕机
  • 缓存雪奔一般是由于大量数据同时过期造成的,对于这个原因,可通过均匀设置过期时间解决,即让过期时间相对离散一点,如较大固定值+较小的随机值(5h+0-1800s)
  • Redis故障宕机也可能引起缓存雪奔,这需要构造Redis高可用集群

3.缓存击穿

  • 缓存击穿热点key在某个时间点过期的时候,而恰好在这个时间点对这个key有大量的并发请求过来,从而大量的请求打到数据库
  • 缓存雪崩针对很多key,缓存击穿针对某一热点key
  • 解决方案就有两种
1.互斥锁:缓存失效时,不是立即去加载DB数据,而是先使用某些带成功返回的原子操作命令,如Redis的setnx,成功再去加载数据库数据和设置缓存,否则就去重试获取缓存
2.永不过期:指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间

布隆过滤器

1.概述

  • 布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组Hash映射函数组成,用于检索一个元素是否在一个集合中,空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难

2.原理

  • 假设集合A中有n个元素,利用k个哈希散列函数,将A中的每个元素映射到一个长度为a位的数组B中的不同位置上,这些位置上的二进制数均设置为1
  • 如果待检查的元素经过这k个哈希散列函数的映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A,反之一定不属于集合A

3.案例

  • 假设集合A有3个元素,分别为{d1,d2,d3},有1个哈希函数为Hash1,现在将A的每个元素映射到长度为16位数组B
    在这里插入图片描述
  • 映射d1,假设Hash1(d1)= 2,则数组B中下标为2的格子改成1
    在这里插入图片描述
  • 映射d2,假设Hash1(d2)= 5,则数组B中下标为5的格子改成1
    在这里插入图片描述
  • 映射d3,假设Hash1(d3)= 2,则也将数组B中下标为2的格子改成1
    在这里插入图片描述
  • 因此确认元素dn是否在集合A里,我们只要算出Hash1(dn)得到索引,索引对应元素是0则该元素不在集合A,如果索引下标是1也只能表示该元素可能是A中的某一个元素,因为d1和d3得到的下标值都可能是1,所以布隆过滤器是存在hash碰撞导致的假阳性,判断存在误差的缺点
  • 如何减少这种误差?可以多搞几个哈希函数映射,降低哈希碰撞的概率;同时增加B数组的bit长度,可以增大hash函数生成的数据的范围,也可以降低哈希碰撞的概率
  • 又增加一个Hash2哈希映射函数,假设Hash2(d1)=6,Hash2(d3)=8,使d1和d3不冲突
    在这里插入图片描述
  • 布隆过滤器并没有存放完整的数据,只是运用一系列哈希映射函数计算出位置,然后填充二进制向量,如果数量很大的话,布隆过滤器通过极少的错误率,换取了存储空间的极大节省

热点Key问题

1.什么是热点Key

  • 即访问频率高的key
  • 若热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务
  • 热点key产生于秒杀,热点新闻等场景,或者请求分片集中,超过单台Redis服务器的性能

2.如何解决热点key

  • Redis集群扩容:增加分片副本,均衡读流量
  • 将热key分散到不同的服务器中
  • 使用二级缓存,即JVM本地缓存,减少Redis的读请求

Redis过期键的删除策略

1.常见删除策略

  • 定时删除:设置键的过期时间
  • 惰性删除:放任键过期,当每次从键空间中取键时,都检查键是否过期,若过期则删除该键,若未过期则返回该键
  • 定期删除:每隔一段时间检查一次,删除过期的键

2.Redis采用的过期删除策略

  • 假设Redis当前存放30万个key,并且都设置过期时间,如果每隔100ms就去检查这全部的key,CPU负载会特别高,最后可能会挂掉
  • 因此Redis采取的是定期删除每隔100ms就随机抽取一定数量的key来检查和删除
  • 但是最后可能会有很多已经过期的key没被删除,这时候Redis采用惰性删除
  • 获取某个key时Redis会检查这个key,如果设置了过期时间并且已经过期就会删除
  • 但是如果定期删除和惰性删除都漏掉很多过期的key,还是会有很多过期key积在内存,直接会导致内存爆满,或者业务量大起来了,Redis的key被大量使用,内存直接不够,运维小哥哥也忘记加大内存,难道redis直接这样挂掉?不会的!Redis用8种内存淘汰策略保护自己~

3. Redis 内存淘汰策略

  • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰
  • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰
  • volatile-lfu:当内存不足以容纳新写入数据时,在过期的key中使用LFU算法进行删除key
  • allkeys-lfu:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LFU算法进行淘汰
  • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中随机淘汰数据
  • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中根据过期时间进行淘汰,越早过期的优先被淘汰
  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错

Redis常用应用场景

1.缓存

  • 合理的利用缓存,比如缓存热点数据,不仅可以提升网站的访问速度,还可以降低数据库DB的压力
  • 并且Redis提供持久化机制与多种数据结构,强的一批

2.排行榜

  • Redis提供的zset数据类型能实现复杂的排行榜
  • 比如用户每天上传视频,获得点赞的排行榜可以这样设计
1.用户Jay上传一个视频获得6个赞
	zadd user:ranking:2021-03-03 Jay 6 ->(Jay 6K V)
2.过了一段时间再获得一个赞
	zincrby user:ranking:2021-03-03 Jay 1
3.如果某个用户John作弊,需要删除该用户
	zrem user:ranking:2021-03-03 John
4.展示获取赞数最多的3个用户
	zrevrangebyrank user:ranking:2021-03-03 0 2

3.计数器

  • 短视频的播放数、电商网站的浏览数都要用到计数器
  • 这些数据一般要求都是实时的,如果并发量很大对于传统关系型数据的性能是一种挑战
  • Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择

4.共享Session

  • 分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录
  • Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取

5.分布式锁

  • 分布式服务下遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景,用synchronize或者reentrantlock本地锁肯定是不行
  • 并发量不大话使用数据库的悲观锁、乐观锁来实现没啥问题
  • 但是在高并发场合中,利用数据库锁来控制资源的并发访问,会影响数据库的性能
  • 实际上,可以用Redis的setnx来实现分布式的锁

6.社交网络

  • 赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能
  • 由于社交网站访问量通常比较大,而且传统的关系型数据不太适保存这种类型的数据,Redis提供的数据结构可以相对比较容易地实现这些功能

7.消息队列

  • 消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件
  • 主要用于业务解耦、流量削峰及异步处理实时性低的业务
  • Redis提供发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统

8.位操作

  • 用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等
  • 腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?千万别说给每个用户建立一个key,然后挨个记
  • 使用setbit、getbit、bitcount命令
  • 原理是redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示用户id(必须是数字),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统

Redis的持久化机制

1.持久化加载流程

  • Redis基于内存,为了避免数据丢失则将数据保存到磁盘
    在这里插入图片描述

2.RDB(默认)

  • 按一定的时间(save参数配置)将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb
    在这里插入图片描述
  • RDB适合大规模的数据恢复场景,如备份,全量复制等
  • RDB是隔一段时间存储,可能在这段时间内Redis发生故障,导致数据丢失,数据安全性低,无法做到秒级持久化,更适合对数据要求不严格的场景
  • 生成RDB期间,Redis 可以同时处理写请求么?
可以,Redis提供两个指令生成RDB,分别是save和bgsave
	save指令会阻塞,因为是主线程执行的
	bgsave指令是fork一个子进程来写入RDB文件的,快照持久化完全交给子进程来处理,父进程则可以继续处理客户端的请求

3.AOF

  • 将Redis每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化日志中文件恢复数据,默认关闭,主要解决持久化实时性问题
  • AOF的工作流程
    在这里插入图片描述
  • 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
  • 通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题
  • 数据的完整性和一致性更高,但随着时间的推移,文件越大数据恢复越慢
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值