Redis 数据类型:String
wptr33 2025-01-21 21:58 18 浏览
介绍
在 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 20
3. 分布式锁
在Redis中,String数据结构可以用来实现分布式锁。
SET命令有一个NX参数,可以实现“只有key不存在时才插入”,可以用来实现分布式锁:
- 如果key不存在,则说明插入成功,可以用来表示获取锁成功;
- 如果key存在,就会显示插入失败,可以用来表示获取锁失败。
一般来说,分布式锁也会加上一个过期时间。分布式锁的命令如下:
SET lock_key unique_value NX PX 10000
lock_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的示例图:
相关推荐
- Linux文件系统操作常用命令(linux文件内容操作命令)
-
在Linux系统中,有一些常用的文件系统操作命令,以下是这些命令的介绍和作用:#切换目录,其中./代表当前目录,../代表上一级目录cd#查看当前目录里的文件和文件夹ls#...
- 别小看tail 命令,它难倒了技术总监
-
我把自己以往的文章汇总成为了Github,欢迎各位大佬star...
- lnav:基于 Linux 的高级控制台日志文件查看器
-
lnav是一款开源的控制台日志文件查看器,专为Linux和Unix-like系统设计。它通过自动检测日志文件的格式,提取时间戳、日志级别等关键信息,并将多个日志文件的内容按时间顺序合并显示,...
- 声明式与命令式代码(声明模式和命令模式)
-
编程范式中的术语和差异信不信由你,你可能已经以开发人员的身份使用了多种编程范例。因为没有什么比用编程理论招待朋友更有趣的了,所以这篇文章可以帮助您认识代码中的流行范例。命令式编程命令式编程是我们从As...
- linux中的常用命令(linux常用命令和作用)
-
linux中的常用命令linux中的命令统称shell命令shell是一个命令行解释器,将用户命令解析为操作系统所能理解的指令,实现用户与操作系统的交互shell终端:我们平时输入命令,执行程序的那个...
- 提高工作效率的--Linux常用命令,能够决解95%以上的问题
-
点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf...
- 如何限制他人操作自己的电脑?(如何控制别人的电脑不让发现)
-
这段时间,小猪罗志祥正处于风口浪尖,具体是为啥?还不知道的小伙伴赶紧去补一下最近的娱乐圈八卦~简单来说,就是我们的小罗同事,以自己超强的体力,以及超强的时间管理能力,重新定义了「多人运动」的含义,重新...
- 最通俗易懂的命令模式讲解(命令模式百科)
-
我们先不讲什么是命令模式,先通过一个场景来引出命令模式,看看命令模式能解决什么样的问题。现在有一个渣男张三,他有还几个女朋友,你现在是不是还是单身狗,你就说你气不气?然后他需要每天分别叫几个女朋友起床...
- 互联网大厂后端必看!Spring Boot 中Runtime执行与停止命令?
-
你是否曾在使用SpringBoot开发项目时,遇到需要执行系统命令的场景?比如调用脚本进行文件处理,又或是启动外部程序?很多后端开发人员会使用Processexec=Runtime.get...
- Linux 常用命令(linux常用的20个命令面试)
-
日志排查类操作命令...
- Java字节码指令:if_icmpgt(0xA3)(java字节码使用的汇编语言)
-
if_icmpgt是Java字节码中的一条条件跳转指令,其全称是"IfIntegerCompareGreaterThan"。它用于比较两个整数值的大小。如果栈顶的第一个...
- 外贸干货|如何增加领英的曝光量和询盘
-
#跨境电商#...
- golang执行linux命令(golang调用shell脚本)
-
需求需要通过openssl生成rsa秘钥,然后保存该秘钥。代码实例packagemainimport("io/ioutil""bytes"&...
- LINUX磁盘挂载(linux磁盘挂载到windows)
-
1、使用root用户查看磁盘挂载情况:fdisk-l2、使用df查看当前磁盘挂载情况,根据和fdisk-l的结果进行对比,查看还有那些磁盘未使用3、挂载:mount磁盘挂载路径...
- Linux命令学习——nl命令(linux ln命令的使用)
-
nl命令主要功能为每一个文件添加行号,每一个输入的文件添加行号后发送到标准输出。当没有文件或文件为-时,读取标准输入...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- 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)