美文网首页
redis学习笔记

redis学习笔记

作者: 三个程序员之一 | 来源:发表于2023-05-12 22:50 被阅读0次
    什么情况下可能会导致Redis阻塞
    1、客户端阻塞
    命令执行时间过长: keys* Hgetall smembers 时间复杂度O(N)
    2、BIGkey删除
    需要释放大量占用内存 zset(100万的元素 删除大概需要2s)
    3、清空库
    flushdb flushall 涉及删除所有键值对
    4、AOF日志同步写,记录AOF日志
    大量写的操作
    1一个同步写磁盘操作大概耗时1~2ms
    5、从库 加载RDB文件
    RDB文件过大
    6、Redis尽量部署在独立的服务器中
    节选文章https://blog.csdn.net/Zyw907155124/article/details/129830935
    
    存储
    aof
    rdb
    save 1000 1  1个改动1万秒生效
    save 600 50  50个改动600秒生效
    save 90 10000  10000个改动90秒
    
    
    image.png
    删除策略   定时删除:key到期就删除会有阻塞情况
                     惰性删除 :内存占用太大
                     定期删除 :中和以上两种抽样删除 从过期字典中随机20个key;删除其中已经过期的;如果过期比例超过1/4,则重复步骤 默认为每秒执行   Redis启动]服务器初始化时,读取配置配置的值 默认是每秒
    
    从库被动执行不会执行主动删除
    Redis使用内存存储数据,在执行没一个命令前,会调用FreeMemoryIFNeeded()检查内存是否充足
    Redis目前有8种逐出策略:
    
    1.volatile-lru:从已设置过期时间的数据中,挑选最近最少使用的数据淘汰。(针对时间)
    2.volatile-lfu:从已设置过期时间的数据中,挑选最近使用次数最少的数据淘汰。(针对访问频率)
    3.volatile-ttl:从已设置过期时间的数据中,挑选快要过期的数据。
    4.volatile-random:从已设置过期时间的数据中,任意选择数据淘汰。
    5.allkeys-lru:在所有的key中,移除最近最少使用的key。
    6.allkeys-lfu:在所有的key中,移除最近使用次数最少的key。
    7.allkeys-random:任意挑选数据淘汰。
    8.no-eviction:禁止驱逐数据(默认策略)内存满了只可读不能写 程序不会宕机(Noeviction策略)。
    需要注意的时,逐出过程并不是100%的能够清理出足够的空间,如果不成功反复执行,在所有数据都尝试完毕后,如果还是不能满足内存清理的要求,将会出现错误信息。
    
    redis 配置解读 引入其他作者文章
    https://www.jianshu.com/p/5cbd0c4ed239
    
    
    
    
    redis为什么快
    1.redis是纯内存操作 内存操作纳秒级别
    2.redis因为是纯内存操作巨快所以设计单线程做数据操作减少多线程切换开销
    3.redis使用的数据结构尽量做到O1的时间查询
    4.redis网络使用epoll模型少量线程进行大量的吞吐解决内存太快网络瓶颈的问题
    5.数据扩容操作使用渐进式的减少每次操作的时间成本利用空闲时间来做迁移
    6.做到redis 使用redis 为字段设计的短结构可以大大的降低内存消耗
    string list set hash zset 都是最大放2^32-1 键 就是int的4个字节写死的 String折算之后最大存储512M
    
    优化指令
    type rediskey --查看数据类型   string list hash set zset
    object encoding  rediskey  --查看底层编码 比如string 类型的 embstr int raw(最差那个)
    STRLEN rediskey--查看是占用多少字节字符串长度
    
    info 是查看所有信息
    config set slowlog-log-slower-than 20000 微妙
    config set slowlog-max-len 1000 
    config rewrite
    CONFIG GET slowlog-* 查看当前慢查询配置 
    slowlog get 
    slowlog len
    slowlog reset
    
    image.png

    按照图片顺序分别代表
    id 执行时候的时间 耗时微秒 下面是执行命令

    数据结构
    hash  用 ziplist hashtable  7.0以后listpack    改成listpack为了解决连锁更新问题
    listpack 用totallen和end 解决了  ziplist每个entry有个记录前一个结点大小的字段 小于255 使用1个字节表示  大于255用5个字节表示 如果每个都是252左右就会连锁更新 前一个记录导致连锁更新
    hash-max-ziplist-entries  entry长度小于512
    hash-max-ziplist-value  kv都要小于64
    
    list 3.2之前是linkedlist和ziplist  之后就是quickList (linkedlist和ziplist的结合7.0之后把ziplist换成listpack)
    list-max-ziplist-entries 512
    list-max-ziplist-value 64
    list-max-ziplist-size -2
    # -5: 每个ziplist最多为 64 kb  <-- 影响正常负载,不推荐
    # -4: 每个ziplist最多为 32 Kb  <-- 不推荐
    # -3: 每个ziplist最多为 16 Kb  <-- 最好不要使用
    # -2: 每个ziplist最多为 8 Kb   <-- 好
    # -1: 每个ziplist最多为 4 Kb   <-- 好
    # 正数为ziplist内部entry个数
    因为list中间很少被访问到所以对中间可以压缩节省空间 这个是压缩配置
    list-compress-depth 0
    # - 0:不压缩(默认值)
    # - 1:首尾第 1 个元素不压缩
    # - 2:首位前 2 个元素不压缩
    # - 3:首尾前 3 个元素不压缩
    # - 以此类推
    List-Max-listpack-size -2
    # -5: 每个listpack最多为 64 kb  <-- 影响正常负载,不推荐
    # -4: 每个listpack最多为 32 Kb  <-- 不推荐
    # -3: 每个listpack最多为 16 Kb  <-- 最好不要使用
    # -2: 每个listpack最多为 8 Kb   <-- 好
    # -1: 每个listpack最多为 4 Kb   <-- 好
    # 正数为listpack内部entry个数
    
    set 结构7.2之前 intset hashtable  7.2之后 intset hashtable  listpack
    都是整型 用intset 并且
    set-max-intset-entries 512
    7.2之前sds类型肯定是hashtable了  7.2之后为了节省空间存listpack
    set-max-listpack-entries 128
    set_max_listpack_value 64
    
    zset  数据结构7.0之前  ziplist skiplist  7.0之后 ziplist  换成了listpack
    zset_max_listpack_entries 128
    zset_max_listpack_value 64
    最大跳表是32层
    skiplist有一个层数上的问题,当层数过多,会影响查询效率。而Redis 使用了一个随机函数来决定每个节点的层数,这个随机函数的期望值是 1/(1-p) ,其中 p 是一个概率常数,Redis 中默认为 0.25。这样可以保证跳跃表的平均高度为 log (1/p) n ,其中 n 是节点数。Redis 还限制了跳跃表的最大层数为 32 ,这样可以避免过高的索引层造成空间浪费
    
    
    stream  最大2^64 - 1  键值对 stream 类型的底层实现是 rax(基数树),它是一种压缩的前缀树结构,它将所有的键值对按照 ID 的字典序存储在一个树形结构中。rax 可以快速地定位、插入、删除任意位置的键值对
    
    stream 类型的应用场景主要是实现事件驱动的架构,比如:
    
    消息队列,利用 xadd 和 xread 命令实现生产者消费者模式。
    操作日志,利用 xadd 和 xrange 命令实现操作记录和回放。
    数据同步,利用 xadd 和 xreadgroup 命令实现多个消费者组之间的数据同步。
    
    stream的底层使用了rax tree和listpack两种结构,rax tree用来存储streamID,而listpack用来存储对应的值
    ![aa074f7072be924a5d99eb33c2f08134.png](https://img.haomeiwen.com/i13239217/15fb4fe73b704ba7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    
    HyperLogLog 特殊得String类型
    用来做基数统计得
    HyperLogLog 是一种概率数据结构,用于在恒定的内存大小下估计集合的基数(不同元素的个数)。它不是一个独立的数据类型,而是一种特殊的 string 类型,它可以使用极小的空间来统计一个集合中不同元素的数量,也就是基数。一个 hyperloglog 类型的键最多可以存储 12 KB 的数据
    
    hyperloglog 类型的底层实现是 SDS(simple dynamic string),它和 string 类型相同,只是在操作时会使用一种概率算法来计算基数。hyperloglog 的误差率为 0.81%,也就是说如果真实基数为 1000,那么 hyperloglog 计算出来的基数可能在 981 到 1019 之间
    hyperloglog 类型的应用场景主要是利用空间换时间和精度,比如:
    
    统计网站的独立访客数(UV)
    统计在线游戏的活跃用户数(DAU)
    统计电商平台的商品浏览量
    统计社交网络的用户关注数
    统计日志分析中的不同事件数
    
    
    GEO(geospatial)  地理存储查询 基于sorted set 
    利用 geohash 算法将经纬度编码为二进制字符串,并作为 sorted set 的 score 值。Redis geospatial 提供了一系列的命令来添加、删除、搜索和计算地理空间位置,例如:
    GEOADD key longitude latitude member [longitude latitude member …]:将一个或多个地理空间位置(经度、纬度、名称)添加到指定的 key 中
    GEOPOS key member [member …]:返回一个或多个地理空间位置的经纬度
    GEODIST key member1 member2 [unit]:返回两个地理空间位置之间的距离,可以指定单位(m, km, mi, ft)
    GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]:返回指定圆心和半径内的地理空间位置,可以指定返回坐标、距离、哈希值、数量、排序方式等,也可以将结果存储到另一个 key 中
    GEORADIUSBYMEMBER key member radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]: 返回以指定成员为圆心的指定半径内的地理空间位置,其他参数同 GEORADIUS
    
    geospatial 的应用是地理位置搜索、分析和展示,例如地图应用、导航应用、位置服务应用等。例如,可以使用 geospatial 来实现以下功能:
    
    统计某个区域内的商家或用户数量
    查询某个位置附近的餐馆或酒店
    计算两个位置之间的距离或行驶时间
    显示某个位置周围的景点或活动
    
    
    
    Bitmap  是String类型  最长是2^32 - 1 
    用来做标记得 
    在2进制位中 使用长度标记那里是1 然后做出1的个数统计
    SETBIT 1000:2024:100 1 1
    setbit key offset 0|1   
    bitcount rediskey
    
    Bitfield   基于String
    
    例如,使用bitfield存储用户的个人信息,
    
    用一个8位的无符号整数来表示用户的性别,0表示男,1表示女
    用一个8位的无符号整数来表示用户的年龄,范围是0-255
    用一个16位的无符号整数来表示用户的身高,单位是厘米,范围是0-65535
    用一个16位的无符号整数来表示用户的体重,单位是克,范围是0-65535
    
    语法是BITFIELD  rediskey set 多少位(u8) 第一个数(#0) 值(实际存的值)  set 。。。。  set 。。。。
    获取是把 存的时候所有set变成get 然后值那个肯定就不用写了其他照旧
    
    
     BITFIELD user:1:info SET u8 #0 1 SET u8 #1 25 SET u16 #2 165 SET u16 #3 50000
    
    一下存很多字段 所以是bitfield型的
     BITFIELD user:1:info GET u8 #0 GET u8 #1 GET u16 #2 GET u16 #3
    
    
    
    bit用法  底层是string结构  string最大512M  开始3.2之前是因为int最大是32位,后来优化sds之后支持了64位不知道是不是为了兼容字符串还是32位 写死的所以最大512M
    setbit key offset 0|1      --offset最大是 2的32次方-1
    比如
    setbit today-1101 5678 0 --意思就是5678位是0  所以占用大小就是 5678除以8取整
    bitcount rediskey  起始字节  结束字节  --结果是有多少个1、可通过STRLEN rediskey 查看占用字节
    bitop and rediskey  rediskey1 rediskey2 ... --意思就是对bitmap进行key1 key2 &运算 可以统计7天连续登录的
    bitop or  rediskey  rediskey1 rediskey2 ...    --意思就是对bitmap进行key1 key2 &运算 可以统计7天登录过的
    
    
    解释redis value 就是 RedisObject 对象里面的实际指向数据的指针*ptr 他是8个字节
    string结构中 ptr指针 直接存的是int数字
    string embstr是因为cpu缓存行一次读取64byte 因为redisobject只有16byte所以用sds的8次方那个空间存储然后本身对象字段和\0占用4个byte所以给字符串的只有44个字节
    
    cpu缓存行cache line一次拿64byte
    object encoding (KEY) 查看底层编码格式
    
    
    redis key 是SDS
    redis底层key 是 sds 叫简单动态字符 simple dynamic string
    SDS好处  解决C语言 0的问题二进制安全  预分配内存避免频繁分配  兼容c语言函数库 
    1.因为c语言字符串 会在末尾增加 \0 所以redis读取key的时候因为怕传过来的有 \0 导致读取不全
    所以 添加了 len字段描述传过来的数据多长 然后直接读取len的长度
    2.追加append 
    3.sds  3.2版本以前
    字串 free  空间换时间为了快 主要append命令 和setbit命令  
            len
            char buf[] = 'fffffffff'
    扩容机制
    (len+addlen)*2  为了不用每次修改都需要申请内存空间
    长度len达到 1024*1024  =1M的时候   就会每次增加1M 不回成倍增加内存了
    
    SDS因为 len 本来是int类型但是他表示的长度太长了
    然后就把len优化了 设置了 一堆 不同大小的len 和buffer
    
    3.2之后的redis sds     1个字节进行对齐的
    
    因为有sds 5 sds8 sds16 sds32 sds64
    sds5只有 flags 和buf[]   使用flags因为占用1个字节  前面3bit位置描述是sds 5 sds8 sds16 sds32 sds64
    后面5个bit来表示buf长度
    
    sds8以后就变成了 
    alloc  申请总长度  根据不同类型大小不一样
    len  buf  数据长度根据不同类型大小不一样
    flags  还是前3位置表示类型  后5位就浪费了
    buf 真正数据
    就和3.2版本那种的差不多了
    
    
    全局哈希表 
    redisDB{
    对象结构
    dict
    id
    expires
    blocking_keys
    ready_keys
    weched_keys
    ttl
    expires_cursor
    }
    
    
    
    dict {
    dicttype
    dictht[]  1 2 用来rehash
    也有entry
    key 一个对象 里面有比对之类的
    value是个数组的 有两个value对象 为了rehash 渐进式
    }
    
    dictht{
    dictEntry  **table
    long size    hashtable容量
    long sizemask   size-1
    long used      元素个数hashtable 占用
    }
    
    dictEntry{
     void *key   SDS
     union{
    redis中union就是value
     void *val     RedisObject 对象  有string hash list set zset
    }
     *next  下一个
    }
    
    redisObject{
    type  代表是hash list zset set string     约定客户端的操作 不满足会报错
    encoding 底层数据结构dict 还是 sds 还是ziplist  skiplist等
    lru
    refcount 引用计数 收集垃圾用的
    *ptr 真实存储的数据指针
    
    
    
    }
    
    
    rehash    触发时机数据量等于数组长度  过程是 被访问到先去老的找然后 把查询到的数据放到新的上面然后 后台轮询的时候也会进行rehash数据迁移。 搬运的时候是同步的 都是主线程做的
    

    相关文章

      网友评论

          本文标题:redis学习笔记

          本文链接:https://www.haomeiwen.com/subject/tegesdtx.html