Redis学习笔记(Redis深度历险 ——钱品文)

本文深入探讨了Redis作为缓存中间件的使用,对比了Redis与Memcached的优势,强调了Redis的持久化和丰富的数据结构。文章详细讲解了Redis的常见问题,如缓存一致性、数据结构的应用、高可用性方案以及如何解决缓存穿透、击穿、雪崩问题。此外,还介绍了Redis的限流、分布式锁和延迟任务的实现策略。

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

为什么要用Redis?

因为传统的关系型数据库比如MySQL已经不能适用于所有的场景了。在传统的关系型数据库里,数据存放在磁盘上,IO开销很大。在高并发的情景下,比如秒杀系统里的库存扣减、对访问流量高峰的应对等,很容易把数据库打崩,所以需要引入缓存中间件。目前比较主流的缓存中间件有Redis和Memcached,综合考虑他们的优缺点,最后选择了Redis。
(1)提高对热点数据的访问性能
(2)在高并发场景下,分担数据库压力。

Redis相比Memcached有什么优势呢?

Memcached不支持持久化,数据只能放在内存里。
Redis可以持久化,支持更多数据类型。
Redis原生支持集群。

Redis常见问题

  1. 缓存和数据库双写一致性问题
  2. 缓存雪崩、缓存穿透、缓存击穿问题
  3. 缓存的并发竞争问题

Redis有哪些数据结构?

最基本的数据结构有5种,包括字符串string、哈希hash、链表list、集合set、有序集合sorted set。
此外还有位图bitset、hyperLogLog、Geo、Pub/Sub、BloomFilter等。

这些数据结构各可以用来做什么?Redis可以用来做什么?

Redis是一个开源的内存中的数据结构存储系统,可以用来做数据库、缓存、消息中间件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fu9z9t3j-1578570129201)(quiver-image-url/4C6EFC9EEDA7EADB30C1F0C0BDF81C5D.jpg =646x372)]
(1)string:缓存功能,计数器,存储用户session
(2)Hash:存储结构化的对象,但是不能表达嵌套对象的对象
(3)List:下拉分页,消息队列
(4)set:去重,交集看共同好友,并集差集
(5)zset:排行榜,热搜榜,带权重的队列
(6)BitMap:位图,以bit位为基本的单位存储信息,记录boolean,省空间
(7)Hyperloglog:不精确的去重技术功能,如统计UV(网站访客量)
(8)Geospatial:保存地理位置,做距离计算。寻找附近的人
(9)pub/sub,订阅发布功能,用作简单的消息队列
(10)pipeline:批量执行一组指令,一次性返回全部结果,减少频繁的请求应答
(11)Lua:保证原子性,秒杀系统可以用

缓存穿透、缓存击穿、缓存雪崩是什么,如何解决 ?

  • 缓存穿透:正常的流程是:用户发起请求,先去缓存中找,没找到再去数据库里找。找到了便更新缓存,返回给用户,找不到便返回空。如果大量请求要找的都是根本不存在(缓存里没有,数据库里也没有)的数据,就会对数据库造成很大压力。
    解决方法: 在接口层增加校验,判断条件是否有效;布隆过滤器;缓存里增加key:null。

  • 缓存击穿:某个热点数据失效,大量请求要找同一条缓存中没有,但是数据库中有的数据(缓存到期),对数据库造成很大压力。
    解决方法: 设置热点数据永不过期(首页更新就更新缓存);访问数据库添加互斥锁,没拿到锁的就等待一下再去缓存里取。

  • 缓存雪崩:大量数据到达过期时间,大量请求需要访问数据库,压力很大。
    解决方法: 过期时间随机化,高可用架构,本地缓存分级缓存,限流+降级。事后rdb快照+aof快速恢复

常见的缓存有哪几种?

缓存的作用是提高对热点数据的访问性能。
本地缓存(性能高,容量小,不便扩展);
分布式缓存(容量大,易扩展,性能稍低);
多级缓存(最热数据放本地+其他作为分布式缓存)

Redis的过期策略和内存淘汰策略?

对于过期的数据,Redis会进行定期删除(每秒10次,随机从过期字典里选择20个key,删除其中已经过期的,如果过期比例超过四分之一,重复,循环上限25ms)。如果同一时间大量key过期,会造成卡顿。所以key的过期时间要随机化。
惰性删除。
此外,还有内存淘汰机制防止过期数据积压,比较常用的是当内存不足时,在所有key或设置了过期时间的key中移除最少访问的数据(近似LRU,随机选若干个,移除上次访问时间最早的那个)。或移除寿命小的,或随机移除。

从库的过期策略:主库会写一个del指令,同步到从库。

如何在成千上万个key里找出特定前缀的key?

keys prefix*,缺点是没有limit,一次返回所有;时间复杂度O(N),如果结果特别特别多,会阻塞线程,造成其他Redis请求阻塞。
scan指令的特点:通过游标分步进行,不会阻塞线程;提供limit参数;也提供模式匹配功能;返回的结果可能会有重复;遍历过程中如果有数据修改,改动后的数据能不能遍历到是不一定的;返回的游标值为0代表遍历结束。

Redis的持久化

持久化机制保证即使宕机,数据也不会丢失。Redis的持久化机制分为快照(一次全量备份,二进制序列化形式,非常紧凑)和AOF日志(连续增量备份,记录内存数据修改的指令,要定期进行重写瘦身)。

  • 快照机制:父进程继续处理客户端请求,调用fork产生一个子进程,进行copy on write快照,即持久化的是子进程产生那一瞬间的数据版本,父进程修改数据时会复制一个新版本再修改,内存占用不是立即增长到2倍,而是逐渐增长,小于2倍。

  • AOF日志:存储Redis服务器的顺序指令序列,只存储修改指令。(收到修改指令,先写指令,再执行)bgrewriteaof可以进行AOF日志瘦身。

  • 混合持久化
    全量日志快照 + 快照时间后的AOF日志重放,效率是最高的

RDB五分钟一次,恢复快但是数据不完整。
AOF一秒一次,通过后台线程fsync操作,追加写数据,异步刷新缓存区,文件太大。

管道Pipeline

管道的作用:客户端一次发送多个请求,一次接受多个请求,提高QPS。不能无限提升,受制于服务端的cpu处理能力。

PubSub

Publisher / Subcriber,支持消息队列的多播(一个生产者,多个消费者队列,每组消费者有各自的消费逻辑)。

Redis5.0新特性:Stream数据结构

强大的支持多播的可持久化的消息队列。

CAP原理

C :Consistent一致性
A : Availability 可用性
P : Partition tolerance 分区容忍性
网络分区(分布式系统中节点之间网络断开)发生时,一致性(断开连接的主机无法继续同步)和可用性(想保持一致性就要停掉服务进行维护)难两全。

Redis如何支持高可用

  • Sentinel哨兵。
    自动化主从切换,抵抗节点故障。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-auArZiuE-1578570129202)(quiver-image-url/A75B50D085625552FB7221D6680062E5.png =490x340)]
    客户端连接redis时,先询问哨兵主节点ip,询问到以后再去连接该ip。如果连接不上,就再次询问哨兵,得到新的主节点ip。对于挂掉的主节点,哨兵持续监控,重新可用以后就变成从节点。
    问题:如果主从延迟大,就会丢失消息。无法保证消息完全不丢,两个参数避免主从延迟过大。min-slaves-to-write 1表示至少有多少从节点在进行正常复制(设置min-slaves-max-lag,如果为=】10,表示超过10s没收到从节点反馈说明复制出现异常)
    三个(若干个)哨兵节点做的事:
    (1)每隔10秒给主从节点发送info指令获取他们的信息,包括ip
    (2)每隔2秒同时跟订阅的sentinel_hello频道进行同步,获取新哨兵节点的信息,交换主节点的状态信息
    (3)每隔1秒对主节点、从节点、其他哨兵节点发送ping命令做心跳测试。
    如果一个哨兵发现主节点down了,此时是”主观下线“,向其他哨兵咨询,如果半数都同意,变为”客观下线“。
    客观下线后,选取一个哨兵领导者,选出新的主节点(不能是不健康节点(比如主观下线了、断线、很久没有回复ping命令、与主节点失联),优先级比较高的,复制偏移量最大数据最完整的)

  • Cluster集群架构

主从同步

异步同步,只能保证最终一致性。从节点加入集群时,快照同步+增量同步。先RDB快照发过去,并且主库的操作写到复制buffer里(环状,新的会覆盖旧的),buffer再发给从节点,从节点反馈偏移量。如果网络不好会发生还没发过去就被覆盖了。
wait指令:同步复制,保证严格一致性,但是如果长时间阻塞,丧失了可用性。

2.8之后快照不用全部生成再发,有优化。

双写一致性问题,缓存更新问题

(1)cache aside:取数据在缓存里取,找不到就去数据库里取,并存到缓存里。更新的时候先更新数据库,再让缓存失效。

Redis是单线程还是多线程服务?为什么单线程还这么快,能支持高并发?(待完善)

纯内存操作,优秀的数据结构底层,不需要线程上下文切换,使用了非阻塞IO多路复用机制。
单线程,特指网络请求模块使用了单线程,即一个线程处理所有网络请求,其他模块仍然是多线程。
为什么使用单线程:操作都是基于内存的,cpu不会成为瓶颈。
单线程避免了多线程会带来的上下文切换开销、死锁现象、对锁的抢夺。
之所以能够处理大量并发的客户端连接,是因为使用了事件轮询(多路复用技术) + NIO(非阻塞IO)。
Redis内部使用文件事件处理器 File Event Handler,采用IO多路复用机制同时监听多个socket。
(阻塞IO在读时,读够指定长度再返回,否则阻塞;写时,缓冲区写满了就阻塞,写完再返回。非阻塞IO能读多少读多少,能写多少写多少,不会阻塞。但是会导致一个问题————如果没读完,剩下来的什么时候继续读?如果没写完,剩下来的什么时候继续写?这就用到了事件轮询技术。)

缓存预热,缓存降级,二八定律

缓存预热:提前加载热点数据到缓存里。

Redis为什么这么快

(1)内存数据库,速度快
(2)单线程,没有锁的竞争和上下文切换。
(2)多路IO复用。
(4)数据结构效率高,比如XXX底层实现用的是XXX。。。

懒惰删除

unlink,对删除大key进行懒处理,异步回收
flushall -asyc
AOF也是异步的

如何进行限流(待完善)

简单限流 + 漏斗限流

分布式锁

setnx key value
如果在这个时候宕机,setnx+expire的原子操作
expire key time (防止出现意外没有显式释放)
del key

如何实现延迟任务

使用zset数据结构,score为时间, 取第一个key,时间到了就拿出来执行。多线程抢夺使用zrem,返回1则为抢夺成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值