侧边栏壁纸
博主头像
Haenu的Blog 博主等级

坚持学习,慢慢进步!

  • 累计撰写 35 篇文章
  • 累计创建 10 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Redis笔记

Haenu
2024-06-24 / 0 评论 / 0 点赞 / 59 阅读 / 0 字

Redis的数据类型

  1. String

  2. Hash

  3. Set

  4. List

  5. Zset

  6. HyperLogLog

  7. Bitmap

  8. Geo

底层结构

String

底层用了简单动态字符串 SDS 的抽象类型。

List

List 类型的底层数据结构是由双向链表或压缩列表实现的:

  • 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;

  • 如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;

但是在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表

Hash

Hash 类型的底层数据结构是由压缩列表或哈希表实现的:

  • 如果哈希类型元素个数小于 512 个(默认值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字节(默认值,可由 hash-max-ziplist-value 配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;

  • 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的底层数据结构。

Set

Set 类型的底层数据结构是由哈希表或整数集合实现的:

  • 如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构;

  • 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。

Zset

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

  • 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;

  • 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;

数据类型的应用场景

String:

序列化之后的对象 图片路径 token 计数简单限流 分布式锁

List

最新文章、最新动态。

Hash

用户信息、商品信息、文章信息、购物车信息。

Set

文章点赞 共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集

抽奖系统、随机点名等场景

Sorted Set

  • 各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。

  • 相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)


Bitmap

Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。

你可以将 Bitmap 看作是一个存储二进制数字(0 和 1)的数组,数组中每个元素的下标叫做 offset(偏移量)。

HyperLogLog(基数统计)

  • 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计、

  • 相关命令:PFADD、PFCOUNT 。

先删除缓存还是先修改数据库呢?

先删除缓存,再更新数据库:存在主要问题不是更新数据库失败,导致读到老数据,
是并发情况下删除缓存后,立刻读库,此时库数据还没更新,读到了老数据,又写入了缓存,导致以后所有人读到都是缓存中错误数据

先更新数据库,再删除缓存:存在主要问题是,更新数据库后,还没删除缓存,导致部分人读到了缓存中老数据,当缓存删除后,就可以从数据库读取正确数据,缓存也就正确,以后大部分人读到正确数据

删除缓存失败怎么办?

  1. 消息队列保证 key 被删除
    可以引入消息队列,把要删除的 key 或者删除失败的 key 丢尽消息队列,利用消息队列的重试机制,重试删除对应的 key。

  1. 数据库订阅+消息队列保证 key 被删除

  2. 设置缓存过期时间兜底

缓存三兄弟

缓存穿透

原因:

查询一个不存在的数据,mysql查询不到数据也不会写入到Redis中,就会导致每次的请求都会发送到mysql中。

解决方式:

方式一:缓存空数据

缓存一个空数据,查询如果返回的结果为空,我们就把这个空结果进行缓存

缺点:容易发生数据不一致的问题,如果我们之后在数据库中继续添加那个数据后,我们的redis中的数据还是为null,此时就造成了缓存空数据


方式二:布隆过滤器

布隆过滤器就是以bit为单位的数组 不是0就是1 经过多次的hash变换

然后将数组中的位置的0置为1


但是此时此刻就会产生误判,如果再来一个不存在的数据,那么经过hash变换后我们的数组如果都是1的话 就证明

在布隆过滤器中可以设置误判率 一般设置百分之5就行

方式三: 接口限流

缓存击穿

原因:热点key

给某一个key设置的过期时间 如果此时大量的请求对这个key的时候,这些并发的请求就会把DB压垮

解决办法:互斥锁/逻辑过期

方式一:互斥锁(分布式锁)

获取到互斥锁后进行缓存重建

强一致性 性能较差(money业务!!)

方式二:逻辑过期

不设置过期时间

查询缓存发现过期了-->获取互斥锁(一个线程进行重建即可) --> 新开一个线程进行缓存重建 重建完进行互斥锁的释放 -->返回一个过期数据

高可用 性能好 一致性较弱

缓存雪崩

原因:同一时段大量的key失效或者redis服务宕机

解决办法:随机数/redis集群/网关限流/多级缓存

给不同的key的TTL添加随机值

双写一致性

先介绍业务 因为双写一致性有两种要求(一致性要求高/允许延迟一致)

强一致

弱一致

总结

持久化

Redis持久化有两种方式

  1. RDB

  2. AOF

  3. RDB 和 AOF 的混合持久化(Redis 4.0 新增)

RDB

RDB全称Redis Database Backup file (Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据 ,或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。

执行原理:

bgsave会fork出一个子进程,然后子进程共享主进程的内存数据,完成fork后读取内存数据写入RDB文件副本(避免脏写 读写)

RDB创建文件时候会阻塞主线程吗

RDB创建方式有两种 第一种种为save 这种会阻塞 他是同步保存 第二种为bgsave 他fork出一个子进程

AOF

命令日志文件

AOF默认是关闭的 我们需要在redis.conf 中开启

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中的。

AOF的工作流程是什么

  • 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。

  • 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write函数(系统调用),write将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。

  • 文件同步(fsync):AOF 缓冲区根据对应的持久化方式( fsync 策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用), fsync 针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。

  • 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。

  • 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。

AOF的持久化方式

Redis 的配置文件中存在三种不同的 AOF 持久化方式( fsync策略),它们分别是:

  1. appendfsync always:主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即会调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。

  2. appendfsync everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsync,fsync间隔为 1 秒)

  3. appendfsync no:主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsync,fsync 的时机由操作系统决定)。

AOF为什么是在执行命令后记录日志?

  • 避免额外的检查开销,AOF 记录日志不会对命令进行语法检查;

  • 在命令执行完之后再记录,不会阻塞当前的命令执行。

这样也带来了风险(我在前面介绍 AOF 持久化的时候也提到过):

  • 如果刚执行完命令 Redis 就宕机会导致对应的修改丢失;

  • 可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的)。

AOF重写了解吗?

AOF 文件重写期间,Redis 还会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

AOF校验机制了解吗?

校验和 CRC64

Redis 4.0对持久化机制做出了什么优化

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

如何选择RDB 和AOF?

RDB 某个时间点进行持久化 恢复的时候也是一下子恢复 AOF类似Mysql的binlog 一条一条 非常慢

  • Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。

  • 不建议单独使用 AOF,因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。

  • 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。

集群

Redis集群的模式了解吗 优缺点了解吗

当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis 切片集群(Redis Cluster )方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。

Redis Cluster 方案采用哈希槽(Hash Slot),来处理数据和节点之间的映射关系。在 Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中,具体执行过程分为两大步:

  • 根据键值对的 key,按照 CRC16 算法计算一个 16 bit 的值。

  • 再用 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽。

接下来的问题就是,这些哈希槽怎么被映射到具体的 Redis 节点上的呢?有两种方案:

  • 平均分配: 在使用 cluster create 命令创建 Redis 集群时,Redis 会自动把所有哈希槽平均分布到集群节点上。比如集群中有 9 个节点,则每个节点上槽的个数为 16384/9 个。

  • 手动分配: 可以使用 cluster meet 命令手动建立节点间的连接,组成集群,再使用 cluster addslots 命令,指定每个节点上的哈希槽个数。

集群模式的优点和缺点

优点:

  • 高可用性:Redis集群最主要的优点是提供了高可用性,节点之间采用主从复制机制,可以保证数据的持久性和容错能力,哪怕其中一个节点挂掉,整个集群还可以继续工作。

  • 高性能:Redis集群采用分片技术,将数据分散到多个节点,从而提高读写性能。当业务访问量大到单机Redis无法满足时,可以通过添加节点来增加集群的吞吐量。

  • 扩展性好:Redis集群的扩展性非常好,可以根据实际需求动态增加或减少节点,从而实现可扩展性。集群模式中的某些节点还可以作为代理节点,自动转发请求,增加数据模式的灵活度和可定制性。

缺点:

  • 部署和维护较复杂:Redis集群的部署和维护需要考虑到分片规则、节点的布置、主从配置以及故障处理等多个方面,需要较强的技术支持,增加了节点异常处理的复杂性和成本。

  • 集群同步问题:当某些节点失败或者网络出故障,集群中数据同步的问题也会出现。数据同步的复杂度和工作量随着节点的增加而增加,同步时间也较长,导致一定的读写延迟。

  • 数据分片限制:Redis集群的数据分片也限制了一些功能的实现,如在一个key上修改多次,可能会因为该key所在的节点位置变化而失败。此外,由于将数据分散存储到各个节点,某些操作不能跨节点实现,不同节点之间的一些操作需要额外注意。

缓存读写策略

旁路缓存模式

缺陷 1:首次请求数据一定不在 cache 的问题

解决办法:可以将热点数据可以提前放入 cache 中。

缺陷 2:写操作比较频繁的话导致 cache 中的数据会被频繁被删除,这样会影响缓存命中率 。

解决办法:

  • 数据库和缓存数据强一致场景:更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。

  • 可以短暂地允许数据库和缓存数据不一致的场景:更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小

读写穿透

写(Write Through):

  • 先查 cache,cache 中不存在,直接更新 db。

  • cache 中存在,则先更新 cache,然后 cache 服务自己更新 db(同步更新 cache 和 db)。

读(Read Through):

  • 从 cache 中读取数据,读取到就直接返回 。

  • 读取不到的话,先从 db 加载,写入到 cache 后返回响应。

Write Behind Pattern(异步缓存写入)

但是,两个又有很大的不同:Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。

0

评论区