二.String字符串
redis中的所有key都是字符串类型,只不过是value类型有差异。
redis中的字符串,直接就是按照二进制的方式存储的(不会做任何的编码转换(就好像mysql,它默认的字符集是拉丁文,插入中文就会失败,除非charset utf8),存的是啥,取出来的就是啥)。
这里的二进制存储,可不仅仅能存储文本数据也不仅仅能存储字符串,还可以存储整数,json,xml,图片/视频/音频……
但是音频视频提及可能比较大而redis对于string类型的存储有要求,限制大小是512MB,因为redis是单线程模型,希望进行的操作能很快
1.常见命令
set key value [ex seconds|px millseconds] [nx|xx]
中括号中的表示可写可不写
设置成功则返回ok,未执行则返回nil
ex:表示设置秒级过期时间
px:表示设置毫秒级过期时间
nx:表示只在key不存在时才设置,如果key已经存在,就不能设置(也就是不可修改key的值)
xx:表示只有key存在时才设置(也就是修改key的值),key不存在时不设置
如果nx和xx都没有写,那就表示key不存在时就将key添加到hash表中,key存在时就修改key的值
时间复杂度O(1)
get key 获取key对应的value
如果key不存在,就返回nil
注意,对于get只支持value为String类型的,如果该key对应的value是其他类型,使用get key就会报错
时间复杂度O(1)
mget key [key……] 一次性获取多个key的值
时间复杂度O(N),这里的N是key的数量,当key很少时,就近似看作O(1)。
mset 一次性设置多个key的值
但也不要太多,否则容易阻塞住
多次get和单次mget的区别:每一个命令都需要一次网络通信,所以使用mset/mget可以减少网络交互的次数,有效减少网络事件,性能相对较高
setnx 只有key不存在的情况下才可以设置;setxxkey存在时才可设置
设置成功返回1,未设置成功返回0
时间复杂度O(1)
2.计数命令
incr key 将key对应的String表示的数字增加一
如果key不存在,则视为key对应的value是0.如果key对应的value不是整形或者范围超过64为有符号整型,就会报错
时间复杂度O(1)
返回值是integer类型加完后的数值
incrby key decrement 将key对应的数加上指定的整数值
如果key不存在,默认key是0,如果key不是整形或超过64为有符号整型的范围,则报错
返回值,加完后的结果
时间复杂度O(1)
decr 将key对应的数减一
与incr同理
decrby 将key对应的值减去指定的整数值
incrbyfloat key increment 将key对应的值加上对应的数
这个命令与incrby的区别就是这里的key的值可以是浮点数,所加的值可以是整数也可以是浮点数
3.其他命令
append key value 在key对应的value后面追加指定的字符串
如果key不存在,其效果就相当于set。
返回值是追加完之后string字符串的长度
时间复杂度:如果追加到字符串不是特别特别长,那就可以看作是O(1)
如上可知,append返回值的长度单位是字节!!!而且通过get k 可以看出,redis的字符串,不会对字符编码做任何处理(redis不认识字符串,只认识字节)
当前,咱们使用的xshell终端,默认的字符编码方式是utf8,里面一个汉字是3个字节,所以在终端输入汉字后,就会utf8来编码并存储到redis中。get k时,得到的就是16进制的utf8编码。
那如果我们就是想要在get k时得到汉字呢?可以在启动redis客户端的时候加一个--raw选项,这就可以时redis客户端能够自动尝试把二进制数据进行翻译
如下:
getrange key start end
返回key对应的string字符串的子串,注意,这里的区间是闭区间,不像java中是左闭右开。可以使用复数表示倒数。-1表示倒数第一个字符,0表示第一个字符;超过string的偏移量时,会根据String的长度调整成正确的值
注意上面对于汉字的操作,这说明,这个偏移量也是按照字节数来分配的!!!
上述问题,在c++中同样存在,但java中没事。因为java中字符串的基本单位是字符,而c++中字符串的基本单位是字节
setrange key offset value覆盖字符串的一部分,从指定的偏移开始
返回值是替换后的字符串的长度
时间复杂度是O(N),N为value的长度,一般value较短,所以可以视为O(1)
同样的问题,这里的偏移量是按照字节计算的,所以如果这里的key的值是汉字的话,不正确的偏移会出问题。
还有一个问题,如果对不存在的key去进行setrange
由上可知,它会凭空多出几个字节
strlen key 获取key对应的string的长度
当key存放的类型不是string时就会报错
同样,单位是字节
时间复杂度O(1)
注意,java中的char时unicode编码,一个汉字是两个字节,然而String是utf8编码,一个汉字是三个字节。当调用string.charAt等类似的方法时,java标准库内部会惊醒字符编码方式的转换,而在进行上述操作的过程中,程序员感知不到编码方式的转换
flushall表示清除所有的key
4.内部编码
int:8个字节的长整型
embstr:小于等于39个字节的字符串。相当于压缩的表示形式
raw:大于39个字节的字符串
注意:
1.39这个数是不固定的,是可以修改的。
就比如某业务场景下:有很多key,类型都是String,但单个value的长度是100左右。如果更关注内存空间的占用,而不太在意效率等其他开销,就可以考虑使用embstr。
上述效果如何实现?1.先看看配置文件中有没有对应的配置项,2.如果没有就需要对源码进行魔改
2.整数直接使用int来存,准却来说是java中的long(因为是8个字节),但小数还是用字符串存储
所以在对小数进行加减运算时,就要先把字符串转化成小数,计算完后,再把结果转化成字符串。这样的转化是有开销的。所以redis计算整数时顺理成章的事情,而计算小数就要考虑成本开销。
5.String的典型应用场景
缓存功能
应用服务器访问数据:
先查询redis,若有则直接从redis获取数据并交给应用服务器;若无则去查询mysql,将读取到的数据交给应用服务器,同时把它也写到redis中。
为什么要同时写到redis中?redis只存热点数据(就是高频使用的数据),不同的应用场景下对高频的定义不同,其中一种定义就是近期使用过的数据。
但上述策略有一个问题:随着时间的推移,会有越来越多的key在redis上访问不到,从而要从mysql写到redis上,那么redis上的数据就会越来越多了。那这该怎么解决?目前有两种解决方案:一种是为这个key设置过期时间,另一种是redis有内存淘汰机制(后面会讲)
计数功能
许多应用都会使用redis作为计数的基础工具,它可以实现快速计数的功能,同时,数据可以异步处理或者落地到其他数据源。
比如记录视频的播放次数,每播放一次,对应的key就自增一次。由于redis自增很快,所以及时用户访问量很大,也可以应对的了。
而对于统计播放次数这样的操作,往往还需要进行其他转化、分析、处理,从而进一步对系统进行优化。所以就可以经redis导入其他存储介质,如hdfs中、mysql中(为了完成后续统计等相关需求。因为redis不擅长统计数据)
上面所说的导入到其他存储介质,也就是写入到其他数据源,使用的是异步的方式。什么是异步?就是说,不是来一个播放请求,就要立马写入一个数据同步到其他数据源,而是可以慢一点,慢一点没关系,只要保证最终都正确写入即可
共享会话
一个分布式的web服务,会将用户的session保存在各自的服务器上,但会出现这样的问题:处于负载均衡的考量,分布式服务会将用户的请求分不到不同的web服务器上,但是却无法保证每次请求都分发到同一个服务器上,这就会导致用户要多次登录。
为了解决这个问题,我们就可以将sessioon信息保存到一个公共服务器上,这个东西就可以使用redis来实现。这样的话,所有的会话数据就都可以被各个服务器共享了
手机验证码
步骤就是
1.生成验证码
2.获取验证码:
限制:一分钟内只能获取5次/每次获取的间隔必须超过30秒(主要是怕用户频繁获取验证码,对于我们服务器这边的压力过大)
3.检查验证码:把短信的数据提交到系统中,系统进行验证