Redis 总结

本文详细介绍了Redis的内部数据结构如跳跃表、Bitmap、HyperLogLog等,以及其在UV浏览量统计、分布式锁等场景的应用。探讨了Redis的高可用架构,包括主从复制、Sentinel哨兵和Cluster集群。此外,还讨论了Redis的持久化策略、性能优化及应对缓存穿透、击穿、雪崩的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

1.简介

 

高可用,高性能 吞吐量能达到10W级别的nosql缓存中间件。

 

为什么单线程还那么快?

 

因为他底层用的是单线程io多路复用,也就是说 多个io请求 复用 一个线程,主线程 会把多个io放进一个任务队列,由一个任务分发器分发给工作线程进行io,这样批量处理io请求,所以它的性能是很快的

 

2.数据结构 以及应用场景

String  存储字符串 计数,提供了原子性操作

map  存储对象  session 

list   集合 队列  列表(先进先出)  文章列表 好友列表

set  不可重复无序,可以统计点赞列表、关注列表,网站ip访问量

zset  有序不可重复 , 加了个分数 用来排序, 排行榜

zskiplist 跳跃表:多层级链表,写入时随机获取层级,高层级的数据也会写入低层级,查询时会从高层级开始查询,没有查到就降低层级从该节点继续查询,提高查询效率,空间换时间

bitmap 数据结构就是内存中连续的二进制位(bit)并且每个bit位初始值都是0,数据hash后的值转成bit位 存在在bit  用户签到,布隆过滤器: 通过key算bit的占位是否存在,如果bit没有命中那么这个key就没有保存过,如果命中 则说明 这个key 有可能被保存过
布隆过滤器

hyperloglog   不精确统计 统计uv浏览量

1.内部结构是有固定的16384个桶

2.通过对值进行hash 选取低14位 计算出 选的哪个桶

3.再通过15位开始数0的个数,这个个数 大于 桶当前的值 就更新为桶的值 返回1

UV浏览量统计

ziplist

高级用法:

1.pipeline 批量执行命令,减少网络开销

2.lua脚本,串行执行一系列命令,锁,秒杀场景  nginx + lua 直接访问redis(库存获取 和 库存扣减)

3.事务:最后一个功能是事务,但 Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去。

 

 

3.高可用 架构

1.单例

2.主从

3.主从+sentinel哨兵

4.cluster集群  多master节点,集群slave冗余机制:防止master 和 slave 都挂掉,没有slave切换

 

4.主从复制 + 心跳 + 持久化

matser 10S一次心跳,slave 1S一次心跳 

主从复制是异步的:

1.首次由slave发起psync命令给master

2.master收到消息,根据本地offset 发送消息给slave

3.首次请求会触发全量复制(fork进程来拷贝数据,引起性能抖动),后续就是增量复制(由master 主动发起异步复制)

4.复制超时时间默认60S 超时会认为复制失败

持久化:

rdb快照:将内存数据生成快照替换旧的快照文件,触发机制 60 300 900s,10000 10 1 操作,有可能造成数据丢失(不能优先作为数据恢复方案),持久化fork紫禁城来io操作如果数据文件太大会导致客户端服务暂停,恢复数据速度快,数据文件小

优化快照生成影响性能:控制redis实例内存大小10G以内,部署多master

aof文件追加:每次操作就追加一条操作日志,先写缓存中每秒刷入日志文件,顺序写性能好,丢失数据比较少,但数据文件大并且写入性能会比rdb差,数据恢复慢

rewrite机制:清除冗余操作日志,保存最后一条操作记录即可,文件太大就会触发,不让他频繁触发 可以配置增量size或者百分比

两种一起使用,rdb每小时 每天 做数据备份发送云端,aof保证数据不丢失,如果aof文件丢失或者损坏 可以用fix命令修复,无法使用就用rdb备份

 

5.sentinel 机制 (sentinel高可用 和 选举)

sdown :主观宕机, 如果一个哨兵ping一个master(每秒一次),超过了down-after-milliseconds指定的毫秒数之后,就主观认为master宕机 

odown :客观宕机,如果quorum数量的哨兵都觉得一个master宕机了,那么就是客观宕机

 

qourum机制:大于半数的sentinel实例都判断为宕机 才可以认定为宕机,所以sentinel数量要3台

majority机制:必须要有超过半数都运行选举了,才可以选举master,会由某个哨兵了选一个slave进行主备切换

 

6.cluster 机制 (扩容、集群部署 树状 、slave冗余)

宕机判断:节点内部会彼此互发心跳,半数以上通过就认定节点宕机

树状 部署主从 避免引起大量主从复制 占用带宽

集群slave冗余:保证集群高可用

7.LRU机制

noeviction(不驱逐):当内存使用超过配置的时候会返回错误,不会驱逐任何键

allkeys-lru(最久没使用):加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键

volatile-lru(过期中最久没使用):加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键

allkeys-random(随机):加入键的时候如果过限,从所有key随机删除

volatile-random(过期中随机):加入键的时候如果过限,从过期键的集合中随机驱逐

volatile-ttl(马上过期):从配置了过期时间的键中驱逐马上就要过期的键

volatile-lfu(过期中最少使用):从所有配置了过期时间的键中驱逐使用频率最少的键

allkeys-lfu(最少使用):从所有键中驱逐使用频率最少的键

 

8.性能优化

1.多master

2.fork进程(rdb生成,rewrite),一般控制redis的内存在10GB以内

3.主从复制风暴问题 (树状结构)带宽占用严重和内存空间

4.最大打开文件句柄

5.Master部署要用树状不要用星状

6.保持服务器空闲内存,持久化(rewrite) 和 同步 会消耗 内存 和 cpu 资源,引起性能抖动

7.避免在主库压力大 加过多从库,因为要同步

 

9.数据恢复、备份、迁移方案

备份:

(1)写crontab定时调度脚本去做数据备份
(2)每小时都copy一份rdb的备份,到一个目录中去,仅仅保留最近48小时的备份
(3)每天都保留一份当日的rdb的备份,到一个目录中去,仅仅保留最近1个月的备份
(4)每次copy备份的时候,都把太旧的备份给删了
(5)每天晚上将当前服务器上所有的数据备份,发送一份到远程的云服务上去

恢复:

(1)默认直接基于AOF日志文件恢复数据,如果AOF文件破损,那么用redis-check-aof fix 恢复
(2)如果redis当前最新的AOF和RDB文件出现了丢失/损坏,那么可以尝试基于该机器上当前的某个最新的RDB数据副本进行数据恢复
(3)如果你同时开启aop 和 rdb ,用RDB副本恢复会失败,因为不会进行rdb数据文件恢复,并且每次重启都会选择aof文件恢复, 所以 必须关掉aof后再重启,这样就会用rdb文件恢复,然后再用热修改 config set 开启aof。

迁移:https://www.cnblogs.com/xingxia/p/redis_migration_tool.html 

 

1.主从进行全同步期间,如果主库此时有expire 命令,那么到从库中,该命令将会被延迟执行  (过期时间不一致)
解决:1.用 expireat 做过期时间 2.对过期时间不敏感 其实也可以不用处理
expire/pexpire/setex/psetex 命令在复制到从库的时候转换成时间戳的方式,比如expire 转成expireat命令,setex转换成set和expireat命令
2.使用Redis-Shake迁移数据 https://www.cnblogs.com/caoweixiong/p/14239315.html
配置Redis-Shake(源集群和目标集群的Master节点和IP)
在线迁移(实时同步数据)
命令 :./redis-shake -type sync -conf redis-shake.conftail -1000f /var/log/redis-shake.log
离线迁移(备份文件导入)
导出RDB文件
./redis-shake.linux -type dump -conf redis-shake.conf
、使用以下命令把RDB文件导入到目标集群
./redis-shake.linux -type restore -conf redis-shake.conf
迁移后验证  
通过info命令查看Keyspace中的Key数量 (info keyspace),确认数据是否完整导入。
检查对比源端集群和目标集群中的每一个master节点的keys数量是否一致。
如果数据不完整,可使用flushall或者flushdb命令清理实例中的缓存数据后重新同步。
3.move迁移(move迁移有个弊端就是会删除源库的所有key迁移到目标库中)
4.migrate迁移 migrate迁移不会删除原有的key并且迁移到目标库中
3 和 4 都是离线 模式下 迁移
5.通过一个自建的redis 做中间 转移  然后 在迁移到 最终的 redis上
6.如果是阿里云上的redis 是有迁移工具 Redis-Shake
7.执行 save\bgsave 触发数据持久化 RDB文件
拷贝redis备份文件(dump.rdb)到目标机器
重启目标实例重新load RDB 文件
redis-full-check校验数据
 

10.主从不一致  1.强制读主(选择性部分业务)

 

https://www.yuque.com/jdxj/3456fg/imsh2t

https://blog.youkuaiyun.com/qq_41373681/article/details/107605756

 

Redis 最普通的分布式锁

第一个最普通的实现方式,就是在 Redis 里使用 SET key value [EX seconds] [PX milliseconds] NX 创建一个 key,这样就算加锁。其中:

  • NX:表示只有 key 不存在的时候才会设置成功,如果此时 redis 中存在这个 key,那么设置失败,返回 nil
  • EX seconds:设置 key 的过期时间,精确到秒级。意思是 seconds 秒后锁自动释放,别人创建的时候如果发现已经有了就不能加锁了。
  • PX milliseconds:同样是设置 key 的过期时间,精确到毫秒级。
  •  
  • 比如执行以下命令:
  • SET resource_name my_random_value PX 30000 NX

普通锁的问题:

1.为啥要用 random_value 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 lua 脚本来释放锁。

2.因为如果是普通的 Redis 单实例,那就是单点故障。或者是 Redis 普通主从,那 Redis 主从异步复制,如果主节点挂了(key 就没有了),key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key,从而拿到锁。

 

RedLock 算法  

这个场景是假设有一个 Redis cluster,有 5 个 Redis master 实例。然后执行如下步骤获取一把锁:

  1. 获取当前时间戳,单位是毫秒;
  2. 跟上面类似,轮流尝试在每个 master 节点上创建锁,超时时间较短,一般就几十毫秒(客户端为了获取锁而使用的超时时间比自动释放锁的总时间要小。例如,如果自动释放时间是 10 秒,那么超时时间可能在 5~50 毫秒范围内);
  3. 尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点 n / 2 + 1 ;
  4. 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
  5. 要是锁建立失败了,那么就依次之前建立过的锁删除;
  6. 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁

 

因为redis自己原理机制是主从异步复制所以比较麻烦 还要基于算法

zookeeeper 是强一致性 所以就没有那么多问题 ,需要复杂的机制去实现,逻辑更加简单  健壮

 

zk 分布式锁

zk 分布式锁,其实可以做的比较简单,就是某个节点尝试创建临时 znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。释放锁就是删除这个 znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。

有两种实现方式:

1.全部监听锁节点,以前比较早期都是这么实现,监听的请求太多 会引起惊群效应,会并发争抢,性能比较低

2.按节点大小顺序监听排在自己前面的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知

但是,使用 zk 临时节点会存在另一个问题:由于 zk 依靠 session 定期的心跳来维持客户端,如果客户端进入长时间的 GC,可能会导致 zk 认为客户端宕机而释放锁,让其他的客户端获取锁,但是客户端在 GC 恢复后,会认为自己还持有锁,从而可能出现多个客户端同时获取到锁的情形。

 

redis 分布式锁和 zk 分布式锁的对比

  • redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
  • zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。

另外一点就是,如果是 Redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。

Redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等......zk 的分布式锁语义清晰实现简单。

 

Redis 跳跃表(zset 数据结构)

 

是有序的,所以需要一个hash结构来存储value和score的对应关系,另一方面需要提供按照score来排序的功能,还能够指定score的范围来获取value列表的功能,上述也就是跳跃表要实现的功能

 

 

  1. zskiplist
    • 一个跳跃表由多个跳跃表节点组成
    • header:跳跃表的表头节点
    • tail:指向跳跃表的表尾节点
    • level:记录目前跳跃表内,层数最大的那个节点的层数比如tail的层数为L5
    • length:记录目前跳跃表的长度,目前3个跳跃表节点则length=3
  2. zskiplistNode
    • Level[] :标记L1 L2 L3 L4标记节点的各个层
    • BW:backward 后退指针,通过tail遍历bw为空为止即为header
    • 前进指针:level[i].forward 从表头向表尾方向访问节点
    • score:各个跳跃表中1.0 2.0 3.0,即通过查找时层的跨度,比如从L5层通过前驱指正找到尾节点跨度为3
    • obj:成员变量,存储是一个指针,指向一个字符串对象,而字符串对象保存的是一个SDS值

总结:   空间换时间

1.多层级的链表结构存储数据,随机层级存储,高层级的数据 底层级的链表也会保存

2.查询以高层级链表先查,没有查询到根据高层级的节点 再往 下一层级的那个节点继续查询

场景:

  • 延时队列:当队列中消费失败后放入到延时队列中
  • 分页排序:比如拉取消息时根据上一次的消息id来进行拉取
  • 时间磋增量查询:根据时间磋来判断是否有新的消息

 

Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,跳跃表按score从小到大保存所有集合元素。使用跳跃表的结构可以获得比较高的查找效率

 

HyperLogLog

1.内部结构是有固定的16384个桶

2.通过对值进行hash 选取低14位 计算出 选的哪个桶

3.再通过15位开始数0的个数,这个个数 大于 桶当前的值 就更新为桶的值 返回1

UV浏览量统计

 

Bitmap

通过key算bit的占位是否存在,如果bit没有命中那么这个key就没有保存过,如果命中 则说明 这个key 有可能被保存过
布隆过滤器

 

热点key

1.将对于热点请求的数据进行,一主多从+哨兵的redis集群架构,有多个从主机负责处理读请求。 读写分离

 

2在redisCluster集群架构下,利用flink,storm这样的实时计算技术,对这SKU进行热点感知,只要是热点品就将数据同步给多台master,并改变lua脚本的执行请求路由的策略

 

 

缓存穿透  穿过去了-- 缓存中 没有这个key

1.设置空值

1.1升级 然后在去数据库查询出值 在更新回去

2.常用方案 布隆过滤器(key 用hash函数 算的哈希值 就是指向bit的位置 bit位默认是0 被指向后更新成1)

(如果布隆过滤器判断元素在集合中存在, 不一定存在.,如果布隆过滤器判断不存在, 则一定不存在.) guava实现,redis Bitmaps  实现

 

 

 

缓存击穿  高并发访问的时候突然 key 失效

 

访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间

 

1.解决方式也很简单,可以将热点数据设置为永远不过期;

2.针对个别热点key ,如果查询缓存不存在就基于 redis or zookeeper 实现互斥锁互斥锁 setnx,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。阻塞的线程 休眠50毫秒后 重写获取数据(获取数据空 再尝试 获取锁 )

3.保存热点key到本地内存

4.缓存过期监听 redis自带功能

5.二级缓存 缓存过期时间和 一级缓存过期时间错开

6.定时任务 缓存重建 或者 重置过期时间

 

缓存 db 一致性方案

 

1.访问key 都加锁

2. 更新db 在更新缓存      问题: 缓存更新失败  数据不一致

3. 双删: 先删除缓存  更新db  在删除或者更新缓存   问题:存在并发问题 的数据不一致

4.缓存过期机制: 更新db,设置缓存过期时间

5.canal 监听 binlog  根据binlog 去更新 缓存

6.本地队列  所以操作 串行执行

 

缓存雪崩解决方案

1、事前解决方案  (吞吐不够,集群来凑)

吞吐不够被打崩:集群Redis Cluster 分布式存储缓存 加节点, 双机房

2、事中解决方案  (多级缓存 + hystrix 限流 降级(可以用队列和发布订阅,通知系统 设置本地缓存))

ecache 二级缓存

redis 限流 降级(独立的缓存服务) hystrix 线程隔离 保护其他业务的redis 正常, 线程不被占用, tomcat 没有资源 接收 服务 导致 整个服务挂掉

针对热点key 做限流 令牌桶 或者 漏桶

3、事后解决方案(数据恢复和预热

(1)redis数据可以恢复,做了备份,redis数据备份和恢复,redis重新启动起来

(2)redis数据彻底丢失了,或者数据过旧,快速缓存预热,redis重新启动起来

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值