Redis远程字典服务器 —— string类型详解

目录

一,string基本情况

二,string命令详解

[2.1 set命令选项](#2.1 set命令选项)

[2.2 setnx,setex](#2.2 setnx,setex)

[2.3 incr,incrby](#2.3 incr,incrby)

[2.4 decr,decrby](#2.4 decr,decrby)

[2.5 append拼接](#2.5 append拼接)

[2.6 getrange获取](#2.6 getrange获取)

[2.7 setrange修改](#2.7 setrange修改)

[2.8 strlen获取长度](#2.8 strlen获取长度)

[2.9 总结](#2.9 总结)

三,内部编码

四,string类型应用场景

[4.1 缓存(cache)](#4.1 缓存(cache))

[4.2 计数(count)](#4.2 计数(count))

[4.3 会话(session)](#4.3 会话(session))

一,string基本情况

  1. Redis中的字符串**,直接就是按照二进制数据方式存储的,不会做任何的编码转换**,存的是啥,取出来就是啥,这样就不仅仅能存文本数据了,整数,文本字符串,JSON,xml,甚至图片视频音频都可以存,但是很少存视频啥的,因为体积比较大
  2. 对于编码转换,我们在讲MySQL的时候,知道MySQL默认的字符集是拉丁文,插入中文就会失败,所以需要进行编码转换;但是Redis没有,所以Redis遇到乱码的概率更小
  3. 所以Redis对于string类型,限制了大小为512M,Redis单线程模型希望你进行的操作都能比较简单快速

二,string命令详解

2.1 set命令选项

前面说过,set命令后面还有一条很长的选项,现在就可以介绍下了:

  1. 方括号里面的EX,相当于在set创建键值对使设定过期时间 ,单位为秒,PX也是设置时间,单位是毫秒,中间的 " | " 表示 " 或 ",代表 EX 和 PX 只能同时存在一个:
  2. 后面方括号的NX和XX,其中NX表示如果key不存在就创建键值对 ,如果key存在就返回nil;XX是如果key存在就用新的value覆盖更新旧的value ,如果key不存在就返回nil,一样的," | "表示" 或 ",NX和XX只能用一个
  3. " FLUSHALL ",这个命令是清除当前Redis上所有的键值对 ,俗称"删库",在学习阶段我们可以使用该命令快速清除旧的键值对,但是以后在公司里后,绝对绝对绝对不能使用该命令!
  • get命令查询value时,只支持查字符串类型的value,如果value是其它类型,再使用get会返回错误
  • mset和mget的时间复杂度是O(N),N是你当前命令行给出的键值对的数量,所以可以认为是O(1)

2.2 setnx,setex

这两个命令就是上面几个set包括选项的简写,那么为什么会有简写呢

  • 之所以这样搞,就是为了让操作"更符合人的直觉",(使用的门槛更低,要记得东西更少)
  • 编程语言中,很多的关键词都是和自然语言相关的 ,后面我们去设置一些库,一些工具,写代码给别人用的时候,也尽量符合人们的直觉,不要设计得反直觉,俗称"反人类"

2.3 incr,incrby

这两个命令是对key的value做加法操作,其中incr是针对value + 1,incrby是针对value + n;所以这几个命令说是操作string,其实是在操作数字:

注意:

  • 当value为其它类型 或者为小数超过了64位,都会报错(64位相当于C++中的long long,Java中的long)
  • 当incr一个不存在的key时,会创建该key,并将value设置为1
  • incrby的n可以为负数 ,简单来说就是 " +(-n) " :

2.4 decr,decrby

decr 和 decrby 和上面的一样,只是变成了减法 ,其余规则和加法一样:

其实对于运算操作还有一个" incrbyfloat ",这个是针对浮点数的,而且这个没有decr版的,只能加上 负数 来实现减法,,然而以后我们很少使用浮点数去计数,所以这里了解一下就好啦~

上述的加法减法操作时间复杂度都是O(1) ,并且就算多个线程同时操作,也不会有线程安全问题

2.5 append拼接

append是我们的老朋友了,C++用过很多,表示字符串拼接 ,在Redis这里的话,如果key存在,就把新value拼接到旧的value后面,如果key不存在,相当于set创建键值对 (没法加选项)

然后是针对中文和编码的一些扩展:

我们将value设置为中文,会返回6,而Redis的返回值是字节 ,而前面也说过Redis不会对字符做任何的处理,由于XShell的默认字符串编码是utf8,所以一个汉字的长度通常是3字节:

然后我们用get命令后,会显示如下结果:

可以看到我们的"你好"两个字变成了一串代码,这其实是因为Redis在存储时只按照二进制的方式存的,不会做任何的编码处理 ,所以get的时候读取到的也只有二进制数,然后将二进制数按照16进制转换就变成了我们看到的样子

其实我们可以把打印的这串结果复制一下,去百度随便找一个编码转换器,其实可以看到使用utf8编码转换后,依旧是"你好" 两个字:

再进一步,如果我们想让Redis直接给我把"你好"打出来 ,该咋操作呢?可以在启动Redis客户端时,带上 " --raw ",使Redis客户端能够将二进制数据尝试翻译:

2.6 getrange获取

这个命令表示 " 获取字符串中指定范围的内容 ",start和end就是要获取的字符串的起始位置和终止位置,具体可以参考C++的substr字符串分割函数,或者Java的substring函数:

  • Redis中指定的区间是闭区间,在编程的大圈中,区间大多都是"前闭后开",但是也有像Redis这样的"前闭后闭"特殊情况
  • 正常下标都是从0开始的整数,但是Redis的下标是可以支持负数的,比如 -1 就是倒数第一个整数,下标为len - 1 的元素,这种允许下标为负数的机制和python一致
  • 但是如果字符串保存的是汉字,进行这种子串切分就不是完整的汉字了,C++中同样有该问题,但是Java不会,因为Java中字符串的基本单位是字符,C++中字符串的基本单位是字节
  • Java中的String 类会帮我们把汉字的编码转换都处理好,而C++对于汉字的处理就没那么完善,需要我们手动处理(推测原因可能是C++发展得比较早,对于中文没有较好的适配)

2.7 setrange修改

这个命令的作用就是"修改字符串具体位置的值",命令选项中的" offset "也是我们的老朋友了,就叫做" 偏移量 ", 表示从第几个字节开始进行替换,后面的value就是要进行替换的新内容:

注意:

  • 修改后的value,会覆盖旧的,如果新的value比旧的长,会延长旧的
  • 如果当前value是中文字符串,再用setrange,会搞出问题的,会被解析成乱码
  • 当key不存在时,也会创建键值对,当offset为n时,会在最前面的位置凭空生成n字节,每个字节的内容是0x00,然后新value就会追加到后面(如果启动Redis时带了--raw选项,就不会显示 " \x00 "了):

2.8 strlen获取长度

这个很简单,就算获取value的长度,和上面部分命令的返回值的数一样,这里就不演示了哈,不过还是有几个地方需要注意:

  • 获取到的单位长度是字节,C++中字符串的长度就是以字节为单位,Java中字符串的长度以字符为单位,(Java中一个char == 2个字节,并且Java的char基于unicode这样的编码方式,就能表示中文等符号)
  • MySQL我们学过varchar(N),N的单位就是字符,MySQL中的字符,也是完整的汉字,这样的一个字符,可能是多个字节
  • utf8和Unicode是两种编码方式,Java中的char是unicode,是2字节,但是Java的String,用的是utf8,是3个字节,Java的标准库内部,在进行上述操作过程中,程序猿一般是感知不到编码方式的变换的
  • 所以C++一个汉字3个字节,Java是2字节

2.9 总结

命令 执行效果 时间复杂度
incr key 指定的 key 的值 +1 O(1)
decr key 指定的 key 的值 -1 O(1)
incrby key n 指定的 key 的值 +n O(1)
decrby key n 指定的 key 的值 -n O(1)
incrbyfloat key n 指定的 key 的值 +n O(1)
append key value 指定的 key 的值追加 value O(1)
strlen key 获取指定 key 的值的⻓度 O(1)
setrange key offset value 覆盖指定 key 的从 offset 开始的部分值 O(n),n 是字符 串⻓度, 通常视 为 O(1
getrange key start end 获取指定 key 的从 start 到 end 的部分值 O(n),n 是字符 串⻓度, 通常视 为 O(1

三,内部编码

字符串类型的内部编码主要有3种:

  • int:8个字节的长整型
  • embstr: <=39 字节的字符串(压缩字符串,适用于比较短的字符串)
  • raw:>39 字节的字符串 (普通字符串,适用于更长的字符串,只是单纯的持有字节数组)
  • Redis会根据当前值的类型和长度动态决定使用哪种内部编码实现

39这个数字,不是绝对的,一切还是要以具体业务为准,比如我们也可以将100个字节长度的value也用embstr来实现,但是有要求:

  1. 先看Redis是否提供了对应的配置项,可以修改39这个数字
  2. 如果没有提供配置项,我们就需要对Redis的源码进行魔改

很多大厂,都是自己造轮子,不用业界成熟的,因为开源的软件,往往考虑的是通用性,但是大厂往往会遇到一些刁钻的业务场景,所以需要针对很多的开源软件进行定制化

四,string类型应用场景

4.1 缓存(cache)

  • 整体的思路是,应用服务器访问数据库的时候,先查绚Redis ,如果有,直接返回,如果没有就去MySQL去找 ,然后把该数据写进Redis再返回。Redis这样的缓存,经常用来存放热点数据(一个数据被用到了,那么有很高的概率会被重复用到,假设)
  • 问题:随着时间的推移,Redis里面的数据肯定是越来越多的 --> 所以我们可以设置过期时间,配合淘汰策略,清理过期的key,以后再详细介绍

4.2 计数(count)

计数功能最直白的例子就是:视频网站的视频播放次数

  • 企业为啥老喜欢手机用户数据? --> 为了统计,进一步明确用户的需求 --> 根据需求进一步改进和迭代产品
  • Redis并不擅长数据统计,比如我要知道播放量前100的视频,Redis搞就很麻烦,但是用MySQL的话,一个sql语句就搞定了
  • Redis写入MySQL的步骤:异步,将播放量同步到其它数据源(异步的,不是来一个播放就立马写数据,因为那样做的话效率太低)

4.3 会话(session)

这个我们之前已经讲到过:Redis远程字典服务器(0)------分布式系统-CSDN博客

举个场景:

  1. 我去看病,医生给我做了检查开了一些药,然后让我"一周后"来复查
  2. 一周后我来了,但是医生换了一个,上一周的那个医生没上班,所以这个医生可能不了解我的情况
  3. 所以,这个医生就拿着我的医保卡一刷,在他的电脑上就出现了我之前的病例

针对上面的场景:

  1. 我是病人,相当于一个客户端,医生相当于服务器给我提供服务,由于病人可能会多次访问服务器,服务器就需要很好的了解病人的情况
  2. 而医生有多个,服务器也有多个,是会通过负载均衡来提供服务的,所以病人两次来看病可能是不同的医生
  3. 所以需要用一台服务器来单独存储病人的情况,叫做session服务器,在"分布式系统"博客已经讲过
  4. 这个记录病人的病例,诊断结果,治疗情况等,就是所谓的会话,是客户端和服务器在交互过程中产生的一些中间状态的数据;而且由于这些数据具有临时性,所以也可以用Redis来存
相关推荐
Oak Zhang1 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
门牙咬脆骨2 小时前
【Redis】redis缓存击穿,缓存雪崩,缓存穿透
数据库·redis·缓存
门牙咬脆骨2 小时前
【Redis】GEO数据结构
数据库·redis·缓存
墨鸦_Cormorant4 小时前
使用docker快速部署Nginx、Redis、MySQL、Tomcat以及制作镜像
redis·nginx·docker
Dlwyz7 小时前
问题: redis-高并发场景下如何保证缓存数据与数据库的最终一致性
数据库·redis·缓存
飞升不如收破烂~8 小时前
redis的List底层数据结构 分别什么时候使用双向链表(Doubly Linked List)和压缩列表(ZipList)
redis
吴半杯9 小时前
Redis-monitor安装与配置
数据库·redis·缓存
ö Constancy10 小时前
设计LRU缓存
c++·算法·缓存
小王码农记10 小时前
vue中路由缓存
前端·vue.js·缓存·typescript·anti-design-vue
会code的厨子11 小时前
Redis缓存高可用集群
redis·缓存