🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(96平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- [1. Redis中的String的相关操作](#1. Redis中的String的相关操作)
-
- [1.1 字符串的存储方式](#1.1 字符串的存储方式)
- [1.2 关于string的常见命令](#1.2 关于string的常见命令)
- [1.3 string的其他命令](#1.3 string的其他命令)
- [1.4 字符串指令小结](#1.4 字符串指令小结)
- [2. String的内部编码方式](#2. String的内部编码方式)
- [3. string类型的具体应用场景](#3. string类型的具体应用场景)
-
- [3.1 缓存功能](#3.1 缓存功能)
- [3.2 计数功能](#3.2 计数功能)
- [3.3 共享会话(session)](#3.3 共享会话(session))
- [3.4 手机验证码](#3.4 手机验证码)
1. Redis中的String的相关操作
1.1 字符串的存储方式
Redis中,key的数据类型是一个string,但是value的数据结构可以是多种多样的,我们在之前的文章中介绍过.这里不再赘述.
这里我们需要注意的是.Redis中存储string的方法和java中,MySQL中的存储方式并不一样.Redis中存储字符串的时候,会直接存储字符串的二进制数据,不会对二进制数据做出任何的编码转换,存入的是什么,取出的还是什么,但是java和MySQL不一样,java中存储字符串的时候会涉及到编码方式的问题,MySQL中,字段不可以插入中文,这是因为MySQL的默认编码方式是拉丁文,与汉字不兼容.
既然Redis中可以存储二进制数据,那么就不仅仅可以存储字符串,还可以存储一些其他的二进制数据 ,比如图片,视频,音频等.
但是Redis是单线程的,Redis为了保证存储于获取数据的高效性,一般不可以存储占据空间太大的数据 ,默认情况下是不超过512M.
1.2 关于string的常见命令
- set
在前面的章节,我们曾经介绍过set
这个命令,但是之前的哪个set
命令还不是最全面的,set
除了可以设置键值对之外,还可以对键值对设置其他的属性.
set key value [ex seconds|px milliseconds] [nx|xx]
- 其中ex是为当前键值对设置过期时间,单位是秒 ,相当于
set key value; expire key seconds
,也可以使用px设置毫秒级别的过期时间. - nx是当前如果当前key存在,则不设置当前的key,直接返回nil,如果不存在,则设置 ,而xx与nx的效果恰好相反,当key存在的时候,覆盖掉当前的key,如果当前key不存在,则不设置.
- 其中ex是为当前键值对设置过期时间,单位是秒 ,相当于
- 夹带选项的
set
指令可以被后续的Redis命令取代,比如setex
,setnx
,setpx
等.- 如果使用xx选项,可能会改变原来value的数据类型,原来key的过期时间也会被覆盖掉
- 原地离职小技巧: 如果想要删除Redis服务器中的所有键值对,可以使用
flushall
指令.
代码演示:
shell
127.0.0.1:6379> set key1 value1 ex 10
OK
127.0.0.1:6379> ttl key1
(integer) 6
127.0.0.1:6379> ttl key1
(integer) 5
127.0.0.1:6379> ttl key1
(integer) 2
127.0.0.1:6379> ttl key1
(integer) -2
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> set key1 value2 nx
(nil)
127.0.0.1:6379> set key1 value2 xx
OK
127.0.0.1:6379> get key1
"value2"
127.0.0.1:6379> set key2 value1 ex 20
OK
127.0.0.1:6379> set key2 value3 xx
OK
127.0.0.1:6379> ttl key2
(integer) -1 //覆盖掉了原来的过期时间
- get
这个指令和我们之前提到过的get
指令没有什么区别,==唯一需要注意的一点就是,获取key对应的value的时候,value必须是string类型的数据,否则就会报错.
shell
127.0.0.1:6379> lpush key val1 val2 val3
(integer) 3
127.0.0.1:6379> get key
(error) WRONGTYPE Operation against a key holding the wrong kind of value
这里我们创建了一个value为列表的键值对,我们发现在用get访问的时候,程序出现了报错.
- mset
用来一次性设置多个键值对.
shell
127.0.0.1:6379> mset key1 val1 key2 val2 key3 val3
OK
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "key3"
- mget
用来一次性获取多个key对应的value.
shell
127.0.0.1:6379> mget key1 key2 key3
1) "val1"
2) "val2"
3) "val3"
我们在前面的章节的exists
指令提到过,我们能把多次查询合并到一步,我们一般不会使用分开好几步的查询.这样一方面会比较占用网络资源,另一方面会让网络请求拖慢Redis的执行性能.
但是我们使用上面两个请求的时候,也不可以一次性设置太多的key和value或者是一次性获取太多的value.否则就会让Redis服务器发生阻塞.
- setnx
与set key value nx
效果一样.key存在的时候不创建,不存在的时候才创建.
shell
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
3) "key3"
127.0.0.1:6379> setnx key1 value1
(integer) 0
127.0.0.1:6379> get key1
"val1"
- setex
setex key seconds value
与set key value ex seconds
效果一样.为键值对设置过期时间.这里要注意一下setex的语法格式,先写key,再写过期时间,再写value.
shell
127.0.0.1:6379> setex key4 10 value4
OK
127.0.0.1:6379> ttl key4
(integer) 5
127.0.0.1:6379> ttl key4
(integer) 4
127.0.0.1:6379> ttl key4
(integer) 2
127.0.0.1:6379> ttl key4
(integer) 0
127.0.0.1:6379> ttl key4
(integer) -2
- incr
incr key
针对某一个key的value+1.如果这个key的value不是64位的整型,则报错.如果这个key不存在的话,则创建这个key,其中value的值默认为0,之后对value+1.这个操作的返回值就是这个key的value+1之后的值.
shell
127.0.0.1:6379> set key1 1
OK
127.0.0.1:6379> incr key1
(integer) 2
127.0.0.1:6379> incr key2
(integer) 1
127.0.0.1:6379> mget key1 key2
1) "2"
2) "1"
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> incr key1
(error) ERR value is not an integer or out of range
- incrby
incrby key increament
针对某个key的value+指定的值.如果这个key的value不是64位的整型,则报错.如果这个key不存在的话,则创建这个key,其中value的值默认为0,之后对value+指定值.这个操作的返回值就是这个key的value+指定值之后的值.
shell
127.0.0.1:6379> mget key1 key2
1) "2"
2) "1"
127.0.0.1:6379> incrby key2 7
(integer) 8
127.0.0.1:6379> incrby key1 2
(error) ERR value is not an integer or out of range
- decr
decr key
针对某一个key的value-1.如果这个key的value不是64位的整型,则报错.如果这个key不存在的话,则创建这个key,其中value的值默认为0,之后对value-1.这个操作的返回值就是这个key的value-1之后的值.
shell
127.0.0.1:6379> incrby key2 7
(integer) 8
127.0.0.1:6379> decr key2
(integer) 7
127.0.0.1:6379> decr key3
(integer) -1
127.0.0.1:6379> get key3
"-1"
127.0.0.1:6379> decr key1
(error) ERR value is not an integer or out of range
- decrby
decrby key decreament
针对某个key的value-指定的值.如果这个key的value不是64位的整型,则报错.如果这个key不存在的话,则创建这个key,其中value的值默认为0,之后对value-指定值.这个操作的返回值就是这个key的value-指定值之后的值.
shell
127.0.0.1:6379> decrby key2 1
(integer) 6
127.0.0.1:6379> decrby key4 3
(integer) -3
127.0.0.1:6379> decrby key1 2
(error) ERR value is not an integer or out of range
- incrbyfloat
incrbyfloat key increament
将key对应的string表示的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值 。如果key不存在,则视为key对应的value是0。如果key对应的不是一个浮点数(或整数),则报错。允许采用科学计数法表示浮点数.
shell
127.0.0.1:6379> set key1 1
OK
127.0.0.1:6379> INCRBYFLOAT key1 0.5
"1.5"
127.0.0.1:6379> set key2 value2
OK
127.0.0.1:6379> INCRBYFLOAT key2 0.5
(error) ERR value is not a valid float
127.0.0.1:6379> INCRBYFLOAT key1 -0.7
"0.8"
127.0.0.1:6379> INCRBYFLOAT key -0.5
"-0.5"
[注意] 这里的小数,Redis在保存的时候,在底层实际保存的是embstr
,在Redis中并没有浮点型这样的数据类型.
shell
127.0.0.1:6379> object encoding key1
"embstr"
1.3 string的其他命令
- append
append key value
给指定key对应value拼接一个字符串,如果key已经存在,需要保证key对应的value是一个string类型的.如果这个key不存在,效果相当于set.
shell
127.0.0.1:6379> append key2 aaaaa
(integer) 11
127.0.0.1:6379> get key2
"value2aaaaa"
127.0.0.1:6379> append key3 bbbbb
(integer) 5
127.0.0.1:6379> get key3
"bbbbb"
127.0.0.1:6379> append key1 ccccc
(integer) 8
127.0.0.1:6379> get key1
"0.8ccccc"
127.0.0.1:6379> set key4 7
OK
127.0.0.1:6379> append key4 ccccc
(integer) 6
127.0.0.1:6379> get key4
"7ccccc"
[注意]
- 当key对应的value保存的是一个整数,或者是一个小数的时候,别看是一个数字,实际Redis在保存他们的时候,同样是把他们当做字符串来看待的.
- 这里append的返回值是增加后的字符串的字节数,而不是字符数,下面我们使用汉字来说明一下这个问题.
shell
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> set key1 你好
OK
127.0.0.1:6379> get key1
"\xe4\xbd\xa0\xe5\xa5\xbd"
这里我们看到Redis在保存汉字的时候,是已汉字的二进制方式来保存的,这也印证了我们上面所提到的,Redis保存string的时候,没有经过字符串编码,而是直接保存二进制数据."你好"两个汉字是6个字节.
shell
127.0.0.1:6379> append key1 你好
(integer) 12
我们在原来字符串的基础上又一次拼接了一个"你好".返回的字符串长度是12字节.
那么如何让Redis显示汉字而不是汉字的16进制编码呢?我们可以在启动Redis的时候,使用redis-cil --raw
指令来打开redis.
shell
root@iZ2zeh2c69p4ax5heci8wmZ:~# redis-cli --raw
127.0.0.1:6379> get key1
你好你好
- getrange
getrange key start end
在指定key的[start,end]区间(两边都是闭区间),截取一段字符串.有点像java中的substring()
方法.
其中redis的getrange
操作与python一样是支持负数下标的,其中-1表示倒数第一个字符串.-2表示倒数第二个字符串.
shell
127.0.0.1:6379> set key2 helloworld
OK
127.0.0.1:6379> getrange key2 0 -1
"helloworld"
127.0.0.1:6379> getrange key2 1 4
"ello"
[注意]
这里截取的时候,start和end依然指的是字节数.所以这里我们不建议使用getrange命令来截取汉字.因为汉字是汉字编码的16进制存储的,如果我们的redis是以--raw的方式打开的,截取出来的汉字很可能是一堆乱七八糟的东西.
shell
127.0.0.1:6379> get key2
你好
127.0.0.1:6379> GETRANGE key2 1 -2
½
- setrange
setrange key offset value
将指定key从第offset(这个叫做偏移量,MySQL中也有这个字段)字节开始的地方设置为指定字符串.返回设置之后字符串的长度.
shell
127.0.0.1:6379> set key3 value
OK
127.0.0.1:6379> setrange key3 2 aaaaaaaa
10
127.0.0.1:6379> get key3
vaaaaaaaaa
- strlen
strlen key
获取指定key对应value的字节长度.
shell
127.0.0.1:6379> get key3
vaaaaaaaaa
127.0.0.1:6379> get key2
你好
127.0.0.1:6379> strlen key2
6
127.0.0.1:6379> strlen key3
10
1.4 字符串指令小结
2. String的内部编码方式
这个话题我们在上一篇文章中提到过
- raw
这个是最基本的字符串,类似与Java中的byte[ ]数组. - int
redis有时会实现一些计数的功能,当value是整数的时候,redis就会直接用int类型来保存. - embstr
当字符串比较短的时候,redis就会把字符串优化为embstr.
有的资料上会写当字符串超过39字节的时候会转换为raw,在39字节以下时候,就是embstr,这里不建议大家去记忆这样具体的数字,因为这个是可以根据具体的业务场景来修改的.
3. string类型的具体应用场景
3.1 缓存功能
其中Redis作为缓冲层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用 。
具体流程:
在应用服务器访问数据的时候,首先访问的是缓存(redis)的数据,如果redis中存在对应的数据,则返回给应用服务器.如果redis中不存在,则去存储层查询(MySQL),如果存在,则返回给应用服务器,同时保存在缓存(redis)中.如果MySQL中也不存在,直接给应用服务器返回错误码.
上述的过程存在一个明显的问题,就是在这种情况下,redis的数据会越积越多.针对这种情况,redis会给其中的一些键值对设置过期时间,也在内存不足的情况下提供了内存淘汰的策略.
3.2 计数功能
许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源.
假如我们需要统计一个视频的播放次数,如果直接对MySQL中的数据进行操作的话,开销会非常大,这时候我们就引入了redis来帮我们计数.但是如果直接使用redis来统计数据的话,这也不是redis擅长的领域,统计的工作还是需要交给MySQL来做.所以在这个过程中redis和MySQL是相辅相成的.
在一个视频被播放的时候.redis就会给对应的视频id的播放次数+1,之后会异步同步到统计数据仓库中,这里的异步同步指的是,数据不是立即同步过去的,而是每隔一段时间对数据进行同步,这样会在某种程度上减小统计数据仓库的开销.
实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等
3.3 共享会话(session)
我们在实际开发的过程中,系统可能是分布式的,一个系统可能会有多台服务器.在用户访问一个应用的不同板块的时候,可能会访问到不同的服务器,这时候不是每一个服务器都保存着这个用户session.如果在用户访问其他服务器的时候,就需要用户重新登录.我们如果对session采用分散存储的方式:
如果我们在上述方案的基础上引入redis.redis就可以对用户的session数据进行集中统一管理 .这种情况下,无论用户被均衡到哪台Web服务器上,都集中从Redis中查询、更新Session信息.
3.4 手机验证码
用户在登录的时候,为了保证用户账号的安全,我们会使用验证码.当用户登录的时候,redis就会在服务器中保存一个与用户对应的验证码,这个验证码具有过期时间(比如在5分钟内有效) .在用户输入验证码之后,会从redis中查询对应的键值对,校验用户的验证码.
当然为了用户反复接收验证码,导致redis压力过大,一般规定在一分钟之内,最多接收一次验证码,如果手机没有验证码,可以尝试在一分钟之后重新获取验证码.