Redis 基础数据结构

这篇博客介绍了Redis的基础数据结构,包括String、Hash、List、Set和Zset。文章详细阐述了每种数据类型的存储原理、应用场景,并探讨了它们之间的区别和转换关系。例如,String类型采用SDS实现,适用于缓存、分布式锁和计数器;Hash用于存储键值对,适合购物车缓存设计;List作为有序集合,可用于消息列表和队列;Set用于抽奖和用户点赞;Zset提供有序元素,适用于排行榜。
摘要由CSDN通过智能技术生成

Redis 基础数据结构

1. 背景

​ 为什么会产生NoSQL数据库? 和传统的RDBM的区别在哪里? 有什么突出优势吗?

​ 传统数据库如MySQL, 更多的是表格结构化的行存储数据, 固定的模式(需要数据适应表结构), 通过事务特性控制数据一致性;存在问题: 扩展性-面临表结构修改困难导致的存储数据格式受限, 读写性能-传统关系型数据库由于会将数据持久化到磁盘, 并发和海量数据的情况下, 磁盘读写压力大.

​ NoSQL的特性: 存储非结构化的数据-如文本, 图片, 音频, 视频; 扩展性强-表与表之间没有关联, 能分片存储, 扩缩容简单; 最终一致性(BASE理论)-保证数据的最终一致性;高频读写支持-海量数据的存储; 针对不同的存储类型: 可分为K-V存储(Redis), 文档存储(MongoDB), 列存储(HBase), 图存储(Neo4j), 对象存储(OSS); NoSQL 产品汇总(https://hostingdata.co.uk/nosql-database/)

​ NewSQL则是前两者的结合, 如TiDB(PingCAP), ScaleDB

2. 基础数据类型

数据结构: String, Hash, Set, List, Zset

2.1 String字符串
(1)存储数据类型: int, float, string
(2)存储原理

SDS, redisObject, 内部编码格式比较

  • int: 存储8个字节的长整型(long, 2^64 - 1)
  • embstr: emdstr格式的SDS, 存储小于44个字节的字符串
  • raw: 存储大于44个字节的字符串.
(3)底层分析:

SDS: Simple Dynamic String 简单动态字符串, (本质字符串数组) 空间换时间

​ 为什么采用SDS实现字符串? C语言本身没有字符串类型, 只能是使用字符数组:

​ a.使用字符串数组需要给目标变量分配足够的空间, 否则可能会溢出

​ b.获取字符串长度需要遍历整个字符串

​ c.C 中字符串长度变更会对字符串数组做内存重分配

​ d.存储图片, 音视频, 压缩文件等二进制文件的不安全性

引入SDS带来的优势

​ 1.不用担心内存溢出, 会对SDS扩容

​ 2.通过空间换时间方式, 获取字符串的长度

​ 3.通过"空间预分配" 和 “惰性空间释放”, 防止多次重分配内存

Q1: embstr 和 raw 编码的区别? 为什么要为不同字符串大小设置不同的编码?

​ embstr 的使用只分配一次内存空间(RedisObject 和 SDS 是连续的), 而raw需要分配两次内存空间, 简单来说, 即少分配, 少释放, 查询方便; embstr 面临字符串长度增加时, 需要重新分配内存, 内存空间重分配

Q2: int、embstr、raw三者之间的转化关系?

​ int 数据不再是整数 --> raw

​ int 大小超过long的范围(2^63 - 1) --> embstr

​ embstr 长度超过44字节 --> raw, 此外 只要修改embstr对象, 修改后一定是raw对象

Q3: 为什么对底层数据结构采用RedisObject进行一次封装

​ 存储内容和存储方式的对应, 尽量节省内存空间和提升查询速度

(4) 应用场景

缓存

String类型, 缓存热点数据(如何实时统计热点数据)

分布式数据共享

独立地作为分布式的独立服务, 可以在多个应用之间共享, 分布式Session

分布式锁 🚩

全局ID

INT 类型, 原子性, 分库分表中一次获取一段数据

计数器

INT类型, INCR 方法, 微博点赞, 允许一定延迟, 先写入Redis然后定时同步到数据库

限流

INT类型, INCR方法, 将访问者的IP和其他信息作为key, 每访问一次就增加一次计数, 超过则返回false

2.1 Hash 哈希
(1) 存储类型:

​ 无序键值对, 最大存储数量2^31 - 1

(2) 存储原理

​ 由dictEntry 实现, 内层Hash层可以采用两种数据结构

ziplist: OBJ_ENCODING_ZIPLIST 压缩列表

hashtable: OBJ_ENCODING_HT 哈希表

ziplist: 一个经过特殊编码的, 由连续内存块组成的双向链表. 存储的是上一个节点的长度和当前节点的长度, 读写速度可能会下降, 但可以节省内存(时间换空间)

hashtable: 对dictEntry进行了多层的封装, 将定义的dictEntry 封装到dictht中, 然后又将dictht封装入dict中, 即数组+链表的结构(注意: dictht后面为NULL表示 第二个ht 还没有用到, dictEntry*后面是NULL说明没有hash到这个地址, dictEntry后面为NULL说明没有发生哈希冲突)

Hash 的 value 只能是字符串, 不能嵌套其他类型

**Q1: Hash 和 String 的区别 **

​ 1.将所有相关的值聚集到一个Key中, 节省内存空间

​ 2.批量获取对象的值时, 只需要使用一个命令, 减少内存/CPU/IO 的消耗

​ 3.Field不能设置过期时间

​ 4.数据量的分布

Q2: 什么时候用ziplist存储?

​ 当hash对象同时满足以下两个条件时候, 采用ziplist编码 (redis.conf可以配置)

​ a. 哈希对象保存的键值对数量 < 512;

​ b.所有的键值对的键和值的字符串长度 < 64 byte

​ 如果超过两个阈值的任何一个, 存储结构会转为hashtable

Q3: 为什么要定义两个哈希表, 而其中一个不用呢

​ redis的hash默认使用ht[0], ht[1]不会初始化和分配空间

​ 哈希表dictht是使用链地址法来解决哈希冲突问题,这种情况下, 哈希表的性能取决于他的大小和保存节点数量之间的比率.

​ 如果单个哈希表的节点数量过多, 哈希表的大小需要扩容rehash:

​ 1.为字符ht[1]哈希表分配空间. ht[1]的大小为第一个大于等于ht[0].used * 2 的 2 的N次方幂

​ 2.将所有的ht[0]上的节点rehash到ht[1]上, 重新计算hash值和索引, 然后放入到指定的位置

​ 3.当ht[0]全部迁移到ht[1]后, 释放ht[0]的空间, 将ht[1]设置为ht[0], 并创建新的ht[1], 为下次rehash做准备

(3)应用场景

​ 购物车的缓存设计

List类型
(1)存储类型

​ 存储有序的字符串, 元素可以重复

(2)底层原理

​ 早期版本中, 数据量较小时用ziplist存储(特殊编码的双向链表), 达到临界值转为linkedlist进行存储; 3.2版本统一使用quicklist来存储(数组+链表), quicklist存储了一个双向链表, 每个节点都是一个ziplist

(3)应用场景

列表

例如用户的消息列表, 网站公告列表, 活动列表, 博客的文章列表, 评论列表: 存储所有字段, lrange 取出一页

队列/栈

当作分布式环境中的队列或栈使用, 提供阻塞的弹出操作(可设置超时时间),

Set类型
(1)存储类型

​ 存储String类型的无序集合

(2)底层原理

​ 使用inset或hashtable存储set, 如果元素是整数类型, 就是inset存储; 不是整数类型的话 采用hashtable存储. 不过元素个数操作512个, 也会使用hashtable存储

(3)应用场景

抽奖

随机获取元素

点赞, 签到, 打卡

用户点赞的维护, 点赞数, 取消点赞等

商品标签

商品的删选, sinter sdiff

用户关注, 推荐模型

相互关注,我关注的人, 可能认识的人

ZSet类型
(1)存储类型

​ 存储有序元素, 每个元素有一个score

(2)存储原理

​ 默认使用ziplist编码, 在ziplist内部, 按照score排序递增来存储, 插入时需要移动后面的数据, 如果元素大于等于128个, 任一member 长度大于等于64则使用skiplist + dict存储.

(3)应用场景

排行榜

BitMaps

​ 位图是定义在字符串类型上的位操作, bit存储数据, 节省空间, 用来做大数据统计, 例如在线用户统计, 留存用户统计等

Hyperloglogs

​ 提供一种不太准确的基数统计方法, 用来统计一个集合中不重复的元素个数, 如统计用户的UV, 应用的日活, 月活

3.总结

3.1 数据结构
对象type 属性command底层存储结构object encoding
字符串OBJ_STRING“string”OBJ_ENCODING_INT
OBJ_ENCODING_EMBSTR
OBJ_ENCODING_RAW
int
embstr
raw
列表OBJ_LIST“list”OBJ_ENCODING_QUICKLISTquicklist
哈希OBJ_HASH“hash”OBJ_ENCODING_ZIPLIST
OBJ_ENCODING_HT
ziplist
hashtable
集合OBJ_SET“set”OBJ_ENCODING_INTSET
OBJ_ENCODING_HT
intset
hashtable
有序集合OBJ_ZSET“zset”OBJ_ENCODING_ZIPLIST
OBJ_ENCODING_SKIPLIST
ziplist
skiplist + hashtable
3.2 编码转换
对象原始编码升级编码
字符串INT
整数且小于 long 型
embstr
超过 44 字节, 被修改
raw
哈希ziplist
K-V的长度小于64byte 且 元素个数<=512
hashtable
列表quicklist
集合intset
元素均为整型类型 且个数 < 512个
hashtable
有序集合ziplis
元素数量不超过128个 且member的长度小于64byte
skiplist

Reference

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值