【Redis】五大常见的数据类型之 String

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String,Hash,List,Set、Zset。今天我们就来详细的聊聊 Redis 这五大常见的数据类型之一 String;

前言

我们都知道 Redis 提供了丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)

今天我们就来详细的聊聊 Redis 这五大常见的数据类型之一 String

结构类型 结构存储的值 结构读写能力
String 可以是字符串,整数以及浮点数; 对整个字符串或字符串的一部分进行操作;
对整数或者浮点数进行自增或者自减操作;

应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。

 

概述简介

String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串, 也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512M

 

内部实现

String 类型的底层的数据结构实现主要是 int 和 SDS(简单动态字符串)。

SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言的字符串表示,因为 SDS 相比于 C 的原生字符串:

  • SDS 不仅可以保存文本数据,还可以保存二进制数据。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据,而且能保存图片、音频、视频、压缩文件这样的二进制数据。
  • SDS 获取字符串长度的时间复杂度是 O(1) 。因为 C 语言的字符串并不记录自身长度,所以获取长度的复杂度为 O(n);而 SDS 结构里用 len 属性记录了字符串长度,所以复杂度为 O(1)
  • Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题。

字符串对象的内部编码(encoding)有 3 种 :intrawembstr

image.png

如果一个字符串对象保存的是整数值,并且这个整数值可以用 long 类型来表示,那么字符串对象会将整数值保存在字符串对象结构的 ptr 属性里面(将 void* 转换成 long),并将字符串对象的编码设置为 int

如果字符串对象保存的是一个字符串,并且这个字符串的长度小于等于 32 字节(redis 2.+ 版本),那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码设置为 embstr, embstr 编码是专门用于保存短字符串的一种优化编码方式:

如果字符串对象保存的是一个字符串,并且这个字符串的长度大于 32 字节(redis 2.+ 版本),那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码设置为 raw

注意,embstr 编码和 raw 编码的边界在 redis 不同版本中是不一样的:

  • redis 2.+ 是 32 字节;
  • redis 3.0-4.0 是 39 字节;
  • redis 5.0 是 44 字节;

可以看到 embstrraw 编码都会使用 SDS 来保存值,但不同之处在于 embstr 会通过一次内存分配函数来分配一块连续的内存空间来保存 redisObjectSDS,而 raw 编码会通过调用两次内存分配函数来分别分配两块空间来保存 redisObjectSDS。Redis这样做会有很多好处:

  • embstr 编码将创建字符串对象所需的内存分配次数从 raw 编码的两次降低为一次;
  • 释放 embstr 编码的字符串对象同样只需要调用一次内存释放函数;
  • 因为 embstr 编码的字符串对象的所有数据都保存在一块连续的内存里面可以更好的利用 CPU 缓存提升性能。

但是 embstr 也有缺点的:

  • 如果字符串的长度增加需要重新分配内存时,整个 redisObject 和 sds 都需要重新分配空间,所以 embstr 编码的字符串对象实际上是只读的,redis 没有为 embstr 编码的字符串对象编写任何相应的修改程序。当我们对 embstr 编码的字符串对象执行任何修改命令(例如 append)时,程序会先将对象的编码从 embstr 转换成 raw,然后再执行修改命令。

 

常用指令

基本操作:

# 设置 key-value 类型的值
127.0.0.1:6379> SET name sid10t
OK

# 根据 key 获得对应的 value
127.0.0.1:6379> GET name
"sid10t"

# 判断某个 key 是否存在
127.0.0.1:6379> EXISTS name
(integer) 1

# 返回 key 所储存的字符串值的长度
127.0.0.1:6379> STRLEN name
(integer) 6

# 删除某个 key 对应的值
127.0.0.1:6379> DEL name
(integer) 1

批量操作:

# 批量设置 key-value 类型的值
# mset key value [key value ...]
127.0.0.1:6379> MSET name sid10t age 18
OK

# 批量获取多个 key 对应的 value
# mget key [key ...]
127.0.0.1:6379> MGET name age
1) "sid10t"
2) "18"

计数器(字符串的内容为整数的时候可以使用):

# 设置 key-value 类型的值
127.0.0.1:6379> SET cnt 0
OK

# 将 key 中储存的数字值 +1
127.0.0.1:6379> INCR cnt
(integer) 1

# 将 key 中存储的数字值 +10
# incrby key increment
127.0.0.1:6379> INCRBY cnt 10
(integer) 11

# 将 key 中储存的数字值 -1
127.0.0.1:6379> DECR cnt
(integer) 10

# 将 key 中存储的数字值 -6
# decrby key decrement
127.0.0.1:6379> DECRBY cnt 6
(integer) 4

过期(默认为永不过期):

# 设置 key 在 30 秒后过期(该方法是针对已经存在的key设置过期时间)
# expire key seconds [NX|XX|GT|LT]
127.0.0.1:6379> EXPIRE name 30
(integer) 1

# 查看数据还有多久过期
127.0.0.1:6379> TTL name
(integer) 20

# 设置 key-value 类型的值,并设置该 key 的过期时间为 20 秒
# set key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT uni
127.0.0.1:6379> SET name sid10t EX 20
OK
# setex key seconds value
127.0.0.1:6379> SETEX name 20 sid10t
OK

不存在就插入:

# 不存在就插入(not exists)
# setnx key value
127.0.0.1:6379> SETNX name sid10t
(integer) 1

 

应用场景

缓存对象

使用 String 来缓存对象有两种方式:

  • 直接缓存整个对象的 JSON: SET user:1 '{"name":"sid10t", "age":18}'
  • 采用将 key 进行分离为 user:ID:property,采用 MSET 存储,用 MGET 获取各属性值: MSET user:1:name sid10t user:1:age 18 user:2:name sidiot user:2:age 20

常规计数

因为 Redis 处理命令是单线程,所以执行命令的过程是原子的。因此 String 数据类型适合计数场景,比如计算访问次数、点赞、转发、库存数量等等。

比如计算文章的阅读量:

# 初始化文章的阅读量
127.0.0.1:6379[3]> SET atc:rcnt:10086 0
OK

#阅读量 +1
127.0.0.1:6379[3]> INCR atc:rcnt:10086
(integer) 1

#阅读量 +1
127.0.0.1:6379[3]> INCR atc:rcnt:10086
(integer) 2

# 获取对应文章的阅读量
127.0.0.1:6379[3]> GET atc:rcnt:10086
"2"

分布式锁

SET 命令有个 NX 参数可以实现「key 不存在才插入」,可以用它来实现分布式锁:

  • 如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
  • 如果 key 存在,则会显示插入失败,可以用来表示加锁失败。

一般而言,还会对分布式锁加上过期时间,分布式锁的命令如下:

127.0.0.1:6379[3]> SET lock uuid NX PX 10000
OK
  • lock 就是 key 键;
  • uuid 是客户端生成的唯一的标识;
  • NX 代表只在 lock 不存在时,才对 lock 进行设置操作;
  • PX 10000 表示设置 lock 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

而解锁的过程就是将 lock 键删除,但不能乱删,要保证执行该操作的客户端就是加锁的客户端。所以,解锁的时候,我们要先判断锁的 uuid 是否为加锁客户端,是的话,才将 lock 键删除。

可以看到,解锁是有两个操作,这时就需要 Lua 脚本来保证解锁的原子性,因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。

// 释放锁时,先比较 uuid 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。

共享 Session 信息

通常我们在开发后台管理系统时,会使用 Session 来保存用户的会话(登录)状态,这些 Session 信息会被保存在服务器端,但这只适用于单系统应用,如果是分布式系统此模式将不再适用。

例如用户一的 Session 信息被存储在服务器一,但第二次访问时用户一被分配到服务器二,这个时候服务器并没有用户一的 Session 信息,就会出现需要重复登录的问题,问题在于分布式系统每次会把请求随机分配到不同的服务器。

分布式系统单独存储 Session 流程图:

因此,我们需要借助 Redis 对这些 Session 信息进行统一的存储和管理,这样无论请求发送到那台服务器,服务器都会去同一个 Redis 获取相关的 Session 信息,这样就解决了分布式系统下 Session 存储的问题。

分布式系统使用同一个 Redis 存储 Session 流程图:

 

后记

Redis 五大常见数据类型之一的 String 就先讲到这里了,后续还会有其他类型的讲解呢,敬请关注!

参考资料:

📝 上篇精讲: 【Redis】概述与安装
💖 我是  𝓼𝓲𝓭𝓲𝓸𝓽,期待你的关注;
👍 创作不易,请多多支持;
🔥 系列专栏: Redis
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
7天前
|
存储 NoSQL Java
深入理解Redis数据类型Hashes原理
本文深入分析了Redis中的hashes数据类型,这是一种用于存储行记录的数据结构,允许一个key下存储多条记录。
深入理解Redis数据类型Hashes原理
|
1天前
|
NoSQL 安全 Java
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
这篇文章深入探讨了Redis中的String数据类型,包括键操作的命令、String类型的命令使用,以及String在Redis中的内部数据结构实现。
Redis6入门到实战------ 三、常用五大数据类型(字符串 String)
|
5天前
|
NoSQL Java Redis
Redis5种数据类型
这篇文章介绍了Redis的五种数据类型:字符串、列表、集合、有序集合和哈希,并通过代码示例展示了如何在Spring框架中使用RedisTemplate操作这些数据类型。
Redis5种数据类型
|
7天前
|
存储 NoSQL 算法
深入理解Redis数据类型Zset原理
本文深入探讨了Redis中的Zset(有序集合)数据类型,它是一种可以存储排序功能的集合,其中每个元素都具有一个浮点型的score属性,用于根据score进行排序。
深入理解Redis数据类型Zset原理
|
1天前
|
存储 NoSQL 算法
Redis6入门到实战------ 三、常用五大数据类型(列表(List)、集合(Set)、哈希(Hash)、Zset(sorted set))
这是关于Redis 6入门到实战的文章,具体内容涉及Redis的五大数据类型:列表(List)、集合(Set)、哈希(Hash)、有序集合(Zset(sorted set))。文章详细介绍了这些数据类型的特点、常用命令以及它们背后的数据结构。如果您有任何关于Redis的具体问题或需要进一步的帮助,请随时告诉我。
|
7天前
|
存储 缓存 NoSQL
深入理解Redis数据类型String原理
本文深入探讨了Redis中String数据类型的实现原理和使用场景,基于Redis 5.0版本进行分析。
深入理解Redis数据类型String原理
|
3天前
|
存储 缓存 NoSQL
Redis深度解析:部署模式、数据类型、存储模型与实战问题解决
Redis深度解析:部署模式、数据类型、存储模型与实战问题解决
|
5天前
|
NoSQL Java Redis
Redis字符串数据类型之INCR命令,通常用于统计网站访问量,文章访问量,实现分布式锁
这篇文章详细解释了Redis的INCR命令,它用于将键的值增加1,通常用于统计网站访问量、文章访问量,以及实现分布式锁,同时提供了Java代码示例和分布式锁的实现思路。
13 0
|
1月前
|
缓存 NoSQL Redis
Redis深度解析:核心数据类型之string
Redis 数据类型包括String、Hash、List、Set和Sorted Set。对于String类型的主要操作, 此外,还有通用的Key操作。
|
3月前
|
存储 NoSQL Java
【Redis】1、学习 Redis 的五大基本数据类型【String、Hash、List、Set、SortedSet】
【Redis】1、学习 Redis 的五大基本数据类型【String、Hash、List、Set、SortedSet】
80 0