详细介绍了Redis的key和String数据类型的底层原理,以及最基本的使用方式。
Redis 不是一个普通的key-value存储服务器,它实际上是一个数据结构服务器,支持不同类型的值,在Redis中值不仅限于简单的字符串,还可以包含更复杂的数据结构。
以下是Redis支持的所有数据结构:
Redis key是String类型,也是二进制安全的,我们可以使用任何二进制序列作为键,从像“foo”这样的字符串到 JPEG 文件的内容。
关于Redis的key有以下规则和建议:
object-type:id:field
”就是个不错的注意,像这样:“user:1000:password
”。对多单词的字段名中可以加上一个点,就像这:“comment:1234:reply.to
”样:。keys
命令用于返回服务指定模式的key列表,该命令是阻塞式的,阻塞期间导致Redis服务不可用,可以使用scan
命令,该命令无阻塞的提取出指定模式的 key 列表,但有一定重复的概率,可以在客户端执行去重。
在Redis的学习中,将会看到非常多的binary safe
的描述,比如key、String等等,那么什么是二进制安全呢?可以简单的这么理解:只关心二进制化的字符串,不关心具体格式,只会严格的按照二进制的数据存取,不会妄图已某种特殊格式解析数据,即二进制数据在写入时是怎么样的,读取时就是怎么样。
C语言的字符串默认是以'\0'
结尾的,也就是说你保存的字符串内存在'\0'
,c语言自会识别前面的数据,后面的就会被忽略掉,所以说是不安全的。比如str=“redis cluster”
这个字符串,在C中直接读取的结果就是“redis”
,而strlen(str)
计算字符串长度的结果也是4,这样就跟原始数据不是相等的,因此就是二进制不安全的。
Redis虽然是采用C语言来开发的,但是Redis内部保存的字符串数据结构是自己实现的,并不是沿用C语言的字符串数据结构。
redis采用SDS(simple dynamic string)来实现简单动态字符串(sds.h/sds.c),同时保证了redis保存的数据是二进制安全的,结构如下:
struct sdshdr {
int len;
int free;
char buf[];
};
实际上的字符串底层是一个char数组
(和Java的ArrayList类似),与此同时还保存了len和free
两个属性。
len表示该字符串的实际内容所占长度,free表示分配给该字符串的全部空间-字符串实际内容长度,也就是free表示该字符串的空闲空间长度,所以你对该字符串取值时是通过len属性判断实际内容的长度,然后取的值。
拼接字符串时是追加到free空间内中的。所以redis对字符串的求长度和更新内容等操作比C语言要快很多,因为求长度只需要返回该字符串的len属性值,C语言想要遍历整个字符串才会知道长度。拼接字符串“一般”也不需要在重新分配空间,拼接的字符串直接放在free内存中就可以了。
因为有了对字符串长度定义len,所以在处理字符串时候不会以零值字节(\0)为字符串结尾标志,能够获取到完整的输入的数据,即保证二进制。
String是最基本的 Redis 值类型,也是Memcached中的唯一值类型。String是最基本的 Redis 值类型,也是Memcached中的唯一值类型。由于 Redis key是字符串,所以当我们也使用字符串类型作为值时,我们将一个字符串映射到另一个字符串。String值的最大长度为 512 MB。
Redis的String是二进制安全的,这意味着 Redis String可以包含任何类型的数据,例如 JPEG 图像或序列化的 Ruby 对象,为了提高网站运行速度,可以使用String类型缓存一些静态文件,如图片文件、CSS文件等。
我们可以使用 redis-cli
命令来对 Redis 进行命令行的操作。最基本的操作就是SET和GET
命令:
127.0.0.1:6379> set xx yyy
OK
127.0.0.1:6379> get xx
"yyy"
使用SET
命令时,如果此前已存在相同key的缓存,则会执行value的替换:
127.0.0.1:6379> set xx yyy
OK
127.0.0.1:6379> get xx
"yyy"
可以使用SETNX
来避免上面这种情况,SETNX 是“SET if Not eXists(如果不存在,则 SET)”的简写。只在key 不存在的情况下,将键 key 的值设置为 value 。若key 已经存在,则 SETNX 命令不做任何动作,也不会覆盖原值。
127.0.0.1:6379> GET a
(nil)
127.0.0.1:6379> SETNX a aa
(integer) 1
127.0.0.1:6379> SETNX a bb
(integer) 0
127.0.0.1:6379> GET a
"aa"
另一个有趣的命令就是操作是GETSET
命令,它为指定key设置新值并且返回原值。这有什么用处呢?例如:你的系统每当有新用户访问时就用INCR命令操作一个Redis key。如果你希望每小时对这个信息收集一次。你就可以在每个小时的开始GETSET这个key并给其赋值0并读取原值。
127.0.0.1:6379> get num
"100"
127.0.0.1:6379> GETSET num 0
"100"
127.0.0.1:6379> get num
"0"
即使Redis 的基本值是字符串类型,也可以对其进行一些特殊操作,比如进行原子递增(前提是整数类型的字符串)。
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num
(integer) 101
127.0.0.1:6379> incr num
(integer) 102
127.0.0.1:6379> get num
"102"
INCR
命令将字符串值解析为10进制的64位有符号整型数据,将其加一,最后将获得的值设置为新值。还有其他类似的命令,如 INCRBY——增加指定的值、DECR——自减1 和 DECRBY——减去指定的值。在内部,它实际上是相同的命令,以稍微不同的方式执行。
127.0.0.1:6379> INCRBY num 20
(integer) 122
127.0.0.1:6379> decr num
(integer) 121
127.0.0.1:6379> decr num
(integer) 120
127.0.0.1:6379> DECRBY num 20
(integer) 100
如果key 不是整数数值类型的字符串,使用增减操作将返回一个异常:
127.0.0.1:6379> set flo 11.1
OK
127.0.0.1:6379> INCR flo
(error) ERR value is not an integer or out of range
如果指定的key不存在,那么在执行增减操作之前,会先将它的值设定为0:
127.0.0.1:6379> DECR numm
(integer) -1
即使多个客户端对同一个key发出增减操作的命令,也决不会导致竞争的情况。例如如下情况永远不可能发生:“客户端1和客户端2同时读出10,他们俩都对其加到11,然后将新值设置为11”。最终的值一定是12。
由于INCR、INCRBY、DECR、DECRBY
等数值操作都是原子性的,因此可以用来做很多有用的事情,最常用的使用场景是计数器。
使用思路是:每次有相关操作的时候,就向Redis服务器发送一个incr命令。例如这样一个场景:我们有一个web应用,我们想记录每个用户每天访问这个网站的次数。web应用只需要通过拼接用户id和代表当天时间的字符串作为key,该每次用户访问这个页面的时候对这个key执行一下incr命令。
这个场景可以有很多种扩展方法:
另外对于电商领域,也有一种应用就是秒杀业务。
Redis支持在单个命令中设置或检索多个key的值的能力,这对于对于减少延迟也很有用。
Redis提供了 MSET 和 MGET
命令:
127.0.0.1:6379> MSET a aa b bb c cc d dd
OK
127.0.0.1:6379> MGET a b d
1) "aa"
2) "bb"
3) "dd"
当使用 MGET
时,Redis 返回一个值数组。
有些命令不是针对特定的值类型,可以与任何类型的key一起使用。
例如,使用EXISTS
命令判断key对应的值是否存在,将会返回1或0。也可以传入多个key,将会返回存在的key的总和:
127.0.0.1:6379> keys *
1) "b"
2) "c"
3) "d"
4) "a"
5) "num"
127.0.0.1:6379> EXISTS a
(integer) 1
127.0.0.1:6379> EXISTS a b
(integer) 2
127.0.0.1:6379> EXISTS a b e
(integer) 2
127.0.0.1:6379> EXISTS e
(integer) 0
使用DEL
命令可以删除key对应的值,将会返回1或0,标识值是被成功删除(值存在)或者没被删除(key对应的值本就不存在)。也可以传入多个key,将会返回成功删除的key的总和:
127.0.0.1:6379> DEL a c
(integer) 2
127.0.0.1:6379> DEL a c
(integer) 0
127.0.0.1:6379> DEL b
(integer) 1
127.0.0.1:6379> DEL a
(integer) 0
TYPE
命令可以返回key对应的值的存储类型:
127.0.0.1:6379> KEYS *
1) "d"
2) "num"
127.0.0.1:6379> TYPE num
string
127.0.0.1:6379> TYPE d
string
127.0.0.1:6379> DEL num d
(integer) 2
127.0.0.1:6379> TYPE num
none
127.0.0.1:6379> TYPE d
none
可以对任何值类型的key设置超时时间,当这个时间到达后key-value会被删除。精度可以使用毫秒或秒,但过期时间的分辨率始终为 1 毫秒。
通常使用EXPIRE
来设置超时时间,默认单位是秒,也可以再次调用这个命令来改变超时时间:
127.0.0.1:6379> set a aa
OK
127.0.0.1:6379> set b bb
OK
127.0.0.1:6379> EXPIRE a 8
(integer) 1
127.0.0.1:6379> GET a
(nil)
127.0.0.1:6379> GET b
"bb"
有关过期的信息被复制并保存在磁盘上,实际上Redis会保存key的过期时间点,当Redis 服务器停止时,时间实际上已经过去了,重启时该过期key将被删除。
使在key过期之前用PERSIST
命令可以去除key的超时时间:
127.0.0.1:6379> SET a aa
OK
127.0.0.1:6379> EXPIRE a 8
(integer) 1
127.0.0.1:6379> PERSIST a
(integer) 1
127.0.0.1:6379> GET a
"aa"
我们也可以在设置key-value的时候设置超时时间,使用TTL
查看key的剩余时间:
127.0.0.1:6379> SET a aa ex 8
OK
127.0.0.1:6379> TTL a
(integer) 4
127.0.0.1:6379> TTL a
(integer) 2
127.0.0.1:6379> GET a
(nil)
以下是超时相关命令的汇总:
EXPIRE
将key的生存时间设置为秒。PEXPIRE
:将key的生存时间设置为毫秒。EXPIREAT
:将key的过期时间设置为timestamp所代表的的秒数的时间戳。PEXPIREAT
:将key的过期时间设置为timestamp所代表的的毫秒数的时间戳。PTTL
:以毫秒为单位检查key的剩余时间。Redis的LOLWUT [VERSION version]
指令将会返回包含生成的计算机艺术图像的字符串,以及带有 Redis 版本的文本。本文演示版本为Redis 6.2。
LOLWUT VERSION 5
:
官方文档:https://www.redis.com.cn/commands/lolwut.html
相关文章:
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!