[Redis] Redis中的String类型

🌸个人主页: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不存在,则不设置.
  1. 夹带选项的set指令可以被后续的Redis命令取代,比如setex,setnx,setpx等.
  2. 如果使用xx选项,可能会改变原来value的数据类型,原来key的过期时间也会被覆盖掉
  3. 原地离职小技巧: 如果想要删除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 valueset 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"

[注意]

  1. 当key对应的value保存的是一个整数,或者是一个小数的时候,别看是一个数字,实际Redis在保存他们的时候,同样是把他们当做字符串来看待的.
  2. 这里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压力过大,一般规定在一分钟之内,最多接收一次验证码,如果手机没有验证码,可以尝试在一分钟之后重新获取验证码.

相关推荐
huaqianzkh1 小时前
了解MySQL 高可用架构:主从备份
数据库·mysql·架构
向往风的男子2 小时前
【mysql】mysql之读写分离以及分库分表
数据库·mysql
阳光开朗_大男孩儿2 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
挠背小能手2 小时前
达梦数据库SCHEMA使用初探
数据库·oracle
楠神说软件测试3 小时前
接口自动化框架入门(requests+pytest)
运维·数据库·自动化
惟长堤一痕3 小时前
医学数据分析实训 项目一 医学数据采集
数据库
xuan哈哈哈3 小时前
web基础—dvwa靶场(八)SQL Injection(Blind)
数据库·web安全·网络安全
Lill_bin3 小时前
Lua编程语言简介与应用
开发语言·数据库·缓存·设计模式·性能优化·lua
终末圆3 小时前
MyBatis动态SQL中的`if`标签使用【后端 19】
java·数据结构·数据库·sql·算法·spring·mybatis
拾伍廿肆3 小时前
Django ORM(多表)
数据库·django