Redis知识点

一、3种常用的缓存读写策略

1.Cache Aside Pattern(旁路缓存模式)
2.Read/Write Through Pattern(读写穿透)
3.Write Behind Pattern(异步缓存写入)

二、Redis持久化方案

RDB(Redis DataBase)方案:
可以将 Redis 在某个时间点的数据保存到磁盘上的一个快照文件dump.rdb中。这个快照文件可以用于恢复 Redis 中的数据,也可以用于在 Redis 重启时加载数据。RDB 方案的优点是对 Redis 的性能影响较小,缺点是可能会丢失最近一次快照之后的数据。适合做数据的备份,灾难恢复。

AOF(Append Only File)方案:
将 Redis 执行的所有写操作都记录在一个追加的日志文件appendonly.aof中。这个日志文件可以用于恢复 Redis 中的数据,也可以用于在 Redis 重启时加载数据。AOF 方案的优点是可以保证数据的完整性,缺点是可能会对 Redis 的性能产生影响,并且日志文件较大

在 Redis 中,可以同时启用 RDB 和 AOF 方案,这样可以在出现问题时更快地恢复数据。另外,在 Redis 中还可以设置自动触发保存快照的策略和自动触发重写 AOF 日志文件的策略,以达到更好的性能和数据保护。

三、Redis 生产问题

1.缓存穿透:
定义:访问缓存中不存在的数据,这些数据既不存在于缓存中,也不存在于后端存储中,导致每次访问都要查询后端存储,从而导致后端存储的压力增大,甚至会导致系统崩溃。
措施:
布隆过滤器:在缓存层之前加入布隆过滤器,可以快速判断出一个key是否存在于缓存中,从而避免对不存在的key进行数据库查询。
缓存空对象:将不存在的key也存入缓存,但是value设置为null或者一个空对象,这样下次请求相同的key就会直接命中缓存,而不会落到数据库上。
2.缓存击穿:
定义:缓存中某个数据失效或者被删除,而后续的请求仍然访问该数据,从而导致每次访问都要查询后端存储,从而导致后端存储的压力增大,甚至会导致系统崩溃。
措施:
加互斥锁:使用互斥锁,避免多个线程同时查询数据库,可以保证只有一个线程去查询数据库,其他线程等待查询结果。
设置热点数据永不过期:将一些热点数据设置成永不过期,避免在失效时造成缓存击穿。
3.缓存雪崩:
定义:缓存中大量的数据同时失效,导致后续的请求都要访问后端存储,从而导致后端存储的压力增大,甚至会导致系统崩溃。
措施:
设置不同的过期时间:将过期时间分散开来,避免大量的key同时失效。
缓存数据预热:在系统启动时,将一些常用的数据预先加载到缓存中,避免在某一时刻缓存大量失效。

四、Redis 分布式锁

Redis 分布式锁是一种常见的分布式应用场景,主要用于协调多个节点之间的并发访问,保证同一时间只有一个节点可以访问共享资源。Redis 分布式锁通常基于 Redis 的单线程机制,通过使用 SETNX 命令或者 RedLock 算法实现。
其中,最简单的实现方式就是通过 SETNX 命令(SET if Not eXists)实现。具体步骤如下:

  1. 在 Redis 中创建一个字符串类型的 key,用于表示分布式锁的名称。
  2. 通过 SETNX 命令在 Redis 中创建该 key,设置其超时时间,并指定其值为当前节点的标识符。SETNX key value
  3. 如果 SETNX 命令返回 1,说明当前节点成功获取了分布式锁,并可以访问共享资源。
  4. 如果 SETNX 命令返回 0,说明当前节点未获取到锁。需要等待一段时间后重新尝试获取锁。在重新获取锁之前,需要释放已经获取到的锁。

另一种实现方式是 RedLock 算法,它通过多个 Redis 节点之间的协作来确保分布式锁的可靠性。具体步骤如下:

  1. 将所有 Redis 节点按照 hash 算法的顺序排列得到一个有序序列。
  2. 对于每个节点,生成一个随机字符串作为 token。
  3. 尝试在每个节点上创建一个 key,并将其值设置为 token,超时时间设置为锁的有效期。如果在大部分节点上成功创建了 key 就认为获取到了锁。
  4. 如果获取到了锁,则可以访问共享资源。
  5. 如果未能在大部分节点上成功创建 key,则认为获取锁失败。需要释放已经获取到的锁,并等待一段时间后重试。

需要注意的是,Redis 分布式锁的实现是有一定局限性的。它在实现时需要考虑很多因素,如锁的有效期、锁的粒度、锁的可重入性、锁的竞争等问题。因此,在实际使用中需要根据具体的场景进行调整和优化。

五、Redis五种常用数据类型:

String:字符串类型,支持整数、浮点数和二进制数据。

List:列表类型,按照插入顺序排序的字符串元素集合,支持在头部或尾部插入元素。

Set:集合类型,不允许重复元素的无序字符串集合,支持交集、并集、差集等操作。

Hash:哈希类型,类似于关联数组,存储键值对的无序散列表。

Zset:有序集合类型,不允许重复元素的有序字符串集合,每个元素关联一个分数,可以按照分数排序。

六、缓存的设计

缓存是一种常见的性能优化技术,其目的是通过存储计算结果或数据副本,以便更快地访问它们,从而提高应用程序的响应速度和效率。以下是缓存的一些设计原则:

1.缓存应该被设计成可扩展的。即使在高负载情况下,缓存也应该能够容易地扩展并适应不断增长的数据量。
2.缓存应该具有高可用性。缓存服务器应该部署在多个地理位置以提高可用性。还应该使用自动故障转移和备份机制来确保缓存服务的连续性。
3.缓存应该具有高性能。缓存系统应该能够快速地响应请求并提供高速缓存访问。优化网络传输和内存管理是提高缓存性能的关键。
4.缓存应该具有有效的过期策略。缓存数据应该具有过期时间,以确保缓存中的数据是最新的。还应该实现缓存清除机制,以避免缓存中存在旧数据。
5.缓存应该具有适当的缓存替换机制。当缓存区满时,缓存替换机制应该能够选择正确的数据进行替换,以确保缓存中的数据是最有用的。
6.缓存应该具有自我保护机制。缓存服务器应该能够检测到过度使用或错误使用,并采取适当的措施,例如限制访问或自动清除缓存,以避免系统故障。

综上所述,缓存的设计需要考虑多个方面,包括可扩展性、可用性、性能、过期策略、替换机制和自我保护机制。有效的缓存设计可以显著提高应用程序的性能和响应速度。

七、redis Zset 应用场景和原理

Redis中的Zset是一种有序集合,它类似于Set,但是每个成员都关联一个score(分值),通过score的大小可以进行排序。Zset的常用场景包括:

1.排行榜:将用户的得分作为score,用户的ID作为成员,可以通过ZREVRANGE命令获取排名前N的用户ID。

2.带权重的消息队列:将消息的优先级作为score,消息内容作为成员,可以使用ZADD命令将消息添加到Zset中,使用ZRANGE命令获取消息队列中的消息。

3.时间轴:将时间戳作为score,将事件作为成员,可以使用ZRANGEBYSCORE命令获取指定时间范围内的事件。

Zset的实现原理是使用一种叫做跳跃表(Skip List)的数据结构,它是一种有序的链表,能够支持快速的插入、删除和查找操作。跳跃表的基本思想是在普通有序链表上增加多级索引层,使得查询操作可以通过跳过一些不必要的节点来达到快速查找的目的。Redis中的Zset也是通过维护跳跃表来实现有序集合的。

八、SET类型的时间复杂度是什么?

平均时间复杂度为O(log n),最坏情况下为O(n)。
其中n表示SET中元素的数量。通常情况下,SET使用红黑树实现,因此添加元素的时间复杂度为树的高度,即log n。
SET使用红黑树实现是因为红黑树具有自平衡的特性,可以保证树的高度始终处于一个比较稳定的范围内。在红黑树中,每个节点都被标记为红色或黑色,并且满足以下规则:
1.根节点是黑色的
2.所有叶子节点都是黑色的空节点(NIL节点)
3.每个红色节点的两个子节点都是黑色的
4.从任意节点到其每个叶子节点的所有路径都包含相同数量的黑色节点。
这些规则保证了红黑树的平衡性,使得树的高度始终不会超过log n,其中n为树中元素的数量。因此,添加元素时需要遍历树的高度,时间复杂度为O(log n)。

九、redis 如何提高vip命中率

提高VIP的命中率需要综合考虑不同因素的影响,包括网络延迟、缓存容量、数据过期时间、数据分片等。

  1. 适当调整Redis的内存容量:如果Redis的内存容量不足,会导致缓存数据被频繁地替换,而这些替换的数据可能正是VIP重要客户端的请求数据,导致VIP命中率下降。

  2. 使用数据分片:将VIP数据拆分为多个分片,并将分片存储在不同的机器上,当一个请求到来时,可以首先根据请求的关键字确定所在的小表,然后只需要访问对应的机器,而不用访问整个映射表,可以减少单个机器的负载并提高VIP数据的命中率。

  3. 确保缓存数据的有效期:设置合适的过期时间,确保 Redis 缓存中的 VIP 数据时刻处于最新状态,并且在过期时能够及时更新,这可以提高 VIP 数据的命中率。

  4. 使用异步写入:将写入请求放到队列中,由后台线程异步处理,这样主线程就可以立即处理下一个请求,不需要等待写入完成,以避免写入VIP数据时造成阻塞。

  5. 使用Redis集群:分布式缓存避免了单点故障、负载均衡减轻了单个节点的压力、数据副本、内存共享

  6. 合理使用淘汰策略:Redis针对过期数据的淘汰策略有多种,如LRU(最近最少使用)、LFU(最近最不常使用)等,可根据实际应用场景选择合适的淘汰策略来提高VIP命中率。

十、redis使用的协议

Redis 使用的是自己定义的简单文本协议,也称为 RESP (Redis Serialization Protocol),是一种基于文本的协议。

RESP 协议的格式是一个数组,第一个元素表示请求的类型,后续元素表示请求的参数。在 Redis 中,请求的类型可以是以下几种:

  • SET:设置一个键值对
  • GET:获取指定键的值
  • EXISTS:检查指定键是否存在
  • DEL:删除指定键
  • MSET:设置多个键值对
  • MGET:获取多个键的值
  • INCR:将指定键的值加 1
  • DECR:将指定键的值减 1

请求的参数可以是任意类型,例如字符串、整数、纯二进制数据等等。在 RESP 协议中,所有的请求和响应都是以\n符号分割的不同的数组元素。

为了提高协议的效率,Redis 还支持内存共享机制,即多个 Redis 进程可以共享同一份内存,从而实现高效地数据交换。RESP 协议不仅能够支持 Redis 的常用操作,而且还可以扩展到多种不同类型的数据结构,如哈希表、列表、集合等等,从而满足更加复杂的数据操作需求。

另外需要注意的是,虽然RESP协议很简单,但是在实际使用中需要根据具体情况进行优化,对于一些特殊场景,如海量数据操作等,需要使用管道和批量操作等技术来提高效率和减少额外的网络开销。

十一、用 Redis 来做限流

限流是指限制某个时间段内的请求次数,防止流量过大导致系统崩溃或服务不可用。Redis可以通过使用令牌桶算法或漏桶算法来实现限流,具体方法如下:

  1. 令牌桶算法:在 Redis 中使用有序集合(sorted set)来实现令牌桶算法。每个请求进来时,从有序集合中获取当前时间对应的令牌数量,如果令牌数量为0,则拒绝请求;如果令牌数量大于0,则将令牌数量减1,并处理请求。
package main

import (
    "fmt"
    "time"

    "github.com/go-redis/redis/v8"
)

// 令牌桶大小
const BUCKET_SIZE = 100

// 令牌添加速率
const RATE = 10

func main() {
    // 创建Redis客户端
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })

    // 初始化令牌桶
    rdb.Del(ctx, "token_bucket")
    for i := 0; i < BUCKET_SIZE; i++ {
        rdb.ZAdd(ctx, "token_bucket", &redis.Z{Score: 0, Member: fmt.Sprintf("%d", i)})
    }

    // 模拟请求
    for i := 0; i < 150; i++ {
        if get_token(rdb) {
            fmt.Println("Request accepted")
        } else {
            fmt.Println("Request rejected")
        }

        // 等待一段时间,以保证限流速率
        time.Sleep(time.Duration(float64(time.Second) / RATE))
    }
}

func get_token(rdb *redis.Client) bool {
    // 获取当前时间戳
    timestamp := time.Now().Unix()

    // 移除过期的令牌
    rdb.ZRemRangeByScore(ctx, "token_bucket", &redis.ZRangeBy{Min: "0", Max: fmt.Sprintf("%d", timestamp - 1)})

    // 获取当前令牌桶中的令牌数量
    tokens, err := rdb.ZCard(ctx, "token_bucket").Result()
    if err != nil {
        panic(err)
    }

    // 如果令牌桶已空,拒绝请求
    if tokens == 0 {
        return false
    }

    // 移除一个令牌,并允许请求通过
    rdb.ZPopMin(ctx, "token_bucket")

    // 返回允许请求通过
    return true
}
  1. 漏桶算法:在 Redis 中使用计数器和定时器来实现漏桶算法。每个请求进来时,检查计数器的值是否小于漏桶的容量,如果小于容量,则将计数器加1,并处理请求;如果大于等于容量,则拒绝请求。同时,使用定时器来定时将计数器减少,以保证漏桶的容量不会超出预设值。

需要注意的是,Redis的限流功能需要结合其他组件一起使用,例如nginx、Spring Cloud Gateway等,来实现更完整的限流策略。

十二、Redis 为什么这么快?

1.Redis基于内存,内存的访问速度是磁盘的上千倍
2.Redis 线程模式:事件处理模型单线程循环+IO多路复用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值