深入学习 Redis - 深挖经典数据类型之 string

目录

前言

一、string 类型

1.1、操作命令

set / get (设置 / 获取)

mset / mget(批量 => 设置 / 获取)

setnx / setex / psetex (设置时指定不同方式)

incr / incrby / decr / decrby/ incrbyfloat(自增自减)

append(字符串拼接)

getrange(获取指定区间的字符串)

setrange(替换指定区域字符串)

strlen(获取字符串长度)

1.2、string 类型内部编码方式

1.3、应用场景

缓存功能

计数功能

共享 session 会话

手机验证码


前言


redis 中所有的 key 都是字符串,value 的类型是存在差异的,因此出现了操控不同 value 的命令,接下来,就一起来学习一下吧~

Ps1:接下来,我给出的指令都是按照 Redis 官方文档的语法格式来解析的,[ ] 相当于一个独立的单元,表示可选项(可有可无),其中 | 表示 “或者” 的意思,多个只能出现一个,[ ] 和 [ ] 之间是可以同时存在的.

Ps2:一个快速失去年终奖的小技巧 —— 清除 redis 上所有的数据 =》 FLUSHALL,这个操作可以把 redis 上所有的键值对全部带走.

一、string 类型


Redis 中的 string 是直接按照二进制数据的方式进行存储的,也就是说不会进行任何编码转化,存的是啥,取出来还是啥(不同于 mysql ,插入中文就会失败).

不仅可以存储文本数据、整数、普通文本字符串、JSON、xml,还可以存储二进制数据(图片、视频、音频...),但是 Redis 对于 string 类型限制了大小最大是 512M(不要记这个数字,因为可以配置),一般不会存放像音频视频这种比较大的数据,因为 Redis 是单线程模型,希望进行的操作都能比较迅速.

1.1、操作命令

set / get (设置 / 获取)

set 如果 key 不存在,则创建新的键值对,如果存在,则覆盖旧的 value,并且可能会改变原来的数据类型,原来的 key 的 ttl (过期时间)也会失效.

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

NX:如果 key 不存在,才设置新的 value,如果存在,则不设置,返回 nil.

XX:如果 key 存在,才设置新的 value,如果不存在,则不设置,返回 nil.

EX:表示秒级别.

PX:表示毫秒级别.

get 只支持 字符串类型的 value,若是其他类型就会报错.

GET key

一些用法如下

mset / mget(批量 => 设置 / 获取)

和 set 和 get 类似,区别就是 mset 和 mget 可以一次操作多组键值对,这样做的目的就是因为网络通讯也是也是需要开销的,分成多个指令就会有多次网络通信,就有可能导致阻塞.

MGET key [key ...]
MSET key value [key value ...]

时间复杂度都是:O(N),N 是 key 的数量

Ps:由于这里设置的时候一般不会设置太多,因为如果一次设置 10w 哥键值对,就有可能把 redis 给阻塞了,因此这里的时间复杂度可以认为是 O(1).

setnx / setex / psetex (设置时指定不同方式)

SETNX key valueSETEX key [xxx] valuePSETEX key [xxx] value

这三个命令实际上前面已经讲到过了,只是针对 set 的一种缩写,之所以这样,是因为为了让操作更符合人的直觉(使用者的门槛越低,要背的东西就越少)~

setnx 等价 set key value nx,不存在才能设置,存在设置失败并返回 nil。

setex 等价 set key value ex [xxx]  ,设置 key 的过期时间(单位是秒).

psetex 等价 set key value px [xxx],设置 key 的过期时间(单位是毫秒).

 

incr / incrby / decr / decrby/ incrbyfloat(自增自减)

incr 针对 value + 1

incrby 针对 value + n

decr 针对 value - 1

decrby 针对 value - n

以上命令操作的 value 必须是整数.

incrbyfloat 针对 value + 或 - 小数,value 可以是整数也可以是小数(此处没有提供减法版本,是因为不常用,用的最多的是 redis 的计数操作,一般都是整数).

INCR keyINCRBY key decrement//后面的依次类推

这些操作的共同点如下:

  1. 时间复杂度都是 O(1).
  2. 这些操作的 key 如果不存在,就会把这些 key 的 value 先当作 0 来使用,然后再次基础上增减.

Ps:由于 redis 处理命令是单线程模型,因此多个客户端对同一个 key 进行操作,不会引起 “线程安全” 问题.

 

 

 

append(字符串拼接)

append 可以对 redis 中的字符串进行拼接操作,若 key 不存在,则对空字符串进行拼接,返回值是字节.

值得注意的是 redis 中的字符串不会对字符编码做任何处理(redis 不认识字符,只认识字节).

APPEND KEY VALUE

 

 

Ps:Xshell 终端默认的字符编码是 utf8. 在终端输入汉字后,也是按 utf8 编码的,一个汉字在 utf8 字符集中,通常是 3 个字节,因此,我们如果直接通过 get 获取汉字,获取到的只是字节信息,\x表示后面的字符是十六进制数.

我们可以在 redis 客户端启动的时候,加上 --raw 这样的选项,就可以使 redis 客户端能自动把二进制数据进行翻译.

注意:操作 linux 的时候,不要乱按 ctrl + s ,他的作用是 “冻结当前画面”(用来观察有些显示过快的日志信息),ctrl + q 是解除冻结.

getrange(获取指定区间的字符串)

getrange 用来获取指定区间的字符串,值得注意的是 redis 中指定的区间都是闭区间,下标从 0 开始,也可以用负数表示,-1 标识倒数第 1 个元素(可以理解为下标为 len - 1 的元素).

GETRANGE key start end

 Ps:如果字符串中保存的是汉字,此时切分,很可能切出来的就不是完整的汉字了,因为这里切割的单位是字节,那么从汉字中切出的结果在 utf8 码表上就不知道能查出什么了(前提是启动时加上了 --raw 这个参数,没加这个参数,查出来的就是类似 \x9c 这种信息)

 

setrange(替换指定区域字符串)

setrange 是从指定的偏移量(offset)开始替换该区域字符串的,返回值是 替换后 新的字符串长度,单位是字节.

SETRANGE key offset value

 

 

Ps:如果 value 是一个中文字符串,进行 setrange 时候,也会弄出问题的

 Ps:setrange 是可以对不存在的 key 操作的,并且会把 offset 之前的内容填充成 0x00(前提是启动时不能添加 --raw 参数)

 

strlen(获取字符串长度)

strlen 用来获取字符串的长度,单位是字节

STRLEN key

 

Ps:一个汉字通常是 3 个字节呀,Java 中为啥能用 2 字节的 char 表示汉字呢?

在 Java 中,字符串的长度是以字符为单位

刚刚说的汉字是 3 个字节,是因为使用 utf8 进行编码的.

而 Java 中的 char 是使用 unicode 进行编码的,一个汉字就是两个字节了.

Java 中的 String 则是使用 utf8 ,一个汉字就是 3 个字节了.


1.2、string 类型内部编码方式

string 内部有三种编码方式:

  1. int:用来表示 64 位/8字节 的整数,redis 通常用来实现 “计数”这样的功能,当 value 是一个整数的时候,此时 redis 可能直接使用 int 来保存.
  2. embstr:压缩字符串,针对短字符串进行的特殊优化,适合用来表示比较短的字符串.
  3. raw:普通字符串,底层类似一个 java 中 byte 类型的数组,适用于表示更长的字符串,只是单纯的字节数组,没有什么特别的优化.

 

 

 

Ps1:redis 存储小数,本质上还是以字符串来存储的,这就意味着每次进行算数运算,都需要把字符串转化成小数,进行运算,最后再转回字符串保存.

Ps2:不建议大家去记 39 这样的数字,例如当某个业务场景有很多很多 key ,类型都是 string ,但是长度都是 100 左右,为了整体的内存空间,我们使用 embstr 来存储也是可以考虑的~

具体做法:1.先看 redis 是否提供了对应的配置项,可以修改 39 这个数字 ;2.如果没有配置项,就需要对 redis 源码进行修改.

这就是为什么很多大厂往往都是自己自己造轮子,而不是使用业界成熟的开源组件,开源组件往往考虑的都是通用性,但是大厂往往会遇到一些极端的业务场景,就需要根据当时场景针对开源组件进行定制化.

1.3、应用场景

缓存功能

使用 redis 作为缓存, MySQL 作为数据库组成的架构

整体思路:

应用服务器访问数据的时候,先查询 Redis,如果 Redis 上存在该数据,就从 Redis 中取数据直接交给应用服务器,不用继续访问数据库了;如果 Redis 上不存在该数据,就会去 MySQL 中把读到的结构返回给应用服务器,同时,把这个数据也写入到 Redis 中.

由于 Redis 这样的缓存经常用来存储 “热点数据”,也就是高频使用的数据,那什么样的数据算高频呢?这里暗含了一层假设,某个数据一旦被用到了,那么可能在最近这段时间就可能被反复用到.

随着时间推移,越来越多的 key 在 redis 上访问不到,那 redis 的数据不是越来越多么?

  1. 把数据写给 redis 的同时,会给这个 key 设置一个过期时间.
  2. Redis 也有内存不足的时候,因此提供了 淘汰策略(后面详细讲).

计数功能

许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,例如网站视频的播放量,点赞数量......

Ps:这些都是相比较 MySQL 数据库而言的,Redis 可以通过简单的键值对操作完成计数任务并且实在内存中完成的,而 MySQL 就需要先查询数据库,然后 +1,然后再存入数据库,是在需要进行硬盘存储的

整体思路:

假设,用户点击某个视频,此时需要进行播放量 + 1 的操作,这时候应用服务器就会直接去操作 Redis ,执行 incr 命令,然后将返回的数据反馈给用户,最后 Redis 会以异步的方式将播放量同步到 MySQL 数据库中(异步就表示:这里并不是每一个播放请求,都需要立即写入数据~ 至于什么时候写入,需要根据实际的业务需求场景而定),将数据持久化.

Ps:实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按 照不同维度计数、避免单点问题、数据持久化到底层数据源等。

共享 session 会话

传统的 session 会话是分布在各自的应用服务器上的,彼此之间不共享,如果用户访问同时访问不同的服务器,就有可能出问题了~

通过 Redis ,我们就可以将 session 会话信息统一管理了~

举个例子(这里实际有很多例子,医院看病,换眼镜片...):

这就像是前段时间我去医院看病,唱歌长过度,声带出了点问题~

我就出去医院挂了个专家号,然后这医生就给用一些医疗手段给我看了一下,然后先给我开了一周的药,先吃着看,一周之后再来复查.

很快,这一周过去了,我再去复查,发现第一天给我看病那医生不在了!虽然今天也有个医生,但是他没给我看过,不了解我这边的情况(这就相当于是传统的,每个服务器都有管理自己的 session ,彼此之间互不干扰).

没办法就硬着头皮去了~  这个新医生就拿着我的就诊卡,在那机子上一刷,我之气的病例就在他电脑上了(这就相当于 Redis 将 session 信息共享了).

抽象一下:

我是病人,就相当于是客户端~

医生给我诊病,相当于是服务器,并且有多个.

而这里的服务器是以负载均衡(根据每个医生上班任务合理分配时间)的方式来提供服务的,因此就有可能出现两次访问同一网站使用的确实不同的服务器.

医院正确的做法,就是搞一个系统,向 Redis 这样共享会话,让多个医生共享.

手机验证码

手机验证码一般会限制以下类型:

  1. 1分钟内,最多能获取 5 次验证码.
  2. 每次获取验证码必须间隔 1 分钟.

使用 redis 的原因主要还是怕用户频繁获取验证码,对服务器压力过大,再者验证码信息需要有过期时间,基于数据库实现,成本太高~

发送验证码伪代码如下:

String 发送验证码(phoneNumber) {key = "shortMsg:limit:" + phoneNumber;// 设置过期时间为 1 分钟(60 秒) // 使⽤ NX,只在不存在 key 时才能设置成功 bool r = Redis 执⾏命令:set key 1 ex 60 nxif (r == false) {// 说明之前设置过该⼿机的验证码了 long c = Redis 执⾏命令:incr keyif (c > 5) {// 说明超过了⼀分钟 5 次的限制了 // 限制发送 return null;}}// 说明要么之前没有设置过⼿机的验证码;要么次数没有超过 5 次 String validationCode = ⽣成随机的 6 位数的验证码();validationKey = "validation:" + phoneNumber;// 验证码 5 分钟(300 秒)内有效 Redis 执⾏命令:set validationKey validationCode ex 300;// 返回验证码,随后通过⼿机短信发送给⽤⼾ return validationCode ;
}

检查验证码伪代码如下:

bool 验证验证码(phoneNumber, validationCode) {validationKey = "validation:" + phoneNumber;String value = Redis 执⾏命令:get validationKey;if (value == null) {// 说明没有这个⼿机的验证码记录,验证失败 return false;}if (value == validationCode) {return true;} else {return false;}}

 

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

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

相关文章

船舶中压配电板应用弧光保护,可解决母排故障短路问题,对于提高船舶电站的安全性、稳定性,降低经济损失具有重要意义。-安科瑞黄安南

摘要:船舶中压配电板弧光故障导致的设备损坏和停电事故,不仅会造成较大的经济损失,而且严重影响船舶电站的安全稳定运行,威胁船舶电站操作人员的安全。弧光保护是基于电力系统开关柜发生弧光故障时而设计的一套母线保护系统&#…

【布局优化】基于遗传算法的车间布局优化 车间设施布局优化【Matlab代码#50】

文章目录 【获取资源请见文章第5节:资源获取】1. 车间布局优化2. 基于GA的布局优化模型3. 部分代码展示4. 仿真结果展示5. 资源获取 【获取资源请见文章第5节:资源获取】 1. 车间布局优化 车间设施布置的规划一直是工业工程领域不断研究和探索的内容&am…

JavaWeb(5)——HTML、CSS、JS 快速入门

一、JavaScript 对象 二、JavaScript BOM对象 和 DOM对象 关于BOM主要对 Window 和 location 进行说明&#xff1a; 三、JavaScript 事件监听 事件绑定 常见事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">…

谷歌Bard:ChatGPT之外的第三选择

2023年年初&#xff0c;为了对应ChatGPT方面的压力&#xff0c;谷歌Bard仓促上线&#xff0c;此后便进入了低调前行。目前为止已经迭代了9个版本&#xff0c;也就是在近期的版本更新中支持了中文&#xff08;简体/繁体&#xff09;语言&#xff0c;对国内用户可以说又友好了一步…

物理层——数据通信基础知识

1.典型的数据通信模型 2.数据通信的相关术语 2.1 三种通讯方式 2.2 数据传输方式 2.3 常用编码方式&#xff08;数字信号&#xff09; 曼彻斯特编码 将一个码元分成两个相等的间隔&#xff1b; 前一个间隔为 低电平 &#xff0c;后一个间隔为 高电平 表示码元1&#xff1b; 码…

el-dialog 添加loading;avue-form 表单插槽

效果: 第一步&#xff1a;custom-class"publishDialog" 新起一个类名 <el-dialog title"发布配置" custom-class"publishDialog" :visible.sync"publishDialogVisible" width"800px" :append-to-body"true":b…

babel兼容低版本游览器

文章目录 1. webpack项目的搭建2. babel 命令行使用3. babel的预设与编译器流程4. babel项目中配置4.1 babel-loader与插件的使用4.2 babel-preset使用 5. 游览器兼容性使用5.1 browserslist工具与编写规则5.2 browserslist配置5.3 优化babel的配置文件 6. polyfill6.1 useBuil…

Flutter——最详细(NavigationRail)使用教程

NavigationRail 简介 一个 Material Design 小部件&#xff0c;旨在显示在应用程序的左侧或右侧&#xff0c;以便在少量视图&#xff08;通常在三到五个视图之间&#xff09;之间导航。 使用场景&#xff1a; 通过Row属性&#xff0c;左侧或右侧菜单栏按钮 属性作用onDestinati…

Halcon机器视觉-15种常用缺陷检测实例

一、Halcon 15种常用缺陷检测实例分享 缺陷检测是一种通过计算机视觉技术来检测产品制造过程中的缺陷的方法。该技术可以检测出产品表面的缺陷&#xff0c;如裂纹、凹陷、划痕、气泡等&#xff0c;并且可以实时监测和诊断制造过程中的问题。在制造业中&#xff0c;机器视觉缺陷…

GRE实验

题目参考&#xff1a; 实验步骤&#xff1a; 第一步&#xff1a;地址规划拓扑设计&#xff0c;配置IP地址 R1配置&#xff1a; <Huawei>system-view [Huawei]sy R1 [R1]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip address 192.168.1.1 24 [R1-GigabitEthernet0/0/1]in…

智能电表数据采集器

智能电表数据采集器是一种用于采集智能电表数据的设备&#xff0c;它可以将智能电表的数据传输到远程服务器上&#xff0c;以便进行数据分析和监控。智能电表数据采集器的主要功能是采集智能电表的实时数据&#xff0c;并将其发送到远程服务器上&#xff0c;从而实现对智能电表…

【windows测试通过】关于Godot导入外部音频文件的问题

file.open(filepath, file.READ) var buffer file.get_buffer(file.get_len()) #put data into audiostreamsample var stream AudioStreamSample.new() stream.data buffer 代码给出&#xff0c;还没有测试过。(godot3.2测试未通过&#xff09; 在运行时轻松加载外部音频…

业务安全分析第19期 | 今年暑假,博物馆的门票为什么抢不到?

目录 “黄牛”&#xff1a;加价代预约、加价售票、兜售野导游套餐 “黄牛”倒票带来的危害 “黄牛”为什么能够抢到票 博物馆与“黄牛”的门票攻防 “黄牛”使用的作弊软件有什么特征 技术上防范“黄牛”的作弊软件抢票 遏制“黄牛”倒票给博物馆带来的收益 随着暑期参观…

[nlp] GPT

一、联合训练任务 1.1 NTP(Next Token Prediction) gpt预训练的一个目标函数有两个,第一个是基础的下一个词预测任务,选择一个K窗口,将窗口中的K个词的embedding作为条件去预测下一个词。 1.2 TC(Text Classification) 第二个是一个分类任务,一段话给一个标签,然后去预…

离线数据仓库

一、数据仓库 1.数据仓库的概念 1)数据仓库的特点: 面向主题的:对数据进行整合、分析和归类的抽象集成的:将不同数据源的数据(业务数据、外部系统数据、埋点日志)经过统一编码、规范命名、字段类型转换等操作,整合到仓库相对稳定的:根据业务场景实时更新、一般会被长…

VIM文本如何复制到系统剪切板?

今天从vim上用鼠标复制代码&#xff0c;发现把VIM当中的行号也复制进去了&#xff0c;就很麻烦&#xff0c;于是简单研究了下&#xff0c;如果vim支持clipboard的话就比较好办&#xff0c;具体支持与否&#xff0c;使用命令查看&#xff1a; vim --version | grep "clipb…

startqueue(模拟实现及底层原理)

目录 容器适配器 STL标准库中stack和queue的底层结构 ​deque deque的原理 deque的缺陷 为什么选择deque作为stack和queue的底层默认容器 stack stack的介绍 stack的函数 stack的模拟实现 queue queue的介绍 queue的函数 queue的模拟实现 priority_queue prior…

Android中线程池

一、线程池的优点 说到线程池的优点就要先说一下不用线程池的坏处 在早些年开发都是直接new Thread()直接创建线程&#xff0c;倘若有N个异步就要创建N个线程&#xff0c;这会导致线程的频繁创建和销毁线程不可控&#xff0c;每个线程都各自执行&#xff0c;内存资源竞争激烈&…

js小写金额转大写 自动转换

// 小写转为大写convertCurrency(money) {var cnNums [零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖]var cnIntRadice [, 拾, 佰, 仟]var cnIntUnits [, 万, 亿, 兆]var cnDecUnits [角, 分, 毫, 厘]// var cnInteger 整var cnIntLast 元var maxNum 999999999999999.9999var…

Leetcode-每日一题【19.删除链表的倒数第N个结点】

题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1输出&#xff1a;[] 示例 3&#x…