
🌈 个人主页:Zfox_
🔥 系列专栏:Redis

🔥 String字符串
🧑💻 字符串类型是Redis
最基础的数据类型,关于字符串需要特别注意:
- ⾸先
Redis
中所有的键的类型都是字符串类型,⽽且其他⼏种数据结构也都是在字符串类似基础上构建的,例如列表和集合的元素类型是字符串类型,所以字符串类型能为其他4种数据结构的学习奠定基础。 - 其次,如图2-7所⽰,字符串类型的值实际可以是字符串,包含⼀般格式的字符串或者类似JSON、XML格式的字符串;数字,可以是整型或者浮点型;甚⾄是⼆进制流数据,例如图⽚、⾳频、视频等。不过⼀个字符串的最⼤值不能超过512MB。
由于Redis内部存储字符串完全是按照⼆进制流的形式保存的
,所以Redis是不处理字符集编码问题的,客户端传⼊的命令中使⽤的是什么字符集编码,就存储什么字符集编码。
🔥 常⻅命令
🦋 SET
将string类型的value设置到key中。如果key之前存在,则覆盖,⽆论原来的数据类型是什么。之前关于此key的TTL也全部失效。
语法:
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
选项:
SET命令⽀持多种选项来影响它的⾏为:
EX
seconds⸺使⽤秒作为单位设置key的过期时间。PX
milliseconds⸺使⽤毫秒作为单位设置key的过期时间。NX
⸺只在key不存在时才进⾏设置,即如果key之前已经存在,设置不执⾏。XX
⸺只在key存在时才进⾏设置,即如果key之前不存在,设置不执⾏。
注意:由于带选项的SET命令可以被 SETNX 、 SETEX 、 PSETEX 等命令代替,所以之后的版本中,Redis可能进⾏合并。
返回值:
- 如果设置成功,返回OK。
- 如果由于SET指定了NX或者XX但条件不满⾜,SET不会执⾏,并返回(nil)。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> SET mykey "Hello"
OK
redis> GET mykey
"Hello"
redis> SET mykey "World" NX
(nil)
redis> DEL mykey
(integer) 1
redis> EXISTS mykey
(integer) 0
redis> SET mykey "World" XX
(nil)
redis> GET mykey
(nil)
redis> SET mykey "World" NX
OK
redis> GET mykey
"World"
redis> SET mykey "Will expire in 10s" EX 10
OK
redis> GET mykey
"Will expire in 10s"
redis> GET mykey # 10秒之后
(nil)
🦋 GET
获取key对应的value。如果key不存在,返回nil。如果value的数据类型不是string,会报错。
语法:
GET key
返回值:key对应的value,或者nil当key不存在。
⽰例:
redis> GET nonexisting
(nil)
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis> DEL mykey
(integer) 1
redis> EXISTS mykey
(integer) 0
redis> HSET mykey name Bob
(integer) 1
redis> GET mykey
(error) WRONGTYPE Operation against a key holding the wrong kind of value
🦋 MGET
⼀次性获取多个key的值。如果对应的key不存在或者对应的数据类型不是string,返回nil。
语法:
MGET key [key ...]
返回值:对应value的列表
⽰例:
redis> SET key1 "Hello"
"OK"
redis> SET key2 "World"
"OK"
redis> MGET key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
🦋 MSET
⼀次性设置多个key的值。
语法:
MSET key value [key value ...]
⽰例:
redis> MSET key1 "Hello" key2 "World"
"OK"
redis> GET key1
"Hello"
redis> GET key2
"World"
图2-8多次getvs单次mget
如图2-8所⽰,使⽤mget/mset由于可以有效地减少了⽹络时间,所以性能相较更⾼。
学会使⽤批量操作,可以有效提⾼业务处理效率,但是要注意,每次批量操作所发送的键的数量也不是⽆节制的,否则可能造成单⼀命令执⾏时间过⻓,导致Redis阻塞。
🦋 SETNX
设置key-value但只允许在key之前不存在的情况下。
语法:
SETNX key value
返回值:1表⽰设置成功。0表⽰没有设置。
⽰例:
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
SET、SETNX和SETXX的执⾏流程如图2-9所⽰
图2-9SET、SETNX、SETXX执⾏流程
🔥 计数命令
🦋 INCR
将key对应的string表⽰的数字加⼀。如果key不存在,则视为key对应的value是0。如果key对应的string不是⼀个整型或者范围超过了64位有符号整型,则报错。
语法:
INCR key
返回值:integer类型的加完后的数值。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> INCR mykey
(integer) 1
redis> SET mykey "10"
"OK"
redis> INCR mykey
(integer) 11
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> INCR mykey
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> INCR mykey
(error) value is not an integer or out of range
🦋 INCRBY
将key对应的string表⽰的数字加上对应的值。如果key不存在,则视为key对应的value是0。如果key对应的string不是⼀个整型或者范围超过了64位有符号整型,则报错。
语法:
INCRBY key decrement
返回值:integer类型的加完后的数值。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> INCRBY mykey 3
1 2 3
比特就业课(integer) 3
redis> SET mykey "10"
"OK"
redis> INCRBY mykey 3
(integer) 13
redis> INCRBY mykey "not a number"
(error) ERR value is not an integer or out of range
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> INCRBY mykey 3
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> INCRBY mykey 3
(error) value is not an integer or out of range
🦋 DECR
将key对应的string表⽰的数字减⼀。如果key不存在,则视为key对应的value是0。如果key对应的string不是⼀个整型或者范围超过了64位有符号整型,则报错。
语法:
DECR key
返回值:integer类型的减完后的数值。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> DECR mykey
(integer) -1
redis> SET mykey "10"
"OK"
redis> DECR mykey
(integer) 9
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
🦋 DECYBY
将key对应的string表⽰的数字减去对应的值。如果key不存在,则视为key对应的value是0。如果key对应的string不是⼀个整型或者范围超过了64位有符号整型,则报错。
语法:
DECRBY key decrement
返回值:integer类型的减完后的数值。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> DECRBY mykey 3
(integer) -3
redis> SET mykey "10"
"OK"
redis> DECRBY mykey 3
(integer) 7
redis> DECRBY mykey "not a number"
(error) ERR value is not an integer or out of range
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
redis> SET mykey 'not a number'
"OK"
redis> DECRBY mykey 3
(error) value is not an integer or out of range
🦋 INCRBYFLOAT
将key对应的string表⽰的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果key不存在,则视为key对应的value是0。如果key对应的不是string,或者不是⼀个浮点数,则报错。允许采⽤科学计数法表⽰浮点数。
语法:
INCRBYFLOAT key increment
返回值:加/减完后的数值。
⽰例:
redis> SET mykey 10.50
"OK"
redis> INCRBYFLOAT mykey 0.1
"10.6"
redis> INCRBYFLOAT mykey -5
"5.6"
redis> SET mykey 5.0e3
"OK"
redis> INCRBYFLOAT mykey 2.0e2
"5200"
很多存储系统和编程语⾔内部使⽤CAS机制实现计数功能,会有⼀定的CPU开销,但在Redis中完全不存在这个问题,因为Redis是单线程架构,任何命令到了Redis服务端都要顺序执⾏。
🔥 其他命令
🦋 APPEND
如果key已经存在并且是⼀个string,命令会将value追加到原有string的后边。如果key不存在,则效果等同于SET命令。
语法:
APPEND KEY VALUE
返回值:追加完成之后string的⻓度。
⽰例:
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> GET mykey
"Hello"
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
在启动 redis 客户端的时候,加上一个 --raw 这样的选项就可以使 redis 客户端能够自动的把二进制数据尝试翻译
🦋 GETRANGE
返回key对应的string的⼦串,由start
和end
确定(左闭右闭)。可以使⽤负数表⽰倒数。-1代表倒数第⼀个字符,-2代表倒数第⼆个,其他的与此类似。超过范围的偏移量会根据string的⻓度调整成正确的值。
语法:
GETRANGE key start end
返回值:string类型的⼦串
⽰例:
redis> SET mykey "This is a string"
"OK"
redis> GETRANGE mykey 0 3
"This"
redis> GETRANGE mykey -3 -1
"ing"
redis> GETRANGE mykey 0 -1
"This is a string"
redis> GETRANGE mykey 10 100
"string"
🦋 SETRANGE
覆盖字符串的⼀部分,从指定的偏移开始。
语法:
SETRANGE key offset value
返回值:替换后的string的⻓度。
⽰例:
redis> SET key1 "Hello World"
"OK"
redis> SETRANGE key1 6 "Redis"
(integer) 11
redis> GET key1
"Hello Redis"
🦋 STRLEN
获取key对应的string的⻓度。当key存放的类似不是string时,报错。
语法:
STRLEN key
返回值:string的⻓度。或者当key不存在时,返回0。
⽰例:
redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
🔥 命令⼩结
表2-3是字符串类型命令的效果、时间复杂度,开发⼈员可以参考此表,结合⾃⾝业务需求和数
据⼤⼩选择合适的命令。
表2-3字符串类型命令⼩结
🔥 内部编码
字符串类型的内部编码有3种:
- int:8个字节的⻓整型。
- embstr:⼩于等于39个字节的字符串。
- raw:⼤于39个字节的字符串。
Redis会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。
整型类型⽰例如下:
127.0.0.1:6379> set key 6379
OK
127.0.0.1:6379> object encoding key
"int"
短字符串⽰例如下:
# ⼩于等于 39 个字节的字符串
127.0.0.1:6379> set key "hello"
OK
127.0.0.1:6379> object encoding key
"embstr"
⻓字符串⽰例如下:
# ⼤于 39 个字节的字符串
127.0.0.1:6379> set key "one string greater than 39 bytes ........"
OK
127.0.0.1:6379> object encoding key
"raw"
🦋典型使⽤场景
🎀 缓存(Cache)功能
图2-10是⽐较典型的缓存使⽤场景,其中Redis作为缓冲层,MySQL作为存储层,绝⼤部分请求的数据都是从Redis中获取
。由于Redis具有⽀撑⾼并发的特性,所以缓存通常能起到 加速读写
和 降低后端压⼒
的作⽤。
图2-10Redis+MySQL组成的缓存存储架构
上述策略,存在一个明显的问题! 随着时间的推移,肯定是会有越来越多的 key 在 redis 上访问不到从而从 mysql 读取并写入 redis 了.此时 redis 中的数据不是就越来越多嘛?
- 在把数据写给 redis 的同时,给这个 key 设置一个过期时间
- Redis 也在内存不足的时候,提供了淘汰策略
下⾯的伪代码模拟了图2-10的业务数据访问过程:
- 假设业务是根据⽤⼾uid获取用户信息
UserInfo getUserInfo(long uid) {...
}
- ⾸先从Redis获取⽤⼾信息,我们假设⽤⼾信息保存在"user:info:"对应的键中:
// 根据 uid 得到 Redis 的键
String key = "user:info:" + uid;// 尝试从 Redis 中获取对应的值
String value = Redis 执⾏命令:get key;// 如果缓存命中(hit)
if (value != null) {// 假设我们的⽤⼾信息按照 JSON 格式存储UserInfo userInfo = JSON 反序列化(value);return userInfo;
}
- 如果没有从Redis中得到⽤⼾信息,及缓存miss,则进⼀步从MySQL中获取对应的信息,随后写⼊缓存并返回:
// 如果缓存未命中(miss)
if (value == null) {// 从数据库中,根据 uid 获取⽤⼾信息UserInfo userInfo = MySQL 执⾏ SQL:select * from user_info where uid =
<uid>// 如果表中没有 uid 对应的⽤⼾信息if (userInfo == null) {响应 404return null;}// 将⽤⼾信息序列化成 JSON 格式String value = JSON 序列化(userInfo);// 写⼊缓存,为了防⽌数据腐烂(rot),设置过期时间为 1 ⼩时(3600 秒)Redis 执⾏命令:set key value ex 3600// 返回⽤⼾信息return userInfo;
}
通过增加缓存功能,在理想情况下,每个⽤⼾信息,⼀个⼩时期间只会有⼀次MySQL查询,极⼤地提升了查询效率,也降低了MySQL的访问数。
与MySQL等关系型数据库不同的是,Redis没有表、字段这种命名空间,⽽且也没有对键名有强制要求(除了不能使⽤⼀些特殊字符)。但设计合理的键名,有利于防⽌键冲突和项⽬的可维护性,⽐较推荐的⽅式是使⽤"业务名:对象名:唯⼀标识:属性"作为键名。例如MySQL的数据库名为vs,⽤⼾表名为user_info,那么对应的键可以使⽤"vs:user_info:6379"、“vs:user_info:6379:name"来表⽰,如果当前Redis只会被⼀个业务使⽤,可以省略业务名"vs:”。如果键名过程,则可以使⽤团队内部都认同的缩写替代,例如 "user:6379:friends:messages:5217"可以被"u:6379 : f r : m:5217"代替。毕竟键名过⻓,还是会导致Redis的性能明显下降的。
🎀 计数(Counter)功能
许多应⽤都会使⽤Redis作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。如图2-11所⽰,例如视频⽹站的视频播放次数可以使⽤Redis来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增1。
图2-11记录视频播放次数
// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid) {key = "video:" + vid;long count = Redis 执⾏命令:incr keyreturn counter;
}
实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等
🎀 共享会话(Session)
如图2-12所⽰,⼀个分布式Web服务将⽤户的Session信息(例如⽤⼾登录信息)保存在各⾃的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤户的访问请求均衡到不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。
图2-12Session分散存储
为了解决这个问题,可以使⽤Redis将⽤⼾的Session信息进⾏集中管理,如图2-13所⽰,在这种模式下,只要保证Redis是⾼可⽤和可扩展性的,⽆论⽤⼾被均衡到哪台Web服务器上,都集中从Redis中查询、更新Session信息。
图2-13Redis集中管理Session
🎀 ⼿机验证码
很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码,然后让⽤户再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤户本⼈。为了短信接⼝不会频繁访问,会限制⽤户每分钟获取验证码的频率,例如⼀分钟不能超过5次,如图2-14所⽰。
图2-14短信验证码
此功能可以⽤以下伪代码说明基本实现思路:
String 发送验证码(phoneNumber) {key = "shortMsg:limit:" + phoneNumber;// 设置过期时间为 1 分钟(60 秒)// 使⽤ NX,只在不存在 key 时才能设置成功bool r = Redis 执⾏命令:set key 1 ex 60 nxif (r == false) {// 说明之前设置过该⼿机的验证码了long c = Redis 执⾏命令:incr keyif (c > 5) {// 说明超过了⼀分钟 5 次的限制了// 限制发送return null;}}// 说明要么之前没有设置过⼿机的验证码;要么次数没有超过 5 次String validationCode = ⽣成随机的 6 位数的验证码();validationKey = "validation:" + phoneNumber;// 验证码 5 分钟(300 秒)内有效Redis 执⾏命令:set validationKey validationCode ex 300;// 返回验证码,随后通过⼿机短信发送给⽤⼾return validationCode ;}// 验证⽤⼾输⼊的验证码是否正确bool 验证验证码(phoneNumber, validationCode) {validationKey = "validation:" + phoneNumber;String value = Redis 执⾏命令:get validationKey;if (value == null) {// 说明没有这个⼿机的验证码记录,验证失败return false;}if (value == validationCode) {return true;} else {return false;}
}
以上介绍了使⽤Redis的字符串数据类型可以使⽤的⼏个场景,但其适⽤场景远不⽌于此,开发⼈员可以结合字符串类型的特点以及提供的命令,充分发挥⾃⼰的想象⼒,在⾃⼰的业务中去找到合适的场景去使⽤Redis的字符串类型。
🔥 共勉
😋 以上就是我对 Redis:常用数据结构 & 单线程模型
的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉