redis系列3---理解内存

248 阅读5分钟

理解内存

为什么要理解内存呢?
    redis所有的数据都存在内存中
    如何高效利用内存,实现用更少的内存存更多的数据,从而降低成本
如何统计内存使用?
    info memory可以获取内存相关指标,如下:
    used_memory:redis分配器分配的内存总量
    used_memory_human:以可读格式返回used_memory
    used_memory_rss:从操作系统的角度显示redis占用的物理内存总量
    used_memory_peak:内存使用的最大值,表示used_memory的峰值
    used_memory_peak_human:以可读格式返回used_memory_peak
    used_memory_lua:Lua引擎所消耗的内存大小
    mem_fragmentation_ratio:used_memory_rss/used_memory比值,表示内存碎片率
    mem_allocator:redis所使用的内存分配器,默认为jemalloc
    (注:mem_fragmentation_ratio>1出现内存碎片,mem_fragmentation_ratio<1出现操作系统把redis内存Swap到硬盘现象)
内存消耗
    自身内存
    对象内存(sizeof(keys)+sizeof(values))
    缓冲内存
        客户端缓冲(所有接入到redis服务器TCP连接的输入输出缓冲,输入缓冲无法控制,最大空间1G,超过将断开连接。输出缓冲通过参数client-output-buffer-limit控制)
            普通客户端(除了复制和订阅的客户端之外的所有连接)
            从客户端(主节点会为每个从节点单独建立一条连接用于命令复制)
            订阅客户端(当使用发布订阅功能时,连接客户端使用单独的输出缓冲区)
        复制积压缓冲区(一个可重用的固定大小缓冲区用于实现部分复制功能,repl-backlog-size)
        AOF缓冲区(用于redis重写期间保存最近的写入命令)
    内存碎片
        内存分配策略(小,大,巨大)
        容易出现内存碎片的场景(频繁做更新操作、大量过期键删除)
        如何解决(数据对齐、安全重启)
    子进程消耗(执行AOF/RDB重写时redis创建的子进程内存消耗)
内存管理
    设置内存上限(maxmemory)
    动态调整内存上限(config set maxmemory 6GB)
    内存回收策略
        删除到达过期时间的键对象
            惰性删除(当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空,节省CPU,存在内存泄漏问题)
            定时任务删除(自适应算法,键的过期比例、使用快慢两种模式)
        内存使用达到maxmemory上限时触发内存溢出控制策略(maxmemory-policy控制)
            noeviction:不会删除任何数据,拒绝所有写入操作并返回客户端错误信息
            volatile-lru:根据LRU算法删除设置了超时属性的键,直到腾出足够空间为止,如果没有删除的键,回退到noeviction
            allkeys-lru:根据LRU算法删除所有键,直到腾出足够空间为止
            allkeys-random:随机删除所有键,直到腾出足够空间为止
            volatile-random:随机删除过期键,直到腾出足够空间为止
            volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据,如果没有,回退到noeviction
内存优化
    首先了解一下RedisObject对象
        type字段:表示当前对象使用的数据类型
        encoding字段:表示redis内部编码类型
        lru字段:记录对象最后一次被访问的时间(object idletime {key}查看键的空闲时间)
        refcount字段:记录当前对象被引用的次数(object refcount {key}获取当前对象的引用)
        *ptr字段:与对象的数据内容有关,如果是整数,直接存储数据,否则表示指向数据的指针
    有哪些具体优化手段?
        缩减键值对象(key尽可能短,value采用序列化或者压缩算法)
        共享对象池(redis内部维护[0-9999]的整数对象池)
            注意:当设置maxmemory并启用LRU相关淘汰策略时,redis禁止使用共享对象池;对于ziplist编码的值对象,即使内部数据为整数也无法使用共享对象池
        字符串优化
            SDS(字符串长度、已用长度、未用长度)
            预分配机制(减少字符串频繁修改操作)
            字符串重构
        编码优化
            什么是编码?
            具体使用哪种底层数据结构来实现
            string--raw
                    embstr
                    int
            hash----hashtable
                    ziplist(value<=hash-max-ziplist-value and count(field)<=hash-max-ziplist-entries)
            list----linkedlist
                    ziplist(value<=list-max-ziplist-value and 链表长度<=list-max-ziplist-entries)
                    quicklist
            set-----hashtable
                    intset(元素为整数 and 集合长度<=set-max-intset-entries)
            zset----skiplist
                    ziplist(value<=zset-max-ziplist-value and 有序集合长度<=zset-max-ziplist-entries)
            (hashtable、ziplist、linkedlist、quicklist、intset、skiplist)
            注意:小编码可以转大编码,大不能转小
            ziplist(所有数据采用线性连续的内存结构,节约内存)
                zlbytes:记录整个压缩列表所占字节长度,方便重新调整ziplist空间。类型是int-32,长度为4字节
                zltail:记录距离尾节点的偏移量,方便尾节点弹出操作。类型int-32,4字节
                zllen:记录压缩链表节点数量,类型是int-16,2字节
                entry:记录具体的节点
                    prev_entry_bytes_length:记录前一个节点所占空间,用于快速定位上一个节点,可实现列表反向迭代
                    encoding:当前节点编码和长度,前两位表示类型,其余表示长度
                    contents:节点的值
                zlend:记录列表结尾,1字节
            intset(存储有序、不重复的整数集)
                encoding:表示类型
                length:集合元素个数
                contents:整数数组,从小到大顺序保存
        控制键的数量
        
针对自己现在使用的模式,分析其内存消耗和可优化的地方。