redis的一些重要的基础知识

1. rehash

本文只介绍数据结构和结果图,如果要看文字描述过程,可以参考链接:rehash的详细过程

1.1 redis的hash表的数据结构

在这里插入图片描述
dictType类型:

type dictType struct {
    hashFunction   func(key interface{}) uint32
    keyDup         func(privdata interface{}, key interface{}) interface{}
    valDup         func(privdata interface{}, obj interface{}) interface{}
    keyCompare     func(privdata interface{}, key1 interface{}, key2 interface{}) int
    keyDestructor  func(privdata interface{}, key interface{})
    valDestructor  func(privdata interface{}, obj interface{})
}
//hashFunction:哈希函数,用于计算键的哈希值; MurmurHash方法
//keyDup:键复制函数,用于插入插入键值对
//valDup:值复制函数, 用于插入键值对
//keyCompare:键比较函数,用于比较两个键是否相等,用于查找键值对;
//keyDestructor:键销毁函数,用于释放键占用的内存;
//valDestructor: 值销毁函数,用于释放值占用的内存;

dict类型:

type dict struct {
    ht [2]*dictht //两个hash表
    rehashIndex int // 表示重新哈希的索引位置(如果等于 -1,则表示没有进行重新哈希)
    iterators uint32 //当前正在迭代的迭代器数
    randomSeed uint64 //是一个随机种子,用于在哈希函数中生成随机值
    privdata interface{} //私有数据,保存着dictType结构中函数的 参数,保存哈希表的一些额外信息
    type_ *dictType //指向了一个 dictType 结构体,用于实现哈希表的各项功能。
}

dictht类型:

//dict结构中ht[0]、ht[1]哈希表的数据结构
type dictht struct {
    table []dictEntry 存放一个数组的地址,数组中存放哈希节点dictEntry的地址
    size int 哈希表table的大小,初始大小为4
    sizemask int 用于将hash值映射到table位置的索引,大小为(size-1)
    used int  //记录哈希表已有节点(键值对)的数量
}

dictEntry类型

type dictEntry struct {
    key interface{}
    v   dictEntryValue
    next *dictEntry
}
type dictEntryValue struct {
    ptr interface{}
    integer int64
    uinteger uint64
    _float float64
}

2. AOF日志

2.1 简要介绍

  • 主要作用:redis是内存存储,如果系统崩溃可能会导致数据丢失,而AOF的作用就是用来redis崩溃后数据恢复的;
  • 格式实例:AOF是直接将redis命令进行记录的,set testKey testValue这个命令:
    在这里插入图片描述
  • 该日志属于写后日志:在redis执行命令后在插入日志
    • 优点:不会阻塞当前写操作;确保写入的命令是正确执行的;
    • 缺点:redis执行成功后,数据写入AOF日志缓冲区没能及时同步,导致数据丢失,从而数据不一致;这个时候就需要选择合理的同步策略:
      在这里插入图片描述

2.2 AOF重写

  • 重写原因:作为文本记录的AOF日志,随着命令的增加,记录越来越多,从而导致了文件也越来越大,在数据恢复的时候会对系统产生较大的负担;
  • 重写步骤:
    • 主线程fork一个子线程来进行重写,用主线程如果文件过大,会阻塞主流程;
    • 子线程对AOF日志进行重写,重写过程中新来的命令记录在AOF日志和重写AOF日志缓冲区中,避免数据不一致。重写完成后,将缓冲区中的数据加入到日志中,从而完成重写。
    • 重写逻辑:即将多个命令合并成一个命令。通过这种方式,可以有效地减少AOF文件的大小,提高数据恢复的速度,并解决上述问题。因此,AOF重写确实是因为AOF日志中重复key的操作合并,以提高数据持久化的效率和效果‌。
      在这里插入图片描述
//代码浅浅编写:
func aofRewrite(newAofFileName string) {
    // 创建新的 AOF 文件
    f := createFile(newAofFileName)
    
    // 遍历数据库
    for _, db := range redisServer.db {
        // 忽略空数据库
        if db.isEmpty() {
            continue
        }
        
        // 写入 SELECT 命令,指定数据库号码
        f.writeCommand("SELECT", db.id)
        
        // 遍历数据库中的所有键
        for _, key := range db {
            // 忽略已过期的键
            if key.isExpired() {
                continue
            }
            
            // 根据键的类型对键进行重写
            switch key.type {
            case String:
                rewriteString(key, f)
            case List:
                rewriteList(key, f)
            case Hash:
                rewriteHash(key, f)
            case Set:
                rewriteSet(key, f)
            case SortedSet:
                rewriteSortedSet(key, f)
            }
            
            // 如果键带有过期时间,那么过期时间也要被重写
            if key.haveExpireTime() {
                rewriteExpireTime(key, f)
            }
        }
    }
    
    // 写入完毕,关闭文件
    f.close()
}

func rewriteString(key *Key, f *File) {
    // 使用 GET 命令获取字符串的值
    value := redis.Get(key).Val()

    // 使用 SET 命令重写字符串键
    f.writeCommand("SET", key, value)
}

func rewriteList(key *Key, f *File) {
    // 使用 LRANG 命令获取列表键包含的所有元素
    items := redis.LRange(key, 0, -1).Val()

    // 使用 RPUSH 命令重写列表键
    args := []interface{}{key}
    for _, item := range items {
        args = append(args, item)
    }
    f.writeCommand("RPUSH", args...)
}

func rewriteHash(key *Key, f *File) {
    // 使用 HGETALL 命令获取哈希键包含的所有键值对
    fieldVals := redis.HGetAll(key).Val()

    // 使用 HMSET 命令重写哈希键
    args := []interface{}{key}
    for field, value := range fieldVals {
        args = append(args, field, value)
    }
    f.writeCommand("HMSET", args...)
}

func rewriteSet(key *Key, f *File) {
    // 使用 SMEMBERS 命令获取集合包含的所有元素
    elems := redis.SMembers(key).Val()

    // 使用 SADD 命令重写集合键
    args := []interface{}{key}
    for _, elem := range elems {
        args = append(args, elem)
    }
    f.writeCommand("SADD", args...)
}

func rewriteSortedSet(key *Key, f *File) {
    // 使用 ZRANG 命令获取有序集合包含的所有元素
    memberScores := redis.ZRangeWithScores(key, 0, -1).Val()

    // 使用 ZADD 命令重写有序集合键
    args := []interface{}{key}
    for _, memberScore := range memberScores {
        args = append(args, memberScore.Score, memberScore.Member)
    }
    f.writeCommand("ZADD", args...)
}

func rewriteExpireTime(key *Key, f *File) {
    // 获取毫秒精度的键过期时间戳
    timestamp := getExpireTimeInUnixStamp(key)

    // 使用 PEXPIREAT 命令重写键的过期时间
    f.writeCommand("PEXPIREAT", key, timestamp)
}

3. RDB快照

3.1 执行过程

在这里插入图片描述

3.2 存在问题

如果快照间隔时间太短:
在这里插入图片描述
如果间隔时间过长,可能会导致数据大量丢失

解决方式

在这里插入图片描述
本文的目的只是做一个简单的记录,详细的可以参考链接:https://blog.csdn.net/m0_70325779/article/details/132409948

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SAO&asuna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值