目录
前言:
Redis 中的 String 类型是最基础的数据类型,同时也是最常用的。
一些基本命令上篇基本有讲,本篇对上篇未提到的命令进行补充
Redis的基本使用命令(GET,SET,KEYS,EXISTS,DEL,EXPIRE,TTL,TYPE)_php redis get exists-CSDN博客
特点
- 二进制安全:Redis 的 String 可以存储任何数据,包括文字、数字、图片等二进制数据。
- 最大长度:String 类型的值最大可以存储 512MB 的数据。
- 多功能性:不仅能存储字符串,还能对数值型字符串执行一些操作。
在redis中String三种内部编码形式,但他们都是属于String这个大类型中
- 整数优先:当值为整数时,优先使用
int
。- 小字符串优先:当值为短字符串时,使用
embstr
。- 长字符串优先:当值为长字符串时,使用
raw
。查看内部编码命令
OBJECT ENCODING KEY
127.0.0.1:6379> set key2 111 OK 127.0.0.1:6379> object encoding key2 "int" 127.0.0.1:6379> set key3 hhhh OK 127.0.0.1:6379> object encoding key3 "embstr" 127.0.0.1:6379> set key4 hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh OK 127.0.0.1:6379> object encoding key4 "raw"
基本命令:
setnx/setxx
FLUSHALL
清空所有的数据库 删库切忌
类似 mysql中的 drop database
mest
一次性设置多个 key 的值。
MSET key value [key value ...]
127.0.0.1:6379> mset key1 111 key2 hhhhh key3 ggggggggggg
OK
127.0.0.1:6379> keys *
1) "key3"
2) "key2"
3) "key1"
mget
一次性获取多个 key 的value值。
127.0.0.1:6379> mget key1 key2 key3
1) "111"
2) "hhhhh"
3) "ggggggggggg"
mget vs 多次 get
主要开销大就在网络开销 网络环境下一来一回很费时间,所以最后还是尽量一次弄完
操作 时间
1000 次 get 1000 x 1 + 1000 x 0.1 = 1100 毫秒 1 次 mget 1000 个键 1 x 1 + 1000 x 0.1 = 101 毫秒 学会使用批量操作,可以有效提高业务处理效率,但是要注意,每次批量操作所发送的键的数量也不是无节制的,否则可能造成单⼀命令执行时间过长,导致 Redis 阻塞。
计数命令
INCR / INCRBY
将 key 对应的 string 表示的数字加⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
127.0.0.1:6379> set key1 110 OK 127.0.0.1:6379> incr key1 (integer) 111 127.0.0.1:6379> set key2 22222222222222222222222222222222222222222222222 OK 127.0.0.1:6379> incr key2 (error) ERR value is not an integer or out of range 127.0.0.1:6379> incr key3 (integer) 1 127.0.0.1:6379> set key4 hhhhhhhhhhh OK 127.0.0.1:6379> incr key4 (error) ERR value is not an integer or out of range 127.0.0.1:6379> keys * 1) "key4" 2) "key3" 3) "key2" 4) "key1"
可以看出:
1.如果是数字将会自增1
2.字符类型则直接报错
3.超过一定大小 也会报错
4.如果为设置key 自增key为默认从0+ 变成1
INCRBY
将 key 对应的 string 表⽰的数字加上对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。 语法:
INCRBY key decrement
127.0.0.1:6379> set key1 12 OK 127.0.0.1:6379> incrby key1 20 (integer) 32 127.0.0.1:6379> incrby key2 20 (integer) 20 127.0.0.1:6379> set key3 hh OK 127.0.0.1:6379> incrby key3 20 (error) ERR value is not an integer or out of range
DECR/DECYBY
DECR:
将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
127.0.0.1:6379> set key1 10 OK 127.0.0.1:6379> decr key1 (integer) 9 127.0.0.1:6379> decr key2 (integer) -1
DECYBY:
将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。语法:
DECRBY key decrement
127.0.0.1:6379> decrby key1 10 (integer) -10 127.0.0.1:6379> set key2 100 OK 127.0.0.1:6379> decrby key2 50 (integer) 50
INCRBYFLOAT
将 key 对应的 string 表示的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的不是 string,或者不是⼀个浮点数,则报错。允许采用科学计数法表示浮点数。
127.0.0.1:6379> set key1 1.5 OK 127.0.0.1:6379> incr key1 (error) ERR value is not an integer or out of range 127.0.0.1:6379> decr key1 (error) ERR value is not an integer or out of range 127.0.0.1:6379> incrbyfloat key1 10 "11.5" 127.0.0.1:6379> incrbyfloat key1 -5 "6.5" 127.0.0.1:6379> incrbyfloat key2 -1.0 "-1" 127.0.0.1:6379> incrbyfloat key3 5 "5" 127.0.0.1:6379> incr key3 (integer) 6
1.float类型redis并没有单独存放 使用incr or decr 都会出错 只能使用incrbyfloat
2.也是可以对未定义的key进行增减
其他命令
APPEND
如果 key 已经存在并且是⼀个 string,命令会将 value 追加到原有 string 的后边。如果 key 不存在,则效果等同于 SET 命令。
类似java中的StringBuffer/StringBuilder 中的append命令效果类似
语法:
APPEND KEY VALUE
127.0.0.1:6379> set key1 hello OK 127.0.0.1:6379> append key1 redis (integer) 10 127.0.0.1:6379> get key1 "helloredis" 127.0.0.1:6379> append key2 hi! (integer) 3 127.0.0.1:6379> get key2 "hi!" 127.0.0.1:6379> append key2 世界 (integer) 9 127.0.0.1:6379> get key2 "hi!\xe4\xb8\x96\xe7\x95\x8c"
1.返回的结果为字节个数
2.在未设置key的情况下 append追加可以有set的功能
2.如果是中文字符 在redis一个中文字符占3个字节 所以可以看见3+6为9个字节在key2中
GETRANGE
返回 key 对应的 string 的子串,由 start 和 end 确定(左闭右闭)。可以使用负数表示倒数。-1 代表倒数第⼀个字符,-2 代表倒数第二个,其他的与此类似。超过范围的偏移量会根据 string 的长度调整成正确的值。 遵循(左闭右闭)的原则
127.0.0.1:6379> set key1 helloworld OK 127.0.0.1:6379> getrange key1 0 4 "hello" 127.0.0.1:6379> getrange key1 0 -1 "helloworld" 127.0.0.1:6379> getrange key1 -1 0 "" 127.0.0.1:6379> getrange key1 1 -2 "elloworl" 127.0.0.1:6379> getrange key2 0 1 ""
SETRANGE
覆盖字符串的一部分,从指定的偏移开始。语法:
SETRANGE key
127.0.0.1:6379> set key1 "hello world" OK 127.0.0.1:6379> setrange key1 6 redis (integer) 11 127.0.0.1:6379> get key1 "hello redis" 127.0.0.1:6379> setrange key2 2 hh (integer) 4 127.0.0.1:6379> get key2 "\x00\x00hh" 127.0.0.1:6379> set key3 helloworld OK 127.0.0.1:6379> setrange key3 4 reids (integer) 10 127.0.0.1:6379> get key3 "hellreidsd"
1.未设置的key 使用setrange可以直接设置 并且偏移量前的字符都为0
STRLEN
获取 key 对应的 string 的长度。当 key 存放的类似不是 string 时,报错。语法:
STRLEN key
127.0.0.1:6379> set key1 hello OK 127.0.0.1:6379> strlen key1 (integer) 5 127.0.0.1:6379> strlen key2 (integer) 0 127.0.0.1:6379> lpush key3 11 22 33 44 (integer) 4 127.0.0.1:6379> strlen key3 (error) WRONGTYPE Operation against a key holding the wrong kind of value
字符串类型命令小结:
命令 | 执⾏效果 | 时间复杂度 |
set key value [key value...] | 设置 key 的值是 value | O(k), k 是键个数 |
get key | 获取 key 的值 | O(1) |
del key [key ...] | 删除指定的 key | O(k), k 是键个数 |
mset key value [key value ...] | 批量设置指定的 key 和 value | O(k), k 是键个数 |
mget key [key ...] | 批量获取 key 的值 | O(k), 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),n 是字符串⻓度, 通常视为 O(1) |
getrange key start end | 获取指定 key 的从 start 到 end 的部分值 | O(n),n 是字符串⻓度, 通常视为 O(1) |
String的典型使用场景
缓存(Cache)功能
其中 Redis 作为缓冲层,MySQL 作为存储层,绝⼤部分请求的数据都是从 Redis 中获取。由于 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。
Redis + MySQL 组成的缓存存储架构
下面一个详细案例抽奖系统:查询活动详细信息的内容
根据活动Id去redis查找
如果没有 只能去Mysql对应的表中查找
查找到相应的表之后,再返回结果 并且缓存到redis中 方便下一次查找
@Override
public ActivityDetailDTO getActivityDetail(Long activityId) {
if(null == activityId) {
logger.warn("获取活动详细信息失败!");
return null;
}
//查询redis
ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
if(null != activityDetailDTO) {
return activityDetailDTO;
}
//如果redis不存在 查表
//活动表
ActivityDO aDO = activityMapper.selectById(activityId);
//活动人员表
List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
//活动奖品表
List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);
//奖品表
List<Long> prizeIds = apDO.stream()
.map(ActivityPrizeDO::getPrizeId)
.collect(Collectors.toList());
List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
//重新整合到redis中
activityDetailDTO = convertToActivityDetailDTO(aDO,apDO,pDO,auDO);
cacheActivityDetailDTO(activityDetailDTO);
//返回
return activityDetailDTO;
}
计数(Counter)功能
许多应用都会使用 Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。如图所示,例如视频网站的视频播放次数可以使⽤ Redis 来完成:用户每播放⼀次视频,相应的视频播放数就会自增 1。
共享会话(Session)
假设一个病人(cookie)(客户端)去找医生(小绿)(session)(服务器)看病 ,看完之后,过一个礼拜回来复查
一个礼拜后,病人去找医生复查,可以恰巧不幸的是此时医生(小绿)不是上一个看病的医生,而是另外一个医生(小红),此时俩个不同的医生,并不知道病人的状态如何
所以医院的正确是:使用一套系统,记录病人的病例,即使在不同的医生的情况下,也可以找到当时的病人的情况
使用 Redis 将用户的 Session 信息进⾏集中管理,如图所示,在这种模式下,只要保证 Redis 是高可用和可扩展性的,无论用户被均衡到哪台 Web 服务器上,都集中从 Redis 中查询、更新 Session 信息。
手机验证码功能
很多应用出于安全考虑,会在每次进行登录时,让用户输人手机号并且配合给手机发送验证码,然后让用户再次输⼊收到的验证码并进⾏验证,从而确定是否是用户本人。为了短信接口不会频繁访问,会限制用户每分钟获取验证码的频率,例如⼀分钟不能超过 5 次
并且可以设置手机验证码有效时间 过期了将不能使用
下面是设置缓存过期时间的代码:
public void sendVerificationCode(String phoneNumber) {
//校验手机号
if(!RegexUtil.checkMobile(phoneNumber)) {
throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
}
//生成随机验证码
String code = CaptchaUtil.getCaptcha(4);
//发送验证码
Map<String,String> map = new HashMap<>();
map.put("code",code);
smsUtil.sendMessage(VERIFICATION_CODE_TEMPLATE_CODE,
phoneNumber,
JacksonUtils.writeValueString(map));
//缓存验证码
redisUtil.set(VERIFICATION_CODE_PREFIX + phoneNumber,code,VERIFICATION_CODE_TIMEOUT);
}
以上介绍了使用 Redis 的字符串数据类型可以使用户的几个场景,但其适用场景远不止于此,开发人员可以结合字符串类型的特点以及提供的命令,充分发挥自己的想象力,在自己的业务中去找到合适的场景去使用 Redis 的字符串类型。
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!