Redis|青训营笔记

135 阅读4分钟

Redis|青训营笔记

这是我参与「第五届青训营 」笔记创作活动的第16天

redis是什么

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些[数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

工作原理
  • 数据从内存中读写

  • 数据保存到硬盘上防止重启数据丢失

    • 增量数据保存到AOF文件
    • 全局数据RDB文件
  • 单线程处理所有操作命令

    image-20230216200924782

  • redis启动时会先加载RDB文件(存储了所有键值信息),然后加载AOF文件,看是否有未执行的命令,如果有,就执行

这种机制保证了redis数据不会丢失

Redis应用案例

  • 连续签到

uTools_1676549719066

  • 消息通知
  • 计数

一个用户有多项计数需求,可通过hash存储结构

  • 排行榜

积分变化时,排名要实时更新

结合dict后,可实现通过key操作跳表的功能:

ZINCRBY myzset 2 "Alex"
ZSCORE  myset "Alex"
  • 限流
  • 分布式锁

可以用redis的setnx实现,利用两个特性:

  • Redis时单线程执行命令
  • setnx只有未设置过从才能设置成功

Redis基础

string数据结构—sds
  • 可以存储字符串、数字、二进制数据
  • 通常与expire配合使用
  • 场景:存储计数Session

uTools_1676549932789

list数据结构Quicklist

使用场景: 消息通知

例如文章更新,将更新后的文章推送到ES,用户就能搜索到最新的文章数据uTools_1676550247396

实现: 一个双向链表和listpack

uTools_1676550344908

hash数据结构dict
  • rehash::rehash操作是将ht[0]中的数据,全部迁移到ht[1]中。

    数据量小的场景下, 直接拷贝较快

    数据量大的时候,迁移会明显阻塞用户请求

  • 渐进式rehash:为避免出现这种情况,使用渐进式rehash方案,每次用户访问时都会迁移少量数据

uTools_1676550744619

zset数据结构—zskiplist
  • 查找数字7路径,head, 3, 3, 7
  • 查找数字1路径,head,3, 3, 3, 1
  • 结合dict后,可以实现通过key操作跳表
  • dict作用:存储分值

uTools_1676551124847

uTools_1676551221548

Redis注意事项

大key、热key

大Key定义

uTools_1676551499218

大Key的危害
  • 读取成本高
  • 容易导致慢查询
  • 主从复制异常,服务阻塞,无法正常响应请求
解决方案
KV数据:
  • 拆分

将大key拆分为小key

uTools_1676551782358

  • 压缩(首选)

将value压缩后写入redis,读取时解压后再使用,压缩算法可以是gzip、snappy、lz4等

但一个压缩算法效率高,则解压耗时长

如果是JSON字符串,可以考虑使用MessagePack进行序列化

集合类数据:
  • 拆分:可以hash取余、位掩码来决定放在哪个key中
  • 区分冷热:如榜单列表使用zset,只缓存前十页数据,后续数据走DB

热Key定义

用户访问一个Key的QPS特别高,导致Server示例出现CPU负载突增或不均

uTools_1676552242025

解决方案

设置Localcache

访问Redis前,在业务服务侧设置Localcache,降低访问Redis的QPS

Localcache中缓存过期或未命中,则从Redis中将数据更新到LocalCache

Go中Bigcache就是这类LocalCache

拆分

将key:value这一个热Key复制写入多份,一次将QPS分散到不同实例上,降低负载

代价是需要更新多个Key,存在短期内数据不一致的问题

uTools_1676552515347

使用Redis代理的热Key承载能力

字节跳动的Redis访问代理就具备热Key承载能力,本质是结合了"热Key发现"、"LocalCache"两个功能

uTools_1676552687942

慢查询场景

  • 批量操作一次性传入过多的key/value,尽量不超过100
  • zset大小超过5k以上
  • 操作单个value过大,超过10kB
  • 对大key的delete/expire操作

缓存穿透与缓存雪崩

缓存穿透: 热点数据查询绕过缓存,直接查询数据库

解决方案:

  • 缓存空值
  • 布隆过滤:通过bloom filter算法来存储合法Key,得益于该算法超高压缩率,只需占用极小空间就能存储大量key

缓存雪崩: 大量缓存同时过期

解决方案:

  • 分散过期事件
  • 使用缓存集群