一、九种数据类型
▪ String:二进制安全、可以包含任何数据,(常规计数,分布式锁,共享session信息)
▪ List:简单的字符串列表,按照插⼊顺序排序(最新回复,消息队列)
▪ Hash:键值对集合(缓存对象、购物车)
▪ Set:无序的去重集合(共同好友、共同关注,点赞,抽奖活动)
▪ SortedSet:可排序版的set(排行榜,电话和姓名排序)
▪ Bitmap:位图,⼀个以位为单位的数据(二值状态统计的场景:签到,判断登录状态等)
▪ Hyperloglog:统计基数,有误差(海量数据基数统计的场景)
▪ Geospatial:地理位置信息(打车定位,滴滴打车)
▪ Stream:为消息队列设计的数据类型
二、底层数据结构
▪ SDS:
• 记录长度,可以o(1)时间获得长度
• ⼆进制格式,能存储任意数据类型
• 多种数据类型,int、raw和 embstr,节省内存空间
• 二进制安全,不会发生缓冲区溢出
▪ 链表(双向链表)
• 双向链表、有头尾指针、有链表长度
• void*保存节点值,链表节点内容多样性
▪ 哈希表
• 链式哈希解决冲突
• rehash:redis使⽤两个哈希表,数据增多需要扩容的时候,搬移数据到ht[1]上,然后 交换
• 渐进式rehash:新增、更新、删除、更新操作时候,都会迁移到ht[1]上,最终都搬到 ht[1]的时候上,完成rehash(查询时候如果ht[0]没有查到会去ht[1]上查找)
• 渐进式条件:
◦ 负载因子 = 哈希表保存节点数 / 哈希表大小
◦ 负载因子 >= 1,没有执行RDB和AOF时候rehash
◦ 负载因子 >= 5,不管有⽆RDB和AOF操作都会强制rehash
▪ 压缩链表
• 根据数据类型和大小进行分配,节省空间
• 连锁更新问题:前⼀个节点长度⼤于254字节,当前节点prevlen⻓度为1字节(2^8) 变为5字节保存
▪ 整数集合:
• ⼀个数组
• 升级操作:节省空间,不能降级
▪ 跳表:
• 单纯性能,跳表和红⿊树性能相差不大,并发环境下,更新数据时,跳跃表需要更新的部分更少,锁的东西比较少,竞争锁的代价也更小(链表加多级索引,二分思想)
• Redis选用跳表的原因
◦ 内存占用:平衡树每个节点还需要保存两个左右指针,跳表每个节点包含的指针数目平均为 1/(1-p),Redis中平均使⽤1.33个指针
◦ 遍历操作:平衡树拿到节点后进⾏中序遍历很困难,跳表有前后指针
◦ 实现难度:跳表实现更简单
▪ quicklist:
• 对链表的更新,把每个节点换成了压缩列表 双向链表 + 压缩列表
◦ 对双向链表的优势:压缩列表是内存连续的,比每个节点都离散的存法,更能利用缓存都读到缓存里
◦ 对压缩列表的优势:把⼀整个压缩列表切分成了多个节点,连锁更新影响
• 当前节点的压缩列表不能容纳时候,再创建下⼀个节点
▪ listpack
• quicklist中每个压缩列表还是存在连锁更新的问题
• 压缩链表不再记录前⼀个节点的⻓度,记录当前节点的长度,避免连锁更新