目录
[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 总结)
[4.1 缓存(cache)](#4.1 缓存(cache))
[4.2 计数(count)](#4.2 计数(count))
[4.3 会话(session)](#4.3 会话(session))
一,string基本情况
- Redis中的字符串**,直接就是按照二进制数据方式存储的,不会做任何的编码转换**,存的是啥,取出来就是啥,这样就不仅仅能存文本数据了,整数,文本字符串,JSON,xml,甚至图片视频音频都可以存,但是很少存视频啥的,因为体积比较大
- 对于编码转换,我们在讲MySQL的时候,知道MySQL默认的字符集是拉丁文,插入中文就会失败,所以需要进行编码转换;但是Redis没有,所以Redis遇到乱码的概率更小
- 所以Redis对于string类型,限制了大小为512M,Redis单线程模型希望你进行的操作都能比较简单快速
二,string命令详解
2.1 set命令选项
前面说过,set命令后面还有一条很长的选项,现在就可以介绍下了:
- 方括号里面的EX,相当于在set创建键值对使设定过期时间 ,单位为秒,PX也是设置时间,单位是毫秒,中间的 " | " 表示 " 或 ",代表 EX 和 PX 只能同时存在一个:
- 后面方括号的NX和XX,其中NX表示如果key不存在就创建键值对 ,如果key存在就返回nil;XX是如果key存在就用新的value覆盖更新旧的value ,如果key不存在就返回nil,一样的," | "表示" 或 ",NX和XX只能用一个
- " 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来实现,但是有要求:
- 先看Redis是否提供了对应的配置项,可以修改39这个数字
- 如果没有提供配置项,我们就需要对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博客
举个场景:
- 我去看病,医生给我做了检查开了一些药,然后让我"一周后"来复查
- 一周后我来了,但是医生换了一个,上一周的那个医生没上班,所以这个医生可能不了解我的情况
- 所以,这个医生就拿着我的医保卡一刷,在他的电脑上就出现了我之前的病例
针对上面的场景:
- 我是病人,相当于一个客户端,医生相当于服务器给我提供服务,由于病人可能会多次访问服务器,服务器就需要很好的了解病人的情况
- 而医生有多个,服务器也有多个,是会通过负载均衡来提供服务的,所以病人两次来看病可能是不同的医生
- 所以需要用一台服务器来单独存储病人的情况,叫做session服务器,在"分布式系统"博客已经讲过
- 这个记录病人的病例,诊断结果,治疗情况等,就是所谓的会话,是客户端和服务器在交互过程中产生的一些中间状态的数据;而且由于这些数据具有临时性,所以也可以用Redis来存