• Home
  • Archives
  • 随笔
所有文章 友链 关于我

  • Home
  • Archives
  • 随笔

Redis技术要点

发布于: 2024-05-19
更新于: 2024-05-19

Redis 高效的内存数据库

  • 为什么 redis 那么快
    • 纯内存模型
    • 基于 Reactor 模式设计了时间处理,依赖了单线程事件循环和 IO 多路复用实现
    • 内置了大量优化的数据结构实现
  • 支持丰富的数据结构
  • 有灾备机制
  • 有集群模式
  • 有发布订阅模式,能做消息订阅,但没必要
  • Lua 脚本、事务功能
  • 支持惰性删除以及定期删除

缓存读写模式

旁路缓存 CacheAside

image.png

  • 对于这个情况,如果要更新数据怎么实现【注,这里场景是指没有分布式锁】
    • 可以先删除 cache 再更新 DB 吗?
      • 不行,因为如果先删除 cache,那么正在写数据的事务,还没有更新到 DB 上,但别的读事务读的数据还是老的
    • 那么反过来呢?先更新 DB 再删除 cache 呢?
      • 其实也会有问题,只是因为短暂时间内 cache 会比 DB 块,所以最好的实现方式还是加分布式锁,把写的动作解耦开
      • 并且提前对热点 key 进行缓存

读写穿透

  • 都是操作 cache 先,如果不存在,则查询 DB 里的数据进行回写
    image.png

异步缓存写入

  • 只操作缓存数据,DB 异步写入
  • 适合数据经常变化,但一致性要求较低的,比如点赞数、游览数场景

技术实现

技术架构

redis 单线程模型

  • 基于 Reactor 模式开发事件处理机制,Netty 也是 - 简单来说就是为了解决 IO 阻塞等待调用,以及占用资源的问题 - 通过一个服务分发中间层,将 client 的请求转发给服务端 - 大白话解释就是 - 餐厅有多个客人,前台点菜的服务员是一个,而后面做饭的厨师有多个【当然随着业务复杂,这里面的服务员和厨师可以是 1,也可以是 n】
    image.png

  • Redis 中基于 file event handler 实现,而这个是单线程实现

    • 基于 IO 多路复用,监听多个套接字 socket,并根据目前执行的任务,关联不同的任务处理器
    • 多个 socket 链接
    • 文件事件分发机制
    • IO 多路复用程序
    • 事件处理器【连接应答、命令请求、命令回复】
  • 好处 - 不需要创建多于的线程监控大量的连接,降低损耗 - 处理上是单线程,也避免了线程的上下文切换损耗 - 备注 6.0 后引入了“多线程”,主要是解决 IO 返回数据,增加数据解析协议以及返回数据的内容,但核心处理数据线程依旧是单线程的
    image.png

  • 除了这项还有什么线程在跑的

    • 比如 AOF、RDB
    • 惰性删除
    • fsync 刷盘等
  • 需要操作内容 - IO 处理 - 键值读写 - 网络 iO 传输
    image.png

redis 内存实现

  • 缓存时间
    • 避免内存打爆了,及时清理不需要使用的 cache
  • 怎么识别是否过期呢
    • redis 维护了一个过期字典,可以理解为 hash 表,保存数据的过期时间,UNIX 时间戳
typedef struct redisDb {
    ...

    dict *dict;     //数据库键空间,保存着数据库中所有键值对
    dict *expires   // 过期字典,保存着键的过期时间
    ...
} redisDb;

image.png

删除机制实现

  • 惰性删除
    • 获取 key 的时候判断是否过期
  • 定期删除
    • 定期删除,定期扫描 key 实现
  • 怎么保证能及时删除不需要使用的内存,通过内存淘汰机制
    • lru 最少最近使用
    • ttl 挑选要过期淘汰
    • random 从已过期的数据删除
    • lru 从最近最少用的 key 删除,最常用
    • random 随机
    • no-eciction OOM 时报错,禁止写入,几乎没人用

事务

MULTI
     SET xx xxx
EXEC
DISCARD
WATCH
  • redis 的事务不支持回滚,也就是不满足 ACID 中的 A 原子性
  • 认为没必要保证这个内容
  • 事务是支持备份的,rdb 与 aof 都支持
  • 通过 lua 脚本实现原子性功能
    • 通过 lua 脚本将多条命令一次传输,减小网络开销
    • 脚本可以作为一条命令执行,保证不会被其他命令插队
    • 同时,如果脚本中间执行报错了,也是不会实现回滚的,所以并不能完全实现原子性操作

基础数据

image.png

  • 数据结构
    • String key-val
      • 实现场景:库存,微博数量,信息存储
    • hash
      • hashmap 的实现
      • 可以用来做有主体的数据缓存,比如用户信息,商品信息
    • list
      • 消息列表,关注列表,公告栏列表
      • 可以通过 lrange 实现分页查询功能
    • set
      • 实现去重的 list 功能,做一些共同关注,共同粉丝等等
      • 或者说做一些防重的 key
    • sortedset
      • 增加 score 打分机制,可以实现礼物排行榜,弹幕等功能
  • 底层实现
    • sds simple dynamic String 除了用作字符串实现外,还作为 aof 缓冲区,用户输入缓冲区
      • 记录 len
      • free 未使用字节
      • char 数组 buf 保存字符串
      • o1 获取长度,通过 free 以及长度可以更好减少内存重分配次数
    • 链表 除了 list 结构外还用于发布,订阅,监视器,客户端状态等等
      • 双向,无环
      • 表头指针,表尾指针
      • 链表长度计数器
      • 支持多态
    • 字典
      • 哈希键值底层实现之一,以及是数据库的增伤改查的实现
      • 哈希表
      • 表大小,used 已用节点

批量操作

  • 通过批量操作优化网络开销
  • MGET 批量获取数据
  • MSET 批量设置数据
  • lua 脚本

BIGKey 优化

  • 定义
    • string 超过 10kb
    • 复合元素超过 5000 个
  • 危害
    • 网络、内存占用大
    • 可能造成阻塞
  • 扫描
    • 通过 bigkeys 命令查询,scan 所有的 key 进行查询
  • 解决方案
    • 拆分大 key,打散存储
    • 手动清理
    • lazy-free 机制

hotKEY 问题

  • 如果访问过高,可能导致节点压力过大,阻塞 redis
  • 通过 hotkeys 查询
  • 提前预估是否存在热点 key
  • 解决方案
    • 二级缓存,JVM 本地
    • 读写分离,从节点读取
    • 打散数据
    • redis cluster 实现
    • cluster 搭配 65535 个槽再打散分配

慢查询

  • On 的命令
    • KEYS 可能导致阻塞
    • HGETALL 量大
    • LRANGE
    • SMEMBERS

内存碎片

  • 存储数据时申请的大于实际大小
  • 频繁修改数据时,或者删除数据,也会导致碎片,因为这部分的内存也不会返回给操作系统
  • 重启节点可恢复、或者通过自动内存碎片整理实现
    Redis 内存碎片详解 | JavaGuide

持久化实现

  • AOF append only file
  • RDB redis database snapshot 快照
  • RDB 与 AOF 混用
  • RDB
    • 通过 save、bgsave 的形式创建快照,获取相关数据
    • 压缩的二进制文件,保存所有的数据文件,类似于 binlog
    • RDB 大数据恢复时会比 AOF 快
  • AOF - 开启 AOF 后,每一条命令都会写入到 AOF 缓冲区,再写到 AOF 文件中,根据 fsync 刷盘策略决定是否同步磁盘 - 同时是在执行完命令后进行重写 - 1. 避免额外检查开销,aof 的日志不会检查语法 - 2.命令执行完再记录,就不会阻塞命令的执行 - 流程 - append 写命令到 AOF 缓冲区 - write 缓冲到文件 - fsync 持久化硬盘 - rewrite 重写文件压缩命令 - load 重启宕机回复
    image.png
  • AOF 重写 - AOF 重写是子进程中执行 - 还会有 AOF 重写缓冲区,在创建新的 AOF 文件期间,记录所有的写命令 - 当子进程完成创建新的 AOF 文件创建后,将重写缓冲区的文件追加到新的 AOF 文件后面 - 然后再用新的 AOF 文件替代旧的,保证与数据库的状态一致
    image.png

宕机恢复

AOF 校验机制

  • redis 启动时,校验 aof 文件,确认是否完整,通过 checksum 校验和的机制实现
  • 如果发现文件不完整,则拒绝启动并报错;RDB 也有类似的机制
  • 这块的原因是因为写入时,没有校验是否完整,是弱依赖的实现

场景题

购物车怎么存储

  • 比如可以用 hash 结构,用户 ID 是它的 key,map 就是商品 id 和加购的商品数量

排行榜怎么做

  • 通过 sorted set 实现
  • 通过 zrange、zrevrange 实现正序和倒序排序,以及 zrevrank 指定元素排名

统计活跃用户怎么实现

  • 通过 bitmap 实现,布谷鸟逻辑
  • bitap 存储的是一个连续庞大的 01 二进制 bit 位
  • key 为日期,value 为当天统计的人数
  • 查询多天的数据
    • bitop and desk1 20240518 20240520
    • bitcount desk1 查询出来总人数

缓存穿透

  • redis 没有数据,将 DB 打爆了
  • 解决方案 - 提前缓存无效 key,直接返回 - 布隆过滤器 - 先判断用户在不在范围内,再决定要不要往下走
    image.png

缓存击穿

  • 缓存过期了,打爆了 DB
  • 解决方案
    • 热点 key 不失效
    • 提前预热热点 key 数据
    • 查询数据库的操作增加互斥锁,减少流量访问压力

缓存雪崩

  • 缓存大量失效
  • 解决方案
    • key 失效时间随机化
    • 查询增加互斥锁
    • 二级缓存机制

统计亿级 keys 场景

统计页面 UV

  • 可以考虑使用 hyperLogLog
  • 每个 HyperLogLog 只花费 12KB 内存,就能存储 2^64 元素
  • PFADD page1:uv user1 user2 user3
  • PFCOUNT page1:uv

如何解决数据倾斜

  • 原因
    • bigKey
    • Slot 分配不均
    • Hash tag 倾斜
      • 是指 key 里面的花括号{},计算花括号里面的 crc16 来算 key
  • 解决方案
    • 打散 key
    • 可以手动迁移实例上的 slot 到其他节点上 cluster setslot xx importing 实例 id

如何保证秒杀场景

  • 支持高并发查询
    • 先通过 CRC 算法计算不同 key 对应的 slot,然后将这些打散到均匀的 key 上
    • 保存库存校验以及扣减的原子性执行
#获取商品库存信息
local counts = redis.call("HMGET", KEYS[1], "total", "ordered");
#将总库存转换为数值
local total = tonumber(counts[1])
#将已被秒杀的库存转换为数值
local ordered = tonumber(counts[2])
#如果当前请求的库存量加上已被秒杀的库存量仍然小于总库存量,就可以更新库存
if ordered + k <= total then
    #更新已秒杀的库存量
    redis.call("HINCRBY",KEYS[1],"ordered",k)                              return k;
end
return 0

参考文档

  • Reactor 模式 - 知乎
  • Redis 常见面试题总结(上)
  • 10 第 1 ~ 9 讲课后思考题答案及常见问题答疑
Redis技术要点
/archives/ea18a0c8/
作者
tyrantqiao
发布于
2024-05-19
更新于
2024-05-19
许可协议
CC BY-NC-SA 4.0
赏

蟹蟹大佬的打赏,大家一起进步

支付宝
微信
  • 面试
  • 技术
  • Redis

扫一扫,分享到微信

微信分享二维码
jvm技术要点
RocketMQ技术要点
© 2024 tyrantqiao 本站总访问量次 本站访客数人次 载入天数...载入时分秒...
  • 所有文章
  • 友链
  • 关于我

tag:

  • 复盘
  • 我
  • 规划
  • java
  • 面试
  • 源码
  • 架构
  • Hadoop
  • HTTP
  • TCP
  • 学习笔记
  • IDEA
  • maven
  • idea
  • Java
  • jdk
  • 面经
  • linux
  • 爱情
  • mysql
  • 性能
  • sql
  • Mysql
  • JAVA
  • 技术
  • Redis
  • MQ
  • Spring
  • 数据库
  • TIDB
  • spring
  • unity
  • chatgpt
  • 经验分享
  • 前端
  • redis
  • vue
  • git
  • shadowsocks
  • hexo
  • blog
  • bug
  • 开发
  • 业务
  • jvm
  • 算法
  • MySQL
  • nginx
  • Linux
  • mq
  • db
  • springCloud
  • ssh
  • python
  • 爬虫
  • test
  • vim
  • 影视剧
  • 中间件
  • 事务
  • 性格
  • 音乐
  • 程序员
  • 随笔
  • mybatis
  • 演讲
  • 域名
  • 猫咪
  • 她
  • github
  • 计划
  • 旅游
  • 软件
  • 心理
  • 情商
  • 幽默
  • 才艺
  • 穿搭
  • 编程
  • 排序
  • 查找
  • 缓存
  • 网络
  • 设计模式
  • c
  • 课程设计
  • centos
  • 数学
  • 本网站主题yilia设计者的主页
如果有问题或者想讨论的可以联系[email protected]或者[email protected]