理解内存
为什么要理解内存呢? 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:整数数组,从小到大顺序保存 控制键的数量 针对自己现在使用的模式,分析其内存消耗和可优化的地方。复制代码