Redis 数据类型:String
wptr33 2025-01-21 21:58 29 浏览
介绍
在 Redis 中,String(字符串)是一种重要的数据类型。 它是最基本的键值结构,其中 key 是唯一标识符,value 是具体值。 事实上,值不仅是字符串,还可以是数字(整数或浮点数,并可进行数值运算),值可容纳的最大数据长度为 512M。
内部实现
字符串类型的底层数据结构实现主要是 int 和 SDS(Simple Dynamic String,简单动态字符串)。
SDS和我们所知道的C语言中的字符串不太一样。之所以不使用C语言的字符串表示,是因为与C原生字符串相比,SDS有以下特点:
- SDS 不仅可以保存文本数据,还可以保存二进制数据。 因为 SDS 使用 len 属性的值而不是 null 字符来判断字符串是否结束,而且 SDS 的所有 API 都将以处理二进制数据的方式来处理存储在 SDS 的 buf[] 数组中的数据。 因此,SDS 不仅可以存储文本数据,还可以存储图片、音频、视频和压缩文件等二进制数据。
- SDS 获取字符串长度的时间复杂度为 O(1)。 由于 C 语言的字符串不记录自己的长度,因此获取长度的复杂度为 O(n);而在 SDS 结构中,字符串长度通过 len 属性记录,因此复杂度为 O(1)。
- Redis 的 SDS API 是安全的,连接字符串不会导致缓冲区溢出。 因为 SDS 在连接字符串前会检查 SDS 空间是否满足要求,如果空间不足,会自动扩容,所以不会导致缓冲区溢出问题。
关于编码的便利性,字符串对象有 3 种内部编码(encoding):int、raw 和 embstr,它们之间的关系如下所示:
如果一个字符串对象存储了一个整数值,并且这个整数值可以用long类型表示,那么该字符串对象就会将该整数值存储在字符串对象结构体的ptr属性中(将void*转换为long),并设置将字符串对象编码为int 。
如果字符串对象存储一个字符串,并且这个字符串的长度小于或等于32字节(在Redis 2.+版本中),那么字符串对象将使用简单动态字符串(SDS)来存储这个字符串并设置对象的编码为embstr 。 embstr编码是一种专门用于存储短字符串的优化编码方法。
如果字符串对象存储一个字符串,并且这个字符串的长度大于32字节(在Redis 2.+版本中),那么字符串对象将使用一个简单的动态字符串(SDS)来存储这个字符串,并设置其编码对象为raw 。
注意:不同版本的Redis中embstr编码和raw编码的边界是不同的:
- 对于 Redis 2.+,它是 32 字节。
- 对于 Redis 3.0–4.0,为 39 字节。
- 对于 Redis 5.0,它是 44 字节。
可以看到,“embstr”编码和“raw”编码都会使用“SDS”来保存值,主要区别在于内存分配和数据存储方式:
- 内存分配
- “raw”编码:需要两次内存分配,分别为Redis对象(“redisObject”)和SDS(简单动态字符串)分配内存空间。
- “embstr”编码:只进行一次内存分配,同时为Redis对象和SDS分配连续的内存空间。
- 数据存储
- “raw”编码:Redis对象和SDS分别存储在不同的内存区域。
- “embstr”编码:Redis对象和SDS存储在连续的内存区域中。
- 修改操作
- “embstr”编码的字符串是只读的。一旦字符串需要修改,它将被转换为“raw”编码。
- “raw”编码的字符串可以直接修改。
- 性能影响
- “embstr”编码在创建和释放内存时的开销很小,因为只需要一次内存操作。
- “raw”编码在修改字符串时的性能更好,因为不需要进行编码转换。
选择使用哪种编码取决于具体的应用场景和需求。如果字符串长度较短且不经常修改,“embstr”编码可能更合适;如果字符串长度较长或需要频繁修改,“raw”编码可能更合适。
常用命令
普通字符串的基本操作:
# 设置键值类型的值。
> SET name Sea
OK
# 根据键值获取相应的值。
> GET name
"Sea"
# 判断是否存在某个键。
> EXISTS name
(integer) 1
# 返回键存储的字符串值的长度。
> STRLEN name
(integer) 3
# 删除某个键对应的值。
> DEL name
(integer) 1批量设置:
# 批量设置键值类型的值。
> MSET k1 v1 k2 v2 k3 v3
OK
# 批量获取多个键对应的值。
> MGET k1 k2
1) "v1"
2) "v2"计数器(可在字符串内容为整数时使用):
# 设置键值类型的值。
> SET number 0
OK
# 将键中存储的数值递增 1。
> INCR number
(integer) 1
# 将键中存储的数字值加上 100。
> INCRBY number 100
(integer) 101
# 将键中存储的数值递减 1。
> DECR number
(integer) 100
# 将键中存储的数字值减去 50。
> DECRBY number 50
(integer) 50过期(默认为永不过期):
# 设置键在 30 秒后过期(此方法用于设置已存在键的过期时间)。
> EXPIRE name 30
(integer) 1
# 查看键的过期时间。
> TTL name
(integer) 23
# 设置键值类型的值,并将此键的过期时间设置为 30 秒(有两种方法)。
> SET key value EX 30
OK
> SETEX key 30 value
OK如果不存在则插入:
>SETNX key value
(integer) 1应用场景
1. 统计计数
由于Redis在单线程中处理命令,因此执行命令的过程是原子的。因此,String数据类型适合计数场景,比如计算访问数、点赞数等。
例如,计算一篇文章的访问量:
# 初始化文章的访问量
> SET aritcle:visit:count:1000001 0
OK
# 访问量 +1
> INCR aritcle:readcount:1000001
(integer) 1
# 访问量 +1
> INCR aritcle:readcount:1000001
(integer) 2
# 获取相应文章的访问量
> GET aritcle:readcount:1000001
"2"2. 缓存对象
使用String来缓存对象有两种常见的方式:
- 直接缓存整个对象的JSON :将对象转换为JSON字符串,然后将其作为值存储在Redis的String类型中。这种方式简单直观,但是当修改对象的某个字段时,需要取出整个JSON字符串进行反序列化修改,然后重新存储,这可能会带来较大的网络开销和性能开销。
SET user:1 '{"name":"Bob", "age":18}'- 单独的key来存储对象属性:将对象的属性分别存储在不同的key中,每个key对应一个属性的值。这种方式可以更灵活地访问和修改对象的属性,但需要较多的键操作。
MSET user:1:name Bob user:1:age 18 user:2:name Jerry user:2:age 203. 分布式锁
在Redis中,String数据结构可以用来实现分布式锁。
SET命令有一个NX参数,可以实现“只有key不存在时才插入”,可以用来实现分布式锁:
- 如果key不存在,则说明插入成功,可以用来表示获取锁成功;
- 如果key存在,就会显示插入失败,可以用来表示获取锁失败。
一般来说,分布式锁也会加上一个过期时间。分布式锁的命令如下:
SET lock_key unique_value NX PX 10000lock_key:是键;
unique_value:是客户端生成的唯一标志符;
NX:表示仅当“lock_key”不存在时才设置“lock_key”;
PX 10000:表示将“lock_key”的过期时间设置为10秒,这是为了避免客户端因异常而无法释放锁。
而解锁的过程就是删除“lock_key”键,但不能随意删除。需要保证执行操作的客户端就是加锁的客户端。因此,在解锁时,我们需要首先判断锁的“unique_value”是否是加锁客户端。如果是,则可以删除“lock_key”键。
可以看到,解锁由两次操作。这时候就需要一个Lua脚本来保证解锁的原子性,因为Redis执行Lua脚本时,可以以原子的方式执行,保证锁释放操作的原子性。
// 释放锁时、
// 首先比较 unique_value 是否相等,以避免错误释放锁。
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end这样就通过SET命令和Lua脚本完成了单个Redis节点上分布式锁的加锁和解锁。
4. 共享会话信息
通常我们在开发登录模块时,会使用Session来保存用户的登录状态。这些Session信息会保存在服务器端,但这仅适用于单系统应用程序。如果是分布式系统,这种模式就不再适用。
例如,用户 1 的会话信息存储在服务器 A 上,但当用户 1 第二次访问时,会被分配到服务器 B 上,此时服务器上没有用户 1 的会话信息,就会出现需要重复登录的问题。 问题在于分布式系统每次都会将请求随机分配给不同的服务器。
分布式系统单独存储Session示例图:
因此,我们需要依靠Redis来对这些Session信息进行统一存储和管理。这样,无论请求发送到哪个服务器,该服务器都会去同一个Redis中获取相关的Session信息,从而解决了分布式系统中Session存储的问题。
在分布式系统中使用同一个Redis存储Session的示例图:
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
-
- oracle数据导入导出_oracle数据导入导出工具
- 继续学习Python中的while true/break语句
- python continue和break的区别_python中break语句和continue语句的区别
- 简单学Python——关键字6——break和continue
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
- Python 中 break 和 continue 傻傻分不清
- python中的流程控制语句:continue、break 和 return使用方法
- L017:continue和break - 教程文案
- 作为前端开发者,你都经历过怎样的面试?
- 面试被问 const 是否不可变?这样回答才显功底
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
