01- Redis 中的 String 数据类型和应用场景

1. 介绍

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

2. 内部实现

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 种:int、raw 和 embstr

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

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

如果字符串对象保存的是一个字符串,并且这个字符串的长度大于 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,然后再执行修改命令。

3. 常用指令

普通字符串的基本操作:

# 设置 key-value 类型的值
> SET name liu
OK
​
# 根据 key 获得对应的 value
> GET name
"liu"
​
#判断某个 key 是否存在
> EXISTS name
(integer) 1
​
# 返回 key 所储存的字符串值的长度
> STRLEN name
(integer) 3
​
# 删除某个 key 对应的值
> DEL name
(integer) 1

批量设置:

# 批量设置 key-value 类型的值
> MSET key1 value1 key2 value2
OK
​
# 批量获取多个 key 对应的 value
> MGET key1 key2
1) "value1"
2) "value2"

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

# 设置 key-value 类型的值
> SET number 0
OK
​
# 将 key 中储存的数字值增一
> INCR number
(integer) 1
​
# 将 key 中存储的数字值加 10
> INCRBY number 10
(integer) 11
​
# 将key 中储存的数字值减一
> DECR number
(integer) 10
​
# 将 key 中储存的数字值减 10
> DECRBY number 10
(integer) 0

过期(默认为永不过期)

# 设置 key 在 60 秒后过期(该方法是针对已经存在的 key 设置过期时间)
> EXPIRE name 60
(integer) 1
​
# 查看数据还有多久过期
> TTL name
(integer) 51
​
# 设置 key-value 类型的值,并设置该 key 的过期时间为 60 秒
> SET key value EX 60
OK

不存在就插入:

# 不存在就插入(not exists)
> SETNX key value
(integer) 1

4. 应用场景

4.1 缓存对象

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

  • 直接缓存整个对象的 JSON,命令例子:SET user1 '{"name":"a", "age": 18}'

  • 采用将 key 进行分离为 user:Id:属性,采用 MSET 存储,用 MGET 获取各属性值,命令例子:MSET user:1:name a user:1:age 18 user:2:name b user:2:age 20

4.2 常规计数

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

比如计算文章的阅读量:

# 初始化文章的阅读量
> SET aritcle:readcount:1001 0
OK
​
# 阅读量 +1
> INCR aritcle:readcount:1001
(integer) 1
​
# 阅读量 +1
> INCR aritcle:readcount:1001
(integer) 2
​
# 阅读量 +1
> INCR aritcle:readcount:1001
(integer) 3
​
# 获取对应文章的阅读量
> GET aritcle:readcount:1001
"3"

4.3 分布式锁

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

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

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

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

SET lock_key unique_value NX PX 10000
  • lock_key 就是 key 键;

  • unique_value 是客户端生成的唯一的标识;

  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;

  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

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

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

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

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

4.4 共享 Session 信息

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

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

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

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

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

相关文章

基于Zynq 7000 SoC的迁移设计

基于Zynq 7000 SoC的迁移设计 Vivado IDE工具使用IP集成器进行嵌入式开发。各种IP Vivado IDE IP目录中提供,以适应复杂的设计。您也可以添加 自定义IP到IP目录。 您可以将基于Zynq 7000平台处理器的设计迁移到Vivado design Suite中 使用以下步骤。 1.生成系统基础…

知攻善防应急响应靶机训练-Web3

前言 本次应急响应靶机采用的是知攻善防实验室的Web-3应急响应靶机 靶机下载地址为: https://pan.quark.cn/s/4b6dffd0c51a 相关账户密码 用户:administrator 密码:xj123456xj123456 解题过程 第一题-攻击者的两个IP地址 直接查看apache的log日志搜索.php 发现…

磁力搜索器

磁力链接是一种特殊的下载链接,磁力链接可以理解为一个文件识别码,而并非具体的资源地址,下载软件需要拿着这个识别码去整个互联网(DHT网络)去寻找持有该资源的用户(节点),如果找到则可以进行传输下载。一般年代越久远的磁力链接下…

今日总结2024/5/27

今日学习了状态压缩DP,状态压缩DP分为棋盘型(基于连通性)和集合型 Acwing.1064 小国王 在 nn的棋盘上放 k个国王,国王可攻击相邻的 8个格子,求使它们无法互相攻击的方案总数。 输入格式 共一行,包含两个整数 n和 k。 输出格式 共一行&…

图像处理知识积累

冲激响应 冲激响应是系统在单位冲激函数激励下引起的零状态响应,它与系统的传递函数互为傅里叶变换关系。在连续时间系统中,任一个信号可以分解为具有不同时延的冲激信号的叠加,因此冲激响应可以描述系统的基本特性。通过电路分析法求解…

【华为OD机试-C卷D卷-200分】篮球游戏(C++/Java/Python)

【华为OD机试】-(A卷+B卷+C卷+D卷)-2024真题合集目录 【华为OD机试】-(C卷+D卷)-2024最新真题目录 题目描述 幼儿园里有一个放倒的圆桶,它是一个线性结构,允许在桶的右边将篮球放入,可以在桶的左边和右边将篮球取出。 每个篮球有单独的编号,老师可以连续放入一个或多个…

三维大场景管理-3Dtiles规范

简介 : 这篇文章都是三年前写的了,一直在笔记库存中,今天把他放出来。主要是讲Cesium 的3Dtiles 格式,当然3Dtiles主要是解决场景管理大场景的LOD实现的问题,不管是剔除渲染性能优化之Culling 剔除或者 LOD 、3Dtiles…

【JAVA】JSONObject.fromObject(message)引发的JSONObject 详解

【JAVA】JSONObject.fromObject(message)引发的JSONObject 详解 一、JsonObject 简介二、JsonObject 基本操作2.1 、创建 JSONObject2.2、访问和操作 JSONObject2.3 生成 JSON 字符串2.4、示例代码 三、JsonObject 常见库3.1、使用 org.json 库3.2、使用 net.sf.jso…

Java 8的Stream API的使用说明,轻松实现集合的各种转换

Java 8中的Stream API为函数式编程带来了极大的便利,使得处理集合数据变得更加高效和简洁。以下是关于Java 8 Stream API的使用说明,我会尽量按照清晰的结构进行分点表示和归纳: 一、Stream API的基本概念 Stream:Stream是Java 8…

Educational Codeforces Round 158 B. Chip and Ribbon (贪心)

有一条带子,分为 n 个单元格,从左到右编号为 1 至 n 。最初,每个单元格中都写入了一个整数 0 。 单细胞与芯片玩游戏。游戏由几个回合组成。在第一轮中,Monocarp 将芯片放入色带的 1 -st 单元中。除了第一轮之外,在每…

SSM基于微信小程序的校园表白墙的设计与实现-计算机毕业设计源码58219

摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,校园表白墙微信小程序被用户普遍使用,为方便用…

Java面试八股之自旋是什么意思

Java中的自旋是什么意思 自旋是多线程编程中的一种同步机制,尤其在Java中与锁的实现密切相关。当一个线程尝试获取某个锁(如内置锁或显式锁)时,如果锁已被其他线程持有,通常的做法是将该线程置于阻塞状态,…

图形学概述

图形学应用 游戏 游戏的画面好坏如何鉴定呢? 看游戏画面是否够亮:渲染中全局光照的好坏 《只狼》 为什么卡通游戏画面看起来是卡通的呢? 《无主之地3》 这些都是图形学需要着手解决的问题 电影 电影《黑客帝国》的特效也是通过计算机…

软件测试面试题(九)

一:说说你对SQA的职责和工作活动的理解? 答:SQA就是独立于软件开发的项目组,通过软件开发过程的监控,来保证软件开发流程按照指定的CMM规程,对于不符合项目及时提出来的建议和改进方案,必要是可…

HTTPS加密过程

今天我们说https具体工作原理。 HTTPS概念 HTTPS是一种网络协议,传统的HTTP是明文传输,非常 不安全,所以HTTPS是基于HTTP基础上进行加密传输内容。 HTTPS使用加密传输方式 第一种是非对称加密,是前期建立连接时候使用的数据加密…

Unity 直线间隔放置物体

直线间隔放置物体 0. 新建一个空物体,挂上脚本ZYF_QuickPlaceObj 设置 间隔距离 和 预制体在Scene中拖动即可按间隔距离实例化物体物体的朝向始终朝向统一方向,并且可以在Scene中拖拽更改 传送门

QML-1- qml简介及项目创建

文章目录 1. QML 简介2. 项目创建3. 目录结构4. CMakeLists.txt 简单介绍5. 运行demo 1. QML 简介 根据官网介绍,qml 为qt一个模块,使用Qml语言开发应用程序和库提供了一个框架。它定义并实现了语言和引擎基础结构,并提供了一个API&#xff…

jQuery事件导读+其它方法

jQuery 事件导读一、事件注册二、事件处理1.内容2.例子,微博绑定事件3.off解绑事件4.自动触发事件 三、事件对象 其他方法一、拷贝对象二、多库共存三、插件 事件导读 一、事件注册 单个事件叫注册,多个事件叫处理 二、事件处理 1.内容 2.例子&#…

Pytorch中乘法函数torch.matmul() 的一种用法

主要记录下torch.matmul(A,B)的用法中的一种情况: 当A,B有一个是3维以上,另一个是3维或3维以上时,如果想要使用torch.matmul(A,B),必须同时满足: 1.A和B的最后两个维度满足矩阵乘法的要求。例如A的维度是…

今日上新——FCP

今年2月,我们FCC-E产品全新上线了专有D区,专门满足用户对高性价比的稀缺大机型需求。 产品研发组的小伙伴们根本停不下来~ 才几个月,我们又又上新了! 今天的新品是FCP(fastone Compute Platform)&#x…