Redis相关总结

一、缓存雪崩、缓存穿透

现象:缓存雪崩:大量缓存同时过期、缓存中间件宕机
缓存穿透:访问不存在key、缓存过期
解决:缓存雪崩:设置不同过期时间
缓存穿透:不存在key也存入缓存、使用布隆过滤器、使用分布式锁

二、Redis集群

主从复制:支持redis读写分离,不支持故障恢复

哨兵集群 :在主从复制模式下增加哨兵监控,当主节点故障,会选取一个新的主节点(无法扩容)
Cluster模式:分片模式,slot槽,可以扩容

三、RDB、AOF

RDB:快照模式

文件:dump.rdb

配置参数:save x y(x秒内有y个写入)

可以配置多个规则

//RDB模式默认是开启的

save 900 1
save 300 10
save 60 10000

 

触发:手动执行bgsave命令触发异步快照、执行save命令触发同步快照,根据redis.conf文件里的配置,自动触发bgsave,主从复制时触发

流程图:

AOF:日志模式

文件:appendfsync.aof

配置参数:

//AOF持久化,默认是关闭的。 打开 redis.conf, 找到如下配置

appendonly no # 改为 yes 就开启了 AOF

appendfilename "appendonly.aof" # aof 文件名

# appendfsync always #每执行一个命令保存一次
appendfsync everysec # 默认,每一秒保存一次
# appendfsync no # 不主动执行保存,由操作系统决定

 

当执行指令时会将执行命令追加到缓冲区,再通过子线程把缓冲区数据写入到aof文件

由于aof文件会很大,aof文件到达一定大小会有一个重写机制
aof重写机制:对相同key的指令只保留最新的指令,然后将过滤后的指令写入到新的aof文件,然后删除旧的aof文件

                       在重写过程中,新的指令会存入到重写缓冲区中,重写完成后再将重写缓冲区内的指令写入到新的aof文件

流程图:

 

四:Redis存在线程安全问题吗?

redis是线程安全的,redis多线程模型只是针对IO网络。
执行多个指令无法保证原子性会出现线程安全问题,可以通过事务和lua脚本、锁实现

事务和lua脚本无法保证完全的原子性,因为不存在回滚机制

事务:MULTI 开启事务

          指令1

          指令2

          EXEC 执行事务

#region Redis事务  StackChange.Redis
if (RedisTranBool)
{
    var db = RedisHelper1.One.CacheRedis;


    if (db.KeyExists("Count") && (int)db.StringGet("Count") == 0)
    {
        db.KeyDelete("Count");
        db.KeyDelete("Index");
        db.KeyDelete("Record");
    }
    Console.WriteLine("删除成功");
    if (!db.KeyExists("Count"))
    {
        db.StringSet("Count", 10000);
    }
    if (!db.KeyExists("Index"))
    {
        db.StringSet("Index", 0);
    }
    Console.WriteLine("初始化成功");
    await Task.Run(async () =>
    {
        try
        {
            while (true)
            {
                var Count = (int)db.StringGet("Count");
                var Index = (int)db.StringGet("Index");
                var tran = db.CreateTransaction();
                tran.AddCondition(Condition.StringEqual("Count", Count));
                tran.AddCondition(Condition.StringEqual("Index", Index));
                if (Count > 0)
                {
                    tran.StringGetAsync("Count");
                    tran.StringDecrementAsync("Count", 1);
                    tran.StringIncrementAsync("Index", 1);
                    tran.SortedSetAddAsync("Record", JsonConvert.SerializeObject(new { index = Index, id = Guid.NewGuid(), time = DateTime.Now.ToString("YYYY-MM-DD HH:mm:ss") }), Index);
                }
                bool IsResult = await tran.ExecuteAsync();
                if (IsResult)
                {
                    Console.Clear();
                    var _count = db.StringGet("Count").ToString();
                    Console.WriteLine(_count);
                    var _index = db.StringGet("Index").ToString();
                    Console.WriteLine(_index);
                    var record = db.SortedSetScan("Record")?
                    .OrderBy(c => c.Score)
                    .Select(c => JsonConvert.DeserializeObject(c.Element))?
                    //.OrderBy(c =>c?.GetType().GetProperty("index"))
                    .ToList();
                    record?.ForEach((r) => { Console.WriteLine(r); });
                }

                Thread.Sleep(5000);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    });
}
#endregion

  

原子性指令

1. SET key value:将字符串值 value 关联到 key。

   SETEX key seconds value   设置键值对和过期时间

2. GET key:返回 key 所关联的字符串值。

3. INCR key:将 key 中存储的数字值增一。

4. DECR key:将 key 中储存的值减一。

5. APPEND key value:如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。

6. HSET key field value:将哈希表 key 中的字段 field 的值设为 value。

7. HGET key field:返回哈希表 key 中给定字段 field 的值。

8. SADD key member:将 member 元素加入到集合 key 当中。

9. SPOP key:移除并返回集合中的一个随机元素。

10.SETNX

五:内存淘汰机制

触发淘汰:当到达了设置的最大内存
1、Random,随机淘汰算法
2、TTL ,在设置了过期时间的key中找到最早过期的key进行移除
3、LRU,移除最近很少使用的key
4、LFU,访问频次和上次访问时间

 

六:key过期了为啥没有释放内存空间

redis并不是实时删除过期数据,而且通过定期删除和过惰性删除,

定期删除是一定时间间隔会随机选取一批数据来判断是否过期,如果过期则删除

惰性删除是当一个key被查询时,会去检测这个key是否过期,如果过期则删除

 

七:使用keys指令可能会有什么问题

1、keys指令需要遍历整个redis的键空间,如果redis的key非常庞大会非常耗时影响性能

2、阻塞问题:keys指令是一阻塞命令,在keys执行完成之前,其他客户端无法访问redis

八:redis中大key怎么处理

定义:key对应的value数据很大

九:redis实现分布式锁,及相关问题

首先redis实现分布式锁使用的是setNX指令,表示插入一个key,能插入成功就获得锁

问题一(死锁):集群环境下如果一个节点获得了锁,然后这个节点挂掉了,锁就无法释放会发生死锁,这时可以引入锁过期机制,

通过lua脚本保证setNX和设置key过期指令原子性

问题二(锁续期):引入过期机制以后,如果业务没有处理完,锁就过期了。这时可以增加一个线程(类似看门狗),如果当前获得锁的进程还存在,则自动增加过期时间(锁续期问题)

问题三(锁释放):在问题二的基础上,假如线程A执行业务没有完成,锁提前过期了,这时线程B拿到锁,执行业务逻辑。这时线程A业务逻辑完成,释放了锁。此时释放的是线程B拿到的锁。

要解决这个问题,可以在加锁时,每个线程将value值设置成guid,释放锁时先判断value是否为当前线程。

 

posted @ 2024-06-13 12:02  DaiWK  阅读(10)  评论(0编辑  收藏  举报