图解Redis 02 | String数据类型的原理及应用场景

news2024/11/14 15:42:40

介绍

在 Redis 中,String 是一种重要的数据类型,是最基本的 key-value 结构,在这个结构中, value 是一个字符串。value 所能容纳的数据最大长度为512M

需要注意的是,这里的字符串不只指文本数据,它还可以是数字、JSON 对象、二进制数据等。

内部实现

String 类型的底层数据结构在 Redis 中主要由整数(int)和 SDS(Simple Dynamic String)实现。

SDS 与我们熟知的 C 语言字符串有所不同。Redis 选择 SDS 而非 C 语言的字符串实现,主要是因为 SDS 具有以下特点:

  • SDS 不仅可以保存文本数据,还可以保存二进制数据。这是因为 SDS 使用 len 属性来判断字符串的结束位置,而不是依靠空字符 (\0) 标记结束。同时,SDS 的所有 API 都会将 buf[] 数组中的数据作为二进制来处理。因此,SDS 不仅可以存储文本,还能存储图片、音频、视频、压缩文件等二进制数据。

  • SDS获取字符串长度的时间复杂度为O(1)。由于C语言的字符串没有记录自身的长度,所以获取长度的复杂度为O(n);而SDS结构体中的len属性记录了字符串长度,所以复杂度为O(1)。

  • Redis 的 SDS API 是安全的,拼接字符串不会导致缓冲区溢出。因为 SDS 会在拼接字符串前检查空间是否足够,如果空间不足,会自动扩展,避免缓冲区溢出的问题。

在 Redis 中,字符串对象的内部编码(encoding)有三种:int、raw 和 embstr,关系如下:

如果字符串对象存储的是一个整数值,并且这个整数值可以用 long 类型表示,那么该字符串对象会将这个整数值存储在RedisObject的 ptr 属性中(将 void* 转换为 long),并将其编码设置为 int。

注意:redisObject是Redis中用于表示数据结构(如字符串、列表、集合、哈希表和群体集合)的一个核心数据结构。通俗来讲,可以把它redisObject想象成一个“包装器”或“容器”,用于封装和管理存储在Redis内存中的数据。后面会有文章详细介绍。

如果字符串对象存储的是一个长度不超过 32 字节的字符串(在 Redis 3.2 版本及以后改成44字节),Redis 会使用简单动态字符串(SDS)来存储这个字符串,并将编码设置为 embstr。

embstr 编码是一种专门为存储短字符串而优化的编码方式,这种编码方式将字符串数据直接嵌入到 redisObject 结构体内部,redisObject 和 SDS 数据都是在同一块内存区域内,这意味着 Redis 只需分配一次内存空间,从而提高了内存管理效率和操作性能。

如果字符串对象存储的是一个长度超过 32 字节的字符串(在 Redis 3.2 版本及以后改成44字节),Redis 会使用简单动态字符串(SDS)来存储这个字符串,并将编码设置为 raw。raw 编码需要分配两次内存空间(分别为redisObject和sds)。

注意,不同版本的 Redis 中,embstr 编码与 raw 编码的界限长度有所不同:

  • 在 Redis 2.x 版本中,界限为 32 字节。
  • 在 Redis 3.0 到 4.0 版本中,界限为 39 字节。
  • 在 Redis 5.0 版本中,界限为 44 字节。

这里再总结一下,embstr 编码和 raw 编码都使用 SDS来保存字符串值,但它们在内存分配和数据存储方式上有所不同:

1. 内存分配
  • “raw”编码:需要进行两次内存分配,分别为Redis对象(“redisObject”)和SDS(Simple Dynamic String)分配内存空间。
  • “embstr”编码:只进行一次内存分配,同时为Redis对象和SDS分配连续的内存空间。
2. 数据存储
  • “raw”编码:Redis对象和SDS分别存储在不同的内存区域。
  • “embstr”编码:Redis对象和SDS存储在连续的内存区域中。
3. 修改操作
  • “embstr”编码的字符串是只读的,一旦需要修改字符串,就会转换为“raw”编码。
  • 可以直接修改“raw”编码的字符串。
4. 性能影响
  • “embstr“ 编码:在创建和释放内存时的开销较小,因为只需要进行一次内存操作。
  • “raw“ 编码:在修改字符串时性能更好,因为不需要进行编码转换。

选择使用哪种编码取决于具体的应用场景和需求。如果字符串长度较短且不经常修改,embstr 编码可能更为适合;而如果字符串较长或需要频繁修改,则 raw 编码可能是更好的选择。

常用命令

基本操作
# 设置value
 > SET name Sea 
OK 
# 根据key获取对应的value。
 > GET name 
"Sea" 
# 判断某个key是否存在。
 > EXISTS name 
( integer ) 1 
# 返回key存储的字符串值的长度。
 > STRLEN name 
( integer ) 3 
# 删除某个key对应的value。
 > DEL name 
( integer ) 1

#不存在则设置
>SETNX key value
(integer) 1
批量操作
# 批量设置key-value类型的值
> MSET k1 v1 k2 v2 k3 v3 
OK 
# 批量获取多个key对应的值
> MGET k1 k2 
1) "v1"
2) "v2"
计数器操作(当字符串内容为整数时可使用)
# 设置键值类型的值。
 > SET number 0 
OK 
# 将键中存储的数字值增加一。
 > INCR number 
( integer ) 1 
# 将键中存储的数字值加 100。
 > INCRBY number 100 
( integer ) 101 
# 将键中存储的数字值减少一。
 > DECR number 
( integer ) 100 
# 将键中存储的数字值减少 50。
 > DECRBY number 50 
( integer ) 50
# 设置key的 过期时间为30秒(此命令是 为已经存在的key设置 过期时间)
> EXPIRE name 30 
(integer) 1
# # 检查数据还有多长时间过期
> TTL name 
(integer) 23
# 设置key- value的同时设置过期时间
> SET key value EX 30
OK
> SETEX key 30 value
OK

应用场景

1. 统计计数

由于 Redis 是单线程处理命令的,执行命令的过程是原子的,因此 String 数据类型非常适合用于计数场景,如计算访问次数、点赞次数等。等等。

例如,我们可以使用 Redis 的 INCR 命令来计算一篇文章的访问次数:每当用户访问这篇文章时,我们就调用 INCR 命令增加该文章的访问次数。由于 Redis 的 INCR 命令是原子的,这保证了每次访问都会正确地更新计数值,而不会出现并发冲突的问题。

# 初始化文章访问次数为  0
> 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 缓存对象的常见方式有两种:

1. 直接缓存整个对象的 JSON

将对象转换为 JSON 字符串,然后将这个 JSON 字符串作为值存储在 Redis 的 String 类型中。这种方式简单直观,但当需要修改对象的某个字段时,就必须将整个 JSON 字符串取出,进行反序列化,修改字段后再重新序列化存储。

这可能会导致较大的性能开销,因为每次修改都涉及到整个对象的序列化和反序列化过程。

SET user:1 '{"name":"Bob", "age":18}'
2. 将对象的属性分开存储

将对象的各个属性分别存储在不同的 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 不存在,Redis 会插入 key 并返回成功,这表示锁获取成功。
  • 如果 key 已经存在,Redis 会插入失败,表示锁获取失败。

通常,为了确保锁不会因异常情况而长时间占用,还会给分布式锁设置一个过期时间。分布式锁的命令如下:

SET lock_key unique_value NX PX 10000
  • lock_key 是锁对应的key;
  • unique_value 是客户端生成的唯一标识,用于标识锁的拥有者;
  • NX 表示仅在 lock_key 不存在时才设置;
  • PX 10000 表示将 lock_key 的过期时间设置为 10 秒,以防客户端异常无法释放锁。

在解锁时,操作的步骤是删除 lock_key 键。但是,为了确保只有持有锁的客户端才能删除 lock_key,需要先验证 unique_value 是否匹配。如果匹配,则可以删除该键。

由于解锁过程涉及两个操作(验证和删除),需要确保这两个操作是原子的。这可以通过使用 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信息会保存在服务器端,但是这只适用于单系统应用。

在分布式系统中,这种模式就不再适用了。由于用户的请求可能会被分配到不同的服务器上,这会导致 Session 信息丢失,导致用户需要频繁重新登录。

例如,假设用户1第一次的登录请求是落到服务器A上,因此其 Session 信息保存在服务器A上。但是当用户1第二次访问的时候,可能会被分配到了服务器B上,而服务器B没有用户1的Session信息,那么就会提示用户1需要登录,这样的用户体验是极度不友好的。

为了解决这个问题,通常需要采用集中式的 Session 存储方案,例如使用 Redis 来存储 Session 信息,这样无论用户的请求被分配到哪台服务器,服务器都会去同一个Redis中获取相关的Session信息,样就解决了分布式系统中Session存储的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2142000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Https AK--(ssl 安全感满满)

免责声明:本文仅做分享! 目录 https探测 openssl Openssl连接服务器获取基本信息 连接命令: 指定算法连接: 测试弱协议连接是否可以连接: 得到的内容包括: sslscan 在线查询证书 https AK type 中间人AK sslsplit 工具…

【C++题解】1997. 孤独的素数

欢迎关注本专栏《C从零基础到信奥赛入门级(CSP-J)》 问题:1997. 孤独的素数 类型:二维数组 题目描述: 在一个 n 行 m 列的矩阵王国中,生活着一些整数,其中一些是素数,一些不是素数…

数据中心一体化智能运维方案

数据中心基础运维概述 在当今这个信息化的时代,数据中心作为支撑企业信息化建设的核心枢纽,其运维工作的重要性不言而喻。某数据中心深知运维工作的关键性,因此,从基础环境到网络、服务器存储以及基础软件,每一环节都力…

openeuler 22.03 lts sp4 使用 kubeadm 部署 k8s-v1.28.2 高可用集群

文章目录 [toc]废话篇这篇文章什么时候写的为什么是 openeuler为什么是 22.03 lts sp4高可用架构题外话 干活篇环境介绍系统初始化相关关闭防火墙关闭 selinux关闭 swap开启内核模块开启模块自动加载服务 sysctl 内核参数调整清空 iptables 规则安装各种依赖和工具修改 .bashrc…

R语言的基础知识R语言函数总结

R语言与数据挖掘:公式;数据;方法 R语言特征 对大小写敏感通常,数字,字母,. 和 _都是允许的(在一些国家还包括重音字母)。不过,一个命名必须以 . 或者字母开头,并且如果以 . 开头&…

Python数据分析案例60——扩展变量后的神经网络风速预测(tsfresh)

案例背景 时间序列的预测一直是经久不衰的实际应用和学术研究的对象,但是绝大多数的时间序列可能就没有太多的其他的变量,例如一个股票的股价,还有一个企业的用电量,人的血糖浓度等等,空气的质量,温度这些…

【工具推荐】Agently:一款灵活易用的 AI 应用开发框架

本文内容均来自个人笔记并重新梳理,如有错误欢迎指正! 如果对您有帮助,烦请点赞、关注、转发、订阅专栏! 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】(全…

LAMP环境搭建记录:基于VM的Ubuntu虚拟机

LAMP环境搭建记录:基于VM的Ubuntu虚拟机 我们从这样的角度出发: 一、简述LAMP环境 二、搭建LAMP环境 一、什么是LAMP环境 首先,该词是复合: ​ LAMP Linux Apache MySQL PHP 其中,逐项简述为: …

基于STM32的温度、电流、电压检测proteus仿真系统(OLED、DHT11、继电器、电机)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STM32F103C8T6 采用DHT11读取温度、滑动变阻器模拟读取电流、电压。 通过OLED屏幕显示,设置电流阈值为80,电流小阈值为50,电压阈值为60,温度阈值…

幼儿与非幼儿识别系统源码分享

幼儿与非幼儿识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

跨域训练评估BEVal:自动驾驶 BEV 的跨数据集评估框架

跨域训练评估BEVal:自动驾驶 BEV 的跨数据集评估框架 Abstract 当前在自动驾驶中的鸟瞰图语义分割研究主要集中在使用单个数据集(通常是nuScenes数据集)优化神经网络模型。这种做法导致了高度专业化的模型,可能在面对不同环境或…

苹果手机怎么清理照片内存

在使用苹果手机的过程中,照片往往是占用大量存储空间的主要元凶。随着时间的推移,无论是拍照、截图还是通过应用下载的图片,都会逐渐堆积,消耗宝贵的内存资源。合理清理照片内存不仅可以帮助你释放空间,还能优化设备的…

【算法】动态规划—最长回文子序列

思路分析 关于”回文串“的问题,是面试中常见的,本文提升难度,讲一讲”最长回文子序列“问题,题目很好理解: 输入一个字符串 s,请找出 s 中的最长回文子序列长度。 比如输入 s"aecda"&#xff0c…

vscode中如何配置c/c++环境

“批判他人总是想的太简单 剖析自己总是想的太困难” 文章目录 前言文章有误敬请斧正 不胜感恩!一、准备工作二、安装 VSCode 插件三、配置 VSCode1. 配置编译任务(tasks.json)2. 配置调试器(launch.json) 四、运行和调…

用EA和SysML一步步建模(07)蒸馏器系统上下文图01

用EA和SysML一步步建模的操作指南(01) 用EA和SysML一步步建模(02)导入ISO-80000 用EA和SysML一步步建模(03)创建包图和包的关系 用EA和SysML一步步建模(04)创建“需求组织”包图 …

jd 京东h5st 最新版 分析

声明: 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 有相关问题请第一时间头像私信联系我…

尚品汇-秒杀列表、详情、倒计时、获取下单码(五十二)

目录: (1)秒杀列表与详情 (2)在service-activity-client模块添加接口 (3)秒杀详情页面功能介绍 (1)秒杀列表与详情 封装秒杀列表与详情接口、 封装接口 package com…

Python 解析 Charles JSON Session File (.chlsj)

Charles 代理,是一款抓包软件,可以帮助我们抓取浏览器请求跟响应。 1、在 Filter 里面输入需要抓包的网址 2、右键 Export Session 3、文件类型选择 JSON Session File (.chlsj) 保存 4、解析响应的数据结构 response.body.text 是文本字符串。 # 导入…

SOMEIP_ETS_113: SD_Empty_Options_Array

测试目的: 验证DUT能够拒绝一个选项数组长度为0的SubscribeEventgroup消息,并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议,当接收到一个选项数组长度为0的SubscribeEventgroup消息时,能够…

网络设备登录——《路由与交换技术》实验报告

目录 一、实验目的 二、实验设备和环境 三、实验记录 1.通过 Console 登录 步骤1:连接配置电缆。 步骤2:启动PC,运行超级终端。 步骤3:进入Console 配置界面 2.通过 Telnet 登录 步骤1:通过 Console 接口配置 Telnet 用户。 步骤2:配置 super 口令 步骤3:配置登录欢迎…