当前位置: 首页 > news >正文

redis的数据类型:string

文章目录

    • String类型介绍
      • redis采用的字符集
      • json类型介绍
    • String类型的命令
      • set key value + [EX seconds] + [NX|XX]
      • incr key
        • incr对操作的key对应的value类型有限制吗?
        • `incr key`操作的返回值是什么?
        • incr操作的key可以不存在吗?
        • 多个客户端同时针对同一个key进行incr操作时,存不存在“线程安全”问题?
      • incrby key n
        • incrby操作的key如果不存在,结果会怎样?
        • incrby key n中n可以是负数吗?
        • incrby操作的key对应的value类型可以不是整数吗?
      • decr key
      • decryby key n
      • incrbyfloat key float
      • append key + Str
        • 如何拼接两个string类型的字符串?
        • append key + Str 中,key可以不存在吗?
        • `append key3 你好` 其中key3在语句执行时不存在,请问redis执行完这条命令之后返回的结果是什么?
      • getrange key [start] [end]
        • 如何从一个字符串中切分出一个子串呢?
        • Redis中start和end指定的区间,是左开右闭区间吗?
        • start和end可以传负数吗?
        • getrange key start end中,key对应是value可以是string类型的汉字吗?
      • SETRANGE key [offset] [value]
        • 我想把一个字符串从下标6开始往后,替换成我给的字符串str,我该怎么做?
        • offset指定的string下标从0开始还是从1开始?
        • setrange操作的key对应的value和替换用的value可以是中文字符串吗?
        • setrange操作的key可以不存在吗?
      • strlen key
        • 如何获得key对应的字符串的长度?
        • strlen操作的key可以不存在吗?
        • strlen操作的key对应的value可以不是string类型吗?
        • 如何获得一个string类型的value的内部编码?
        • string 内部哪些编码方式?分别都是在什么情况下使用?
        • 请问如果String类型的value中存的是“1.5”,那它内部的存储方式是什么呢?
        • 假如说现在有一个场景,有非常多的 key,类型都是 string。但是每个 value 的 string 长度都是 100 左右,请问string类型内部编码方式应该选哪种?
    • Redis中string类型的应用场景
      • 1.Redis最典型的应用场景:充当mysql的Cache
        • 伪代码实现
        • 什么是热点数据?
        • 上述策略有一个明显的问题,随着时间的推移, redis 中的数据肯定是越来越多,那Redis的容量其实是很有限的呀,容不下这么多数据怎么办?
        • Redis中为什么要给key加很多前缀?
      • 2.计数功能
        • 什么叫做,异步的方式将播放量同步到其他数据源?
      • 3.共享session会话
        • 为什么要共享session会话?
      • 4.手机验证码

String类型介绍

redis中的string类型最大的特点就是,string中的字符串不仅仅可以用来存储文本数据、整数和普通的文本字符串,还可以用来存JSON、xml、二进制数据(图片,视频,音频…),因为对于任何类型的数据,redis都会把数据按UTF-8编码成一串二进制存储起来
为了防止音频视频 体积过大,Redis 限制string类型的value大小最大是 512M。
为啥要做这个限制呢,主要还是因为Redis 是单线程模型,不希望进行的操作太复杂,希望操作能比较快速~~

redis采用的字符集

讲 mysql 的时候,我们知道 mysql 默认的字符集,是拉丁文。插入中文,就会失败,而redis就不会,因为redis的字符集只有UTF-8一种

json类型介绍

JSON(JavaScript Object Notation)即 JavaScript 对象表示法 ,是一种轻量级的数据交换格式。它能够以人类可读的文本形式表示数据,结构清晰。比如 {“name”: “John”, “age”: 30} ,很容易理解其中表示的是一个人的姓名和年龄信息。

json字符串不仅可以表示简单的键值对,也能嵌套表示复杂的对象和数组结构 。例如 {“students”: [{“name”: “Alice”, “score”: 90}, {“name”: “Bob”, “score”: 85}]} ,能表示多个学生的成绩信息。
几乎所有现代编程语言都有处理 JSON 的库或内置支持,方便不同语言编写的系统之间进行数据交互。

String类型的命令

set key value + [EX seconds] + [NX|XX]

Redis 的 SET 命令结合 EXNXXX 等选项可以实现更灵活的键值设置功能,具体格式和含义如下:

基本语法:

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

各选项含义:

  • EX seconds:设置键的过期时间,单位为秒(例如 EX 3600 表示 1 小时后过期)。
  • NX:全称 “Not Exists”,仅当键不存在时才设置值(常用于分布式锁场景,避免覆盖已有值)。
  • XX:全称 “Exists”,仅当键已存在时才设置值(常用于更新已有键)。

示例:

  1. 设置键 user:1001 的值为 zhangsan,并设置 1 小时过期:

    SET user:1001 zhangsan EX 3600
    
  2. 仅当 user:1001 不存在时,才设置值并过期 1 小时:

    SET user:1001 zhangsan EX 3600 NX
    

    (若键已存在,命令返回 nil,不做任何操作)

  3. 仅当 user:1001 已存在时,才更新值并保持 1 小时过期:

    SET user:1001 lisi EX 3600 XX
    

    (若键不存在,命令返回 nil,不做任何操作)

注意:

  • NXXX 是互斥的,不能同时使用。
  • 若同时指定 EXPX,以最后出现的选项为准。
  • set key value + ex + EX seconds 后面没有加NX或XX
    • 如果 key 不存在,则会创建新的键值对。
    • 如果 key 存在,则是让新的 value 覆盖旧的 value。可能会改变原来的数据类型。同时原来这个 key 的 ttl(生存时间)也会失效,即新的ttl也会覆盖旧的ttl,即使新语句中没有设置ttl,旧的ttl依然会失效
  • set key value ex 10相当于set key value; expire key 10,唯一区别在于前者是原子性操作,相当于把两步合为一步
  • SETNX + key value相当于 set key value NX
  • SETXX + key value 相当于 set key value XX
  • SETEX + key value相当于 set key value EX + seconds
  • SETPX/PSETEX + key value相当于 set key value PX + pseconds

incr key

incr key效果是让key对应的value=value + 1

时间复杂度:O(1)

incr对操作的key对应的value类型有限制吗?

此时key对应的value必须得是整数~(64位/8字节表示的整数. 相当于C++中的long long 或者Java中long 表示的范围是非常非常大的)

incr key操作的返回值是什么?

就是 + 1 之后的value

incr操作的key可以不存在吗?

incr操作的key如果不存在,就会把这个key的value当做0来使用.

多个客户端同时针对同一个key进行incr操作时,存不存在“线程安全”问题?

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

incrby key n

效果是让key对应的value=value + n

时间复杂度:O(1)

incrby操作的key如果不存在,结果会怎样?

先创造一个<key ,0>的键值对,value = 0 + n = n

incrby key n中n可以是负数吗?

可以

incrby操作的key对应的value类型可以不是整数吗?

不行

decr key

效果是value=value - 1
时间复杂度:O(1)
特殊情况处理和incr 保持一致,故不再多说

decryby key n

效果是value=value - n
特殊情况处理和incr 保持一致,故不再多说
时间复杂度:O(1)

incrbyfloat key float

效果就是value=value+float
时间复杂度:O(1)
注意:

  • 此时key对应的value必须得是浮点数~
  • float值可以为负,可以通过加上负数的形式来实现减法~~

append key + Str

如何拼接两个string类型的字符串?

格式为 append key + Str ,含义是将key对应的String类型的value和后面的String类型的Str,两个字符串拼成一个字符串,str追加到value的后面

append key + Str 中,key可以不存在吗?

可以的,执行完毕之后,key对应的value就是string类型的str

append key3 你好 其中key3在语句执行时不存在,请问redis执行完这条命令之后返回的结果是什么?

返回的结果是6,append key + Str的返回值是追加后字符串的总长度(单位是字节)。在本题中追加后的字符串就是你好,一个汉字用UTF-8去编码,占3个字节,两个汉字就是6字节

注意:

  • 当前咱们的xshell终端,默认的字符编码是utf8。在终端中输入汉字之后,也就是按照utf8编码的,而一个汉字在utf8字符集中,通常是3个字节~
  • 在启动redis客户端的时候,加上一个-raw这样的选项.就可以使redis客户端能够自动的把二进制数据尝试翻译.
  • 操作linux的时候,千万注意,不要乱按ctrl + s,ctrl + s在xshell中的作用是“冻结当前画面”,ctrl + q解除冻结~~

getrange key [start] [end]

如何从一个字符串中切分出一个子串呢?

使用getrange key start end
其中key对应的value就是我们要切分的目标字符串
start就是我们切分的起点下标,end就是我们切分的终点下标,执行完getrange key start end之后,返回的结果就是将原字符串从start到end这中间的一段切分下来形成的子字符串(包括start和end)

Redis中start和end指定的区间,是左开右闭区间吗?

不是,左右都是闭区间!redis 中指定的区间,都是闭区间!!!
C++ 和 Java 中,谈到一个区间,大多都是前闭后开(左闭右开)
编程这个大圈子中,区间大多是前闭后开,但是确实有特殊情况

start和end可以传负数吗?

在 Redis 的 GETRANGE 命令中,start 和 end 参数可以传入负数,表示从字符串的末尾开始计数,其中:

  • -1 代表字符串的最后一个字符
  • -2 代表倒数第二个字符,以此类推
getrange key start end中,key对应是value可以是string类型的汉字吗?

可以是可以,但是切起来会比较奇怪
如果字符串中保存的是汉字,此时进行子串切分,很可能切出来的就不是完整的汉字了
在这里插入图片描述

上述的代码,是强行切出了中间的四个字节.随便这么一切,切出的结果在utf8码表上不知道能查出啥了

上述问题,在C++中的substr中同样存在,Java中就没事,为什么呢?

  • Java中字符串的基本单位,是字符(Java的字符,占2个字节的字符). Java中相当于String帮我们把汉字的编码转换都处理好了~~
  • C++中字符串的基本单位是字节,C++这里头对于汉字的处理,是没那么完善的,就需要程序猿手动处理了

SETRANGE key [offset] [value]

我想把一个字符串从下标6开始往后,替换成我给的字符串str,我该怎么做?

SETRANGE key 6 str
当offset=6时,就会将key对应的字符串从下标6开始往后,替换成我给的str字符串

offset指定的string下标从0开始还是从1开始?

从0开始

setrange操作的key对应的value和替换用的value可以是中文字符串吗?

当然可以,但是如果当前value是一个中文字符串,进行setrange的时候,是可能会搞出问题的!!
我们都知道一个汉字要用三个字节来存,如果你从下标为1的位置开始,将下标123替换为一个汉字,那其实后面解释的时候,是会把012三个下标对应的三个字节解释成一个汉字,这样就肯定不是我们原来想换的汉字了,极有可能出现乱码

setrange操作的key可以不存在吗?

可以,setrange 针对不存在的 key 也是可以操作的。不过会把 offset 之前的内容填充成 0x00(注意这是一个字节,两位16进制数据就是一个字节 ),下图中\x00就是代表0x00
在这里插入图片描述

strlen key

如何获得key对应的字符串的长度?

strlen key
注意:strlen的返回长度单位是字节,而MySQL中varchar(N)此处N的单位是字符. mysql中的一个字符,就可以是一个完整的汉字,所以mysql中的一个字符可能是多个字节

C++中,字符串的长度本身就是用字节为单位.Java中,字符串的长度则是以字符为单位的. Java中的一个char == 2字节,Java中的char基于unicode这样的编码方式 就能够表示中文等符号~~
刚才说了半天,一个汉字通常是 3 个字节呀~~(编码方式是 utf8)Java 里头咋一个 2 字节的 char 就能表示汉字呢??
Java 中的 char 用的是 unicode编码. 一个汉字使用两个字节
Java 中的 String,则是用的 utf8. 一个汉字就是 3 个字节了.
Java 的标准库内部,在进行上述的操作过程中,程序猿一般是感知不到编码方式的变换的~~

strlen操作的key可以不存在吗?

可以,如果是这样,那么结果返回0

strlen操作的key对应的value可以不是string类型吗?

不可以,也就是说如果value不是string类型,那么strlen key执行就会报错

如何获得一个string类型的value的内部编码?

object encoding + key
在这里插入图片描述

string 内部哪些编码方式?分别都是在什么情况下使用?
  • int 64位/8字节的整数,若字符串中存储的是一串数字,比如“1234545”,像这样的字符串我们通常就用int编码方式
  • embstr 压缩字符串. 适用于表示比较短的字符串.,当字符串的长度小于39时,通常用embstr编码方式来存储
  • raw 普通字符串. 适用于表示更长的字符串,只是单纯的持有字节数组,当字符串的长度大于39时,我们通常用raw编码方式来存储
请问如果String类型的value中存的是“1.5”,那它内部的存储方式是什么呢?

实验告诉我们是embstr,即redis 存储小数,本质上还是当做字符串来存储(string类型中embstr编码方式)。这就和整数相比差别很大了。整数直接使用 int 来存(准确来说是一个 long long (C++) / long (Java)),比较方便进行算术运算。小数则是使用字符串来存。意味着每次进行算术运算,都需要把字符串转成小数,进行运算,结果再转回字符串保存……

假如说现在有一个场景,有非常多的 key,类型都是 string。但是每个 value 的 string 长度都是 100 左右,请问string类型内部编码方式应该选哪种?

本来按道理来说,长度大于39,我们应该选择raw,但是raw所占的空间更大,如果我们更关注整体的内存空间,这样的字符串使用 embstr 来存储也不是不能考虑。
那人家规定embstr最多只能存39字节,再多了存不下,你说怎么办呢?
很简单,改配置就行了,先看 redis 是否提供了对应的配置项,可以修改 39 这个数字,如果没有提供配置型,就需要针对 redis 源码进行魔改~

为啥很多大厂,往往是自己造轮子,而不是直接使用业界成熟的呢?
开源的组件,往往考虑的是通用性,但是大厂往往会遇到一些极端的业务场景。 往往就需要根据当前的极端业务,针对上述的开源组件进行定制化
网上也经常有种说法,大厂造轮子,一般是为了 kpi

Redis中string类型的应用场景

1.Redis最典型的应用场景:充当mysql的Cache

Redis+MYSQL架构下,数据的访问策略是什么?应用服务器访问数据的时候,会先查询Redis,如果命中了,会怎么处理,如果没有命中,又会怎么处理?
整体的思路:应用服务器访问数据的时候, 先查询 Redis.如果 Redis 上数据存在了, 就直接从 Redis 取数据交给应用服务器.不继续访问数据库了.
如果 Redis 上数据不存在, 再读取 MySQL. 把读到的结果, 返回给应用服务器同时, 把这个数据也写入到 Redis 中.
在这里插入图片描述

伪代码实现

下面的伪代码模拟了图2-10的业务数据访问过程:
1)假设业务是根据用户uid获取用户信息

UserInfo getUserInfo(long uid) {...
}

2)首先从Redis获取用户信息,我们假设用户信息保存在“user:info:”对应的键中:

// 根据uid得到Redis的键
String key = "user:info:" + uid;// 尝试从Redis中获取对应的值
String value = Redis 执行命令: get key;
// 如果缓存命中(hit)
if (value != null) {// 假设我们的用户信息按照JSON格式存储UserInfo userInfo = JSON 反序列化(value);return userInfo;
}
// 如果缓存未命中(miss)
if (value == null) {// 从数据库中,根据uid获取用户信息UserInfo userInfo = MySQL 执行 SQL: select * from user_info where uid = <uid>// 如果表中没有uid对应的用户信息if (userInfo == null) {响应404return null;}// 将用户信息序列化成JSON格式String value = JSON 序列化(userInfo);// 写入缓存,为了防止数据腐烂(rot),设置过期时间为1小时(3600秒)Redis 执行命令: set key value ex 3600// 返回用户信息return userInfo;
}
什么是热点数据?

Redis 这样的缓存,经常用来存储 “热点” 数据。所谓的热点数据,也就是高频被使用的数据,是如何定义的呢?(什么样的数据叫做被高频使用的数据?)
这个定义方式,结合业务场景有很多种方式的。刚才上述描述的过程,相当于是把最近使用到的数据作为热点数据。
暗含了一层假设: 某个数据一旦被用到了,那么很可能在最近这段时间就会被反复用到,也就是时空局部性

上述策略有一个明显的问题,随着时间的推移, redis 中的数据肯定是越来越多,那Redis的容量其实是很有限的呀,容不下这么多数据怎么办?
  1. 在把数据写给 redis 的同时,给这个 key 设置一个过期时间。到了过期时间,这个键值对就会被redis删除了
  2. Redis 也在内存不足的时候,提供了淘汰策略,比如我们机组中学到的LRUCache置换算法
Redis中为什么要给key加很多前缀?

主要就是为了区分不同种类的数据
mysql中,记录是存在表中的,而redis中没有表这样的结构,那我们就通过前缀对键值对进行归类
与 MySQL 等关系型数据库不同的是,Redis 没有表、字段这种命名空间,而且也没有对键名有强制要求(除了不能使用一些特殊字符 )。但设计合理的键名,有利于防止键冲突和项目的可维护性,比较推荐的方式是使用 “业务名:对象名:唯一标识:属性” 作为键名。
例如,MySQL 的数据库名为 vs,用户表名为 user_info,那么对应的键可以使用 “vs:user_info:6379”、“vs:user_info:6379:name” 来表示,如果当前 Redis 只会被一个业务使用,可以省略业务名 “vs:”。如果键名过长,则可以使用团队内部都认同的缩写替代,例如 “user:6379:friends:messages:5217” 可以被 “u:6379:fr:m:5217” 代替。毕竟键名过长,还是会导致 Redis 的性能明显下降的。

2.计数功能

许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。如下图所示,例如视频网站的视频播放次数可以使用Redis来完成:用户每播放一次视频,相应的视频播放数就会自增1。
在这里插入图片描述

企业为啥老乐意收集用户的数据??
说人话就是,统计你喜欢看啥,统计出来之后,就给你多推送啥
统计 => 进一步明确用户的需求 => 根据需求改进和迭代产品

Redis 并不擅长数据统计,比如,想在上述的 Redis 中,统计播放量前 100 的视频有哪些,基于 Redis 搞就很麻烦,相比之下,如果是 mysql 来存储上述数据, SELECT * FROM videos ORDER BY play_count DESC LIMIT 100;一个 sql 就搞定了~~

什么叫做,异步的方式将播放量同步到其他数据源?

异步写入的意思是,Redis把数据更改的信息传过来之后,不用看着mysql将信息同步,再继续工作,而是我给MYSQL发个信号,通知他一声,然后我就去干下一份工去了

实际中要开发一个成熟、稳定的真实计数系统,要面临的挑战远不止如此简单:防作弊(识别出恶意刷票的情况,并屏蔽这种情况)、按照不同维度计数(不只看播放量)、避免单点问题、数据持久化到底层数据源等。

3.共享session会话

为什么要共享session会话?

就比如你去医院看病。这个病一次瞧不好。需要多次治疗,但是你每次去的时候不一定能够确保都是同一个医生给你看病。那如果你前两次去的时候是甲医生给你看,第三次去的时候是乙医生给你看。那么乙医生头一次给你看病,不了解你的情况,工作就很难开展。那这个问题应该怎么解决呢?医院的解决方式就是给你发个病历,前两次去看病的时候,甲大夫都会将这次看病的过程记录在病历中,第三次你再去看病的时候,一大夫就不用再重新问了。而是直接看病例就能快速的了解你的情况。那么医院引入病例的操作就相当于redis中引入共享session会话
引入共享机制之前,对于某个用户,每个应用服务器都会维护一份该用户与当前服务器的会话数据,不同服务器之间的会话数据彼此之间是不同步的,比如我前两次追番的记录,都在1号服务器的session会话中,我第三次访问到了2号服务器,那这个2号服务器中就没有我的追番记录。
引入共享session机制之后,所有的session信息统一由一个服务器来管理和存储,此时所有的会话数据,就可以被所有服务器共享

4.手机验证码

这个过程很简单,厂家首先将验证码发送到用户的手机中,然后用户把短信收到的验证码这一串数,再提交到系统中,系统进行验证验证码是否正确
但是这个过程中也有一些要求,比如

  • 限制验证码5分钟内有效
  • 限制用户在 1分钟之内,最多获取 5 次验证码
    这个主要还是怕用户频繁获取验证码,对于我们的服务器压力过大。或者 (每次获取验证码必须间隔 30 秒~~ )

伪代码
限制一分钟内最多获取五次验证码
验证码5分钟内有效
将用户输入的验证码,与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.xdnf.cn/news/1483237.html

相关文章:

  • --定位--
  • isAssignableFrom() vs instanceof
  • CuTe C++ 简介02,gemm_device cuda kernel 的实现
  • Kernel中的cgroup2介绍
  • c++八股文1
  • ZooKeeper集群的安装与部署
  • 静态IP一般在什么业务场景中使用
  • Debezium日常分享系列之:Debezium 3.2.2.Final发布
  • 九月六号练习题
  • 【基础-判断】一个页面可以存在多个@Entry修饰的组件。
  • 【LeetCode热题100道笔记】排序链表
  • DMA寄存器学习
  • B.50.10.11-Spring框架核心与电商应用
  • 拯救珍贵回忆:AI照片修复让老照片重获新生
  • 推荐的Java服务环境:JDK17+ZGC(JDK 21的ZGC支持分代回收,性能更高)
  • 一阶低通滤波:从原理到实践,平滑数据的艺术
  • 备份压缩与存储优化:智能数据管理全攻略
  • 读写锁 shared_mutex 共享互斥量介绍
  • Dart HashMap:不保证顺序的 Map 实现
  • (二).net面试(static)
  • MySQL--索引和事务
  • simd学习
  • esbuild入门
  • Cursor安装使用 与 Cursor网页端登录成功,客户端怎么也登陆不上
  • 解析噬菌体实验核心:从材料选择到功能验证的标准化操作框架
  • 数据结构——队列(Java)
  • 基于STM32单片机的酒驾检测设计
  • OpenAvatarChat项目在Windows本地运行指南
  • 【基础-单选】关于自定义组件的生命周期下列说法错误的是
  • 四款主流深度相机在Python/C#开发中的典型案例及技术实现方案