Redis不同数据类型value存储

一、Strings

redis中String的底层没有用c的char来实现,而是使用SDS数据结构( char buf[])。

缺点:浪费空间

优势:

1.c字符串不记录自身的长度,所以获取一个字符串长度的复杂度是O(N),但是SDS记录分配的长度alloc,已使用长度len,获取长度的复杂度为O(1)。比如,为char,必须一个个遍历,直到遍历到\0,字符串越长,那么速度越慢

2.可以减少字符串修改带来的内存重分配次数

字符串更改必须要先申明内存,否则会导致内存溢出。trim(str)时,还需要把不再使用的空间回收,不然会内存泄漏,并且如果操作频率过多,还会导致性能下降。这两点redis是如何优化的呢?

2.1空间预分配:SDS长度如果小于1MB,预分配跟长度一样的,大于1M,每次跟len的大小多1M

2.2惰性空间释放:截取的时候,不马上释放空间,供下次使用!同时提供相应的释放SDS未使用空间的API

2.3二进制安全 :C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符串被误认为是字符串结尾。这是因为c的字符是以空字符来判断这个字符串是否结束的。这些限制使得C字符串只能保存文本数据,而不能保存像图像、音频、视频、压缩文件这样的二进制数据。 SDS字符串是否结束是根据len来, 所以也就不会有这样的问题

二、Hashes

存储结构为ziplist压缩列表,当超过某种条件时,会转换为hashtable

优势:节省内存空间。压缩列表会根据存入的数据的不同类型以及不同大小,分配不同大小的空间。

缺点:因为是一块完整的内存空间,因此当里面的元素发生变更时,会产生连锁更新,严重影响我们的访问性能。所以,只适用于数据量比较小的场景。

那么redis是如何处理该缺陷的呢?

redis中会有相关的配置,hashes只有小数据量时会用到ziplist,当hash对象同时满足以下两个条件的时候,才会使用ziplist编码:

a.hash对象保存的键值对的数量<512个

b.所有的键值对的键和值的字符串长度都<64byte(一个英文字母一个字节)

redis.conf配置

bash 复制代码
hash-max-ziplist-value 64 // ziplist中最大能存放的值长度
hash-max-ziplist-entries 512 // ziplist中最多能存放的entry节点数量

dict hashtable如图(在下篇博客讲到扩容会着重提到)

三、Lists

存储结构为quicklist快速列表(c源码)

cpp 复制代码
typedef struct
{
struct quicklistNode *prev; //前指针
struct quicklistNode *next; //后指针
unsigned char *zl; //数据指针 指向ziplist结果
unsigned int sz; //ziplist大小 /* ziplist
size in bytes */
unsigned int count : 16; /* count of items in
ziplist */ //ziplist的元素
unsigned int encoding : 2; /* RAW==1 or LZF==2 */ //
是否压缩, 1没有压缩 2 lzf压缩
unsigned int container : 2; /* NONE==1 or ZIPLIST==2
*/ //预留容器字段
unsigned int recompress : 1; /* was this node previous
compressed? */
unsigned int attempted_compress : 1; /* node can't
compress; too small */
unsigned int extra : 10; /* more bits to steal for
future usage */ //预留字段
} quicklistNode;

quicklist兼顾了ziplist的节省内存,并且一定程度上解决了连锁更新的问题,quicklistNode每个节点里面是一个ziplist,每个节点又是分开的,那么就算发生了连锁更新,也只会发生在一个quicklistNode节点

quicklist中的每个node的ziplist元素的大小也是可以配置的(redis.conf)

cpp 复制代码
# Lists are also encoded in a special way to save a lot of
space.
# The number of entries allowed per internal list node can
be specified
# as a fixed maximum size or a maximum number of elements.
# For a fixed maximum size, use -5 through -1, meaning:
# -5: max size: 64 Kb <-- not recommended for normal
workloads
# -4: max size: 32 Kb <-- not recommended
# -3: max size: 16 Kb <-- probably not recommended
# -2: max size: 8 Kb <-- good
# -1: max size: 4 Kb <-- good
# Positive numbers mean store up to _exactly_ that number
of elements
# per list node.
# The highest performing option is usually -2 (8 Kb size)
or -1 (4 Kb size),
# but if your use case is unique, adjust the settings as
necessary.
list-max-ziplist-size -2

list-max-ziplist-size如果这个配置值是正数,就代表quickListNode的ziplist的node的数量;如果为负数 固定的是-5到-1,则代表ziplist的大小(上图注释中有说明)

四、Sets集合

Redis的数据类型及使用场景-CSDN博客这篇博客中提到了set中如果存储的是整数的话,会按顺序存储;那么sets集合的存储方式为inset或者hashtable存储。满足元素为整型,并且元素个数小于配置(redis.conf),就用inset存储。

a.如果不是整数类型,就用dict hashtable(数组+链表)

b.如果元素个数超过512个,也会用hashtale存储。

cpp 复制代码
set-max-intset-entries 512

问题:set的key没有value,怎么用hashtable存储呢?value存null就好了

五、Sorted Sets(ZSet)

默认使用的是ziplist(hash的小编码,quicklist的Node都是ziplist)

在ziplist的内部,会按照score排序递增来存储。插入的时候要移动之后的数据。若元素数量大于等于128,或者任一member长度大于等于64字节 则会采用skiplist+dict(跳表)存储。

redis.conf配置

cpp 复制代码
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

skiplist跳表

结构定义(c源码)

cpp 复制代码
* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
sds ele; //sds数据
double score; //score
struct zskiplistNode *backward; //后退指针
//层级数组
struct zskiplistLevel {
struct zskiplistNode *forward; //前进指针
unsigned long span; //跨度
} level[];
} zskiplistNode;
//跳表列表
typedef struct zskiplist {
struct zskiplistNode *header, *tail; //头尾节点
unsigned long length; //节点数量
int level; //最大的节点层级
} zskiplist;

跳表原理图:

如图:已有数据3.7.11.19.22.27.35.40,假如我找27的数据。

只有一个链表的场景下:我需要一个一个遍历,随着数据量越大,效率就会越慢

随机多层跳表:找27,会从最外层开始找,在22-40之间,再找第二层,在22到35之间,就能找到27。在外层的数据,查询的速度越高,比如找22,只需要找一次。

跳表的层级是在redis.conf中配置的(默认为32)

cpp 复制代码
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64elements */
相关推荐
wowocpp5 分钟前
查看 磁盘文件系统格式 linux ubuntu blkid ext4
linux·数据库·ubuntu
Ai 编码助手6 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
陈燚_重生之又为程序员6 小时前
基于梧桐数据库的实时数据分析解决方案
数据库·数据挖掘·数据分析
caridle6 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
白云如幻6 小时前
MySQL排序查询
数据库·mysql
萧鼎6 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
^velpro^6 小时前
数据库连接池的创建
java·开发语言·数据库
荒川之神6 小时前
ORACLE _11G_R2_ASM 常用命令
数据库·oracle
IT培训中心-竺老师6 小时前
Oracle 23AI创建示例库
数据库·oracle
小白学大数据6 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫