Redis基础数据结构——字符串

Redis基础数据结构——字符串

 字符串string是Redis最简单的数据结构,它的内部表示是一个字符数组。它是动态字符串,可以进行修改,内部结构的实现类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
 字符串一个常见的用途是缓存用户信息,通过将用户信息结构体使用JSON序列化成字符串,然后将序列化后的字符串塞进Redis来缓存,取用户信息时要进行反序列化。
Redis中所有的数据结构都以唯一的key字符作为名称,然后通过唯一key值来获取相应的value数据,不同类型的数据结构差异在于value的结构不一样

1.redis字符串的基本操作

(1)单键值对的增删改查操作

set key value

 设置一个key的value值,如果这个key已经存在了,那么再次设置时,会覆盖原来的值。

get key

 获取一个key对应的值,如果该key未设置值返回nil,如果该key对应的不是string,返回异常
在这里插入图片描述

setnx key value

 如果key不存在就执行set创建一个,若是存在返回0,表示创建不成功,key还是原来的值
在这里插入图片描述

getset key value

 将key对应的值设为value,并返回它原来的那个值
在这里插入图片描述

getrange key start end

 获得key对应字符串坐标start到end之间的字符串
在这里插入图片描述
 当end超出字符串长度时,显示到字符串最后一位,不会报错(因为redis的字符串的字符数组是分配冗余数组以供扩展,后面的空间里只是还未存放字符,因而不会报错)
在这里插入图片描述
 但是当超过了最大容量时还是会报溢出,字符串最大长度为512MB
在这里插入图片描述

strlen key

 获取key对应字符串的长度,对于未设置过的key,长度为0
在这里插入图片描述

del key

在这里插入图片描述

append key value

 在key对应值的末尾添加value,若key不存在,相当于创建了一个新的key对应值为value
在这里插入图片描述

(2)与过期时间有关的操作

expire key seconds

 给key设置过期时间,到时间后会被自动删除

 这里是给name设置了5s的过期时间,5s内访问name还存在,5s后访问返回的就是nil了,说明被删除了
在这里插入图片描述

setex key seconds value

在这里插入图片描述
(3)计数操作
 计数的前提是value值是一个整数,才可以对其进行自增操作,自增范围在signed long的最大值和最小值之间

incr key

 key的value值自增1

incrby key increment

 key的value值自增increment
在这里插入图片描述
(4)批量操作

mset key value [key value …]

 批量设置多个key的值

mget key [key …]

 批量获得多个key的值

msetnx key value [key value …]

 批量对key进行setnx创建

在这里插入图片描述
2.redis字符串的内部实现

 redis字符串在内存中虽然也是以字节数组的形式存在,但它是不同于C语言的字符数组的。
 redis的字符串叫作"SDS",也就是Simple Dynamic String,结构是一个带长度信息的字节数组

struct SDS<T> {
	T capacity;		//数组容量
	T len;			//数组长度
	byte flags;		//特殊标记位
	byte[] content;	//数组内容
}

在这里插入图片描述
 形如此,capacity表示所分配数组的长度,len表示字符串的实际长度,因为有了冗余空间供字符串追加新内容,因而使得redis字符串为可以修改的字符串。
 如代码所示,结构体中存放了数组长度,因而获得字符串长度不需要再花费O(n)去遍历数组,只需要花费O(1)时间返回len即可。
 该SDS结构使用了泛型T,为什么不直接使用int呢?这是因为当字符串比较短的时候,len和capacity可以使用byte和short来表示,redis为了对内存做极致的优化,不同长度字符串使用不同的结构体来表示。

(1)redis字符串的两种存储方式
在长度特别短时,使用embstr形式存储,而当长度超过44字节时,使用raw形式存储
在这里插入图片描述
 可以看到,第一个codehole是44个字节,它的encoding是embstr;第二个是45个字节,它的encoding是raw。
 那么为什么会有这种区别呢?
 要搞清楚这个问题,得分析它的内部结构
 下面是redis对象头结构

struct RedisObject {
	int4 type;		//4bits
	int4 encoding;	//4bits
	int24 lru;		//24bits
	int32 refount; 	//4bits
	void *ptr		//8bytes,64-bit system
} robj;

 不同的对象具有不同的特性type(4bit),同一个类型的type会有不同的存储形式encoding(4bit),lru记录对象的LRU信息(24bit),ptr指针指向对象内容的具体存储位置(8byte)。这样一个RedisObject对象头结构需要占据16字节的存储空间。

struct SDS<T> {
	T capacity;		//1byte
	T len;			//1byte
	byte flags;		//1byte
	byte[] content;	//内联数组,长度为capacity
}

 接着看SDS结构体的大小,在字符串比较小时,SDS对象头结构的大小是capacity+3,至少是3字节,意味着分配一个字符串的最小空间占用为19(16+3)字节。
embstr存储形式是这样的,它将RedisObject对象头结构和SDS对象连续存在一起,使用malloc方法一次分配。
 raw没有把两个对象头连续存放,它们在内存地址上是不连续的,需要使用两次malloc方法进行分配
在这里插入图片描述 内存分配器jemalloc、tcmalloc等分配内存大小的单位都是2/4/8/16/32/64字节等,为了能容纳一个完整的embstr对象,jemalloc最少会分配32字节的空间,如果字符串再稍微长一点就是64字节的空间。如果字符串总体超过了64字节,redis认为它是一个大字符串,不再适合使用embstr形式存储,而该使用raw形式。
 当内存分配器分配的64字节空间时,这个字符串的长度最大就是44字节
在这里插入图片描述
 此时两个对象头已经占据了19字节的空间,留给content数组的长度就只有45字节了,还要多出一个字节存放NULL,因而embstr形式最大能容纳的字符串长度就是44字节。

(2)字符串的扩容策略
 在字符串长度小于1MB之前,扩容空间采用加倍策略,也就是保留100%的冗余空间。
 当字符串长度超过1MB之后,为了避免加倍后的冗余空间过大而导致浪费,每次扩容只会多分配1MB大小的冗余空间。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

loser与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值