string介绍
- Redis中的key都是字符串,value的类型是存在差异的
- Redis中的字符串,直接就是按照二进制数据的方式存储的(不会做任何的编码转换,存的是啥,取出来就还是啥),不仅仅可以存储文本呢数据,还可以存储整数,普通的文本字符串,JSON,xml,二进制数据
- 但是对于音频视频来说,体积可能会比较大,Redis对于string类型,限制了大小最大值,并且Redis还是单线程模型,希望进行的操作都是短平快的类型,访问音视频会慢
- 对于MySQL,默认字符集是拉丁文,插入中文,就会失败;但是对于redis是没有这些限制的,插入什么,取出来就是什么;一般来说redis取出的字符不会是乱码,除非是编码对不上
string的常用命令
string的两个关键命令set和get
- set key value ex 10还可以使用px 毫秒
- nx-如果key不存在,才设置;如果key存在,则不设置(返回nil)
xx-如果key存在,才设置(相当于更新key的value);如果key不存在,则不设置(返回nil)
还有一些命令
setnx,setex,setpx
redis给出的文档说明:一个[]相当于一个独立的单元,表示可选项
其中|表示“或者”的意思,多个只能出现一个
[]和[]之间,是可以同时存在的
set key value
expire key 10
两次命令进行打包,可以说是原子操作
如果key不存在,创建新的键值对
如果key存在,则是让新的value覆盖旧的value,可能会改变原来的数据结构
原来这个key的ttl(生存时间)也会失效
一个有趣的技巧,清除redis上所有的数据~删库
flushall 可以把redis上所有的数据带走~~
get
语法较为简单,也就是get key获得value,对于get来说只支持获得字符串类型的value,如果value是其他类型,那么就会出错
mset
mget
mset和mget能够一次操作多组键值对
一下操作很多key的网络通信成本比一次通信操作一个key的成本更低
虽然可以使用mset和mget提高效率,但如果一次请求太多可能会堵住,所以要适量
mset和mget的时间复杂度均为O(N),这个N并不是所有key的数量,而是要操作key的个数,可以认为时间复杂度为O(1)
setnx
setnx表示不存在才能设置,存在则设置失败
设置超时时间,单位是秒,redis-cli会有自动补全
psetnx
设置key的过期时间,单位是毫秒
这三个命令均是对set进行封装,进行缩写,之所以这样写,就是为了操作更符合人的直觉,使用者的门槛月底,要背的东西越少
incr
incr-针对value+1
此操作的返回值就是+1之后的值
此时key对应的value得是整数
value的值也不可以很大
范围是8个字节也就是2^64,相当于C++中的long long,Java中的long
如果incr操作的value不存在,那么就会创建一个key-value键值对
incrby
incrby-针对value+n
同理若是字符串非常大,则会报错,若是针对不存在的值,也会创建一个新的键值对结构
若是增加一个负数可以不?可以,那还要decrby干啥呢?答案就是符合直觉
decr
decr-针对value-1
和incr的用法一致,且相互对应的注意事项也是一致的
和incr一致,也会产生新的键值对
decrby
decrby-针对value-n
同样和incrby的用法一致
同样也可以 - (-10) 为了符合直觉
incrbyfloat
incrbyfloat-针对value+/-小数
针对浮点数作处理,这里并没有对应的decrbyfloat
只能用加上负数的形式实现减法,虽然此处没有减法版本的浮点数操作,但是redis本身就对整数情有独钟,所以无可厚非啦~
上述操作的时间复杂度为O(1),因为只对某个key所对应的value进行计算
由于redis处理命令的时候,是单线程模型,多个客户端同时针对同一个key进行incr操作,不会引起“线程安全”问题
字符串也支持一些常用的操作,拼接/获取/修改字符串的部分内容,获取字符串长度
append
使用append命令
返回值是字符串整体的长度,单位是字节,redis不会对字符编码做任何处理(redis不认识字符,只认识字节),XShell终端默认的编码方式是utf8,那么一个汉字的长度是3个字节
如果append一个不存在的key,那么就会创建一个新的键值对
获得的value是一个什么?是一个原始的utf8编码所表现出来的
有没有什么办法可以让redis客户端展示”你好哈哈“这几个字符呢?
在启动redis客户端的时候,加上一个--raw这样的选项就可以使redis客户端能够自动地把二进制进行翻译
不小心按到了ctrl+s怎么办,一个是重新启动,另一个就是ctrl+q,因为ctrl+s是冻结画面,ctrl+q就是取消冻结
此时发现可以让终端尝试翻译,此时成功了
getrange
获取字符串中的字串,在C++中叫做stu::substr,在Java中叫做String.subString
图中的start end是我们要传的参数
redis中指定的区间是闭区间,C++和Java中谈到一个区间,大多都是前闭后开
正常下标都是从0开始的整数,redis的下标是可以支持负数的,-1是倒数第一个元素,下标为len - 1的元素
如果是汉字,切出来就不一定是原来的完整汉字了
切出来的不一定在utf8编码表上查出什么来
上述问题在C++中同样存在,Java中就没事,Java中字符串的基本单位,是字符,占两个字节
C++中字符串的基本单位是字节;Java中相当于String帮我们把汉字的编码转换都处理好了
setrange
把字符串对应的子串进行修改
offset-偏移量(从第几个字节开始进行替换,结束看value的长度)
value-要修改的字符
返回值是替换之后新的字符串的长度
如果当前value是一个中文字符串,进行setrange的时候可能会弄出问题
如果给一个未创建的key所对应的value进行转换,则会创建一个新的键值对结构
凭空多生成一个字节,也就是\x00
strlen
获取到字符串的长度,单位是字节
C++中,字符串的长度本身就是用字节为单位,Java中,字符串的长度则是以字符为单位
MySQL中的varchar(N),N单位就是字符,MySQL中的字符也是完整的汉字
Java中的char一个汉字占两个字节(unicode),Java中的String则是一个汉字占三个字节(utf8)
Java标准库的内部进行上述操作的过程中,我们一般感受不到编码方式的转换的
当value存放的类似不是string时,报错
string命令总结
指令 | 作用的效果 | 时间复杂度 |
set key value | 设置指定的key-value结构 | O(1) |
get key | 获取key的值 | O(1) |
del [key ...] | 删除指定的key | O(K) |
mset key value [key value ..] | 批量设置指定的key和value | O(K) |
mget key [key ..] | 批量获取key的值 | O(K) |
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) |
getrange key start end | 获取指定key的从start到end的部分值 | O(N) |
string内部的编码:
- int:64位/8个字节的长整型
- embstr:压缩字符串,小于等于39个字节的字符串
- raw:普通字符串,适用于更长的字符串大于39个字节的字符串
- 使用object encoding key来判断编码方式
某个业务场景,有很多很多的key,类型都是string,但是每个value的string长度都是100左右
更关注与整体的内存空间,因此这样的字符串使用embstr来存储也不是不能考虑
上述效果具体怎么实现?
- 先看redis是否提供了对应的配置项,可以修改39这个数字
- 如果没有提供配置型,就需要针对redis源码进行魔改
使用redis存储小数本质上还是当作存储字符串,小数进行字符运算,都需要将字符串转换为小数,进行运算,结果再转换位字符串
string类型的应用场景
作为缓存
整体思路:应用服务器访问数据的时候,先查询redis,如果redis上数据存在,就直接从redis取数据交给应用服务器,不继续访问数据库了;如果redis上数据不存在,再读取MySQL,把读到的结果,返回给应用服务器,同时,把这个数据也写入到redis中
redis这样的缓存,经常用来存储”热点“数据,高频被使用的数据,结合业务场景有很多种方式
刚才描述的过程相当于是把最近使用到的数据作为热点数据(暗含了一层假设,某个数据一旦被使用到了,那么很可能在最近一段时间会被反复用到)
上述策略存在一个明显的问题,随着时间的推移,肯定是会有越来越多的key在redis上访问不到
从而从MySQL读取并写入redis了,此时redis中的数据不是就越来越多吗?
- 在把数据写给MySQL的同时,给这个key设置一个过期时间
- redis也在内存不足的时候,提供了淘汰策略
计数功能-记录视频播放次数
redis并不擅长数据统计,比如想在上述的Redis中统计播放量前100的视频有哪些,基于Redis搞就很麻烦,相比之下,如果是MySQL来存储上述数据,一个sql就搞定了
上述的异步方式同步其他数据源:写入统计数据仓库的步骤,异步的:不是说来一个播放请求,这里就必须马上写一个数据
共享会话-Session分散存储
Session(服务器存储数据的机制)
Cookie(浏览器存储数据的机制)
对于两种机制可以使用学生卡与服务器存储学生的数据来理解
Session
当学生使用学生卡登录学校系统时,系统会创建一个学生档案,记录学生的个人信息、课程表、成绩等。这个档案存储在学校的服务器上,而不是在学生卡上。
Cookie
当学生使用学生卡登录时,学校系统会在学生卡上贴一个标签(Cookie),这个标签包含了一些基本信息,比如学生的ID和一些设置。每次学生使用学生卡时,系统通过读取这个标签来识别学生。
如果每个应用服务器,维护自己的会话数据,此时彼此之间不共享,用户请求访问到不同的服务器上,就可能会出现一些不能正确处理的情况了
此时所有的session数据都被各个服务器共享了
手机验证码
- 生成验证码-用户输入手机号,点击获取验证码(限制一分钟之内,最多获取5次验证码)
- 检查验证码-把短信收到的验证码这一串数,提交到系统中,系统进行验证验证码是否正确
- 可以借助redis来实现限制一分钟之内,最多获取5次验证码
- 可以使用incr和ex命令来设置一分钟之内设置发5次验证码