admin管理员组

文章数量:1030705

初识Redis · 数据类型和初识string

前言:

前文我们已经介绍了基本的命令,涉及到的命令有set,get, expire, ttl, type等命令,并且从中引入了过期策略的实现,涉及到了惰性清除和定期清楚,并且我们还介绍了为什么不引入定时器,因为大概率会有另一个线程的参与,而对于Redis来说,它是一个单线程模型,这就违背了。

那么今天我们介绍Redis中的数据类型,虽然说对于Redis存的键值对中的Key都是雷打不动的string,但是对于value来说的话类型就多了,如下图:

但是光有图还不够,因为对于Redis来说,数据类型有那么多,但是和平常学习的C++ MySQL不一样的是它会根据数据类型的长度等因素确定这个数据应该是哪种编码方式,所以我们首先应该先再认识认识基本的数据类型。


编码方式

首先我们不妨思考一个问题:为什么不同的数据它要用不同的编码方式来表示?

你想,对于Redis来说它缓存的是热点数据,那么既然是热点数据也就代表了它能存放的数据容量并不是那么多,所以对于Redis来说,它要保持快的这个点,就一定会有一个操作是自适应,也就是根据不同的应用场景使得自己能够存放更多的数据。

不过啊,对于程序员来说,Redis底层的这种优化咱们是感觉不到的,不过咱们一定要知道。

以上就是不同的数据类型对应的内部编码了。

对于字符串来说,它一共有三种对应的内部编码,一种是raw,一种是int,一种是embstr。对于raw来说,它的底层就相当于C++里面的char数组,一个字符数组,对于int来说,因为Redis有的时候是可以有计数的功能的,并且也有对应的指令,所以Redis在计数的时候,就会直接使用int来保存,也就更方便计数了。对于embstr来说,就是针对短字符串的特殊优化了,咱们知道就行。那么具体是多短的字符串呢?默认的是39字节,但是我们记这种数字没有意义,最好别记,因为对于这种一般都是可以在配置文件里面修改的。

对于hash来说,它的实现方式有两种一种是hashtable,也就是最基本的哈希表,但是当hash里面的元素比较少的时候,就优化成了ziplist了,通过名字我们也能了解,ziplist翻译的话应该是压缩列表,能够节省空间的。不过你要是问Redis内部是怎么实现哈希表的,咱们可能是不太清楚了,就像你吃肉一样,你别管什么做的,味道是那个就行。对于这里也是一样的,咱们知道Redis给我们提供了对应的数据类型就行,内部编码如何实现的,欸咱们就别管了。

往下看的话咱们也能发现我们之前学习的影子,比如linkedlist,skiplist,也就是链表和跳表了,咱们有一定的基础,所以这里咱也就不再赘述了。

不过从Redis3.2开始,列表就引入了新的实现方式,叫做quicklist,这个点其实是结合了linkedlist和ziplist的,就像双端队列是结合了vector和list的了。

那么现在我们知道了有这么多的编码方式,我们如何进行查看呢?我们可以使用命令object encoding key查看:

这里我们有一个小小的命令没有介绍到,即flushall,这个命令的杀伤力其实是和key * 一样的。它的作用是用来清除所有的键值对,那么就和抖音上传的删库跑路是一样的。所以咱们以后在生产环境上可是千万不要轻易使用这个命令,一不小心,欸就完啦。

那么根据上面实际情况的演示,也是符合我们刚才理论的,但是咱们可以延申一点出去,如果咱们使用的浮点数呢?你说是哪种类型?

相信还是大多数人认为的是int,但是实际情况演示的话,我们发现是embstr类型,这就跟Redis自身的优化有联系了。

那么既然它是转换成了短字符串,在运算的时候就相对来说麻烦一点了,运算的时候Redis会把数据从字符串转换成浮点数,然后再运算,接着再转换回来,相对来说是比较麻烦的。

单线程模型

前面我们提及到Redis是可以用来作为计数器的,那么这里我们通过Redis的单线程模型来讨论一个问题----Redis是否存在线程安全的问题

当然是不存在的,比如C++中对一个变量++ 两次,使用不同的线程加1,那么因为++的操作不是原子性的,所以就会导致线程安全的问题。

那么Redis能够使用单线程完成任务,主要还是因为大多数任务都是操作内存,操作是简单的,但是对于Redis的单线程也是有自己的弊端的,如果一条命令阻塞太久了,是会导致整个系统崩溃的,这点是要尤其注意的。

那么这里存在一个经典的面试题:Redis既然是单线程模型,为什么效率那么高呢?

首先,它操作的是内存,数据库访问的是硬盘,这两个的访问时间肯定是差的非常多的。

其次,Redis的核心功能是比较简单的,它不像数据库一样,数据库增删查改涉及到的限制是比较多的,比如外键约束,主键约束啊什么的,即Redis和MySQL如果做同样的工作的话,数据库的开销肯定是很大的,Redis就不会,它没有那么多的约束。

然后,Redis因为是单线程模型,所以自然就免去了线程竞争的开销,这也是一个原因。

最后,虽然Redis是一个CS模式,基于网络通信,但是使用了IO多路复用技术,即一个线程管理多个Sokcet,因为大多数时候的Socket都是没有什么操作的,可以用上菜举例,所以每个客户端和服务端通信就没有想的那么频繁,所以可以利用IO多路复用的技术提高效率。

对于IO多路复用也是有很多API的,有select,poll,epoll,其中效率最高的是epoll。


基本数据类型·string

好了,从编码方式到了单线程模型,我们可以开始string类型的学习了。

首先,对于string我们要有一个认识就是:Redis中的字符串直接就是按照二进制的方式存储的,也就是说不会进行任何的编码方式的转换,存的是什么取的就是什么。这点和MySQL是不一样的,MySQL中默认的字符集是拉丁文,这点在插入中文的时候就会有点冲突了。

那么Redis没事,对于Redis来说,它因为是直接按照二进制的方式存储的,那么像一般的数据类型,图片,json串,xml都是可以直接存储的,像图片这种二进制数据也是可以直接存储,视频也是同理。

不过不太建议直接存储视频类型,因为视频一般都比较大,要是从Redis访问这种大型数据,免不了会造成阻塞,所以其实很少用Redis存储视频的。毕竟都希望Redis快嘛。

重识Set Get

在之前我们介绍了set和get的一般用法,但是它们如果只有这么点用法就太挫了,我们本文介绍它们的其他用法。

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

在我们敲出set的是时候,我们是可以发现提示符提示了后面的一大串,这其实就是set命令的一些选项。因为二者之间隔了中括号,所以就是EX和PX不能同时存在,NX和XX不能同时存在。

EX其实就是Expire的缩写,PX同样是ex的意思,不过设置的时间变成了毫秒了;NX的意思就是只有key不存在的时候才设置,如果key存在就不设置,XX就是反过来的,如果key存在了才设置,不存在就不设置,这里的XX其实是一种覆盖value的行为。

那么有意思的来了,我们其实是可以合并这几个命令的,并且顺便提一下,因为Redis本质是网络服务,那么我们能一次处理多个数据就多次处理,不然网络的开销也是比较大的。

对于毫秒级别的我们可以使用pttl来查看,不过使用了缩写之后,相应的写法也会进行一点改变。

有意思的是nx有缩写,xx反而没有。不过以上就是ex px xx nx的用法。

简单的穿插一下get的用法,这里我们只需要记住,get是只能获取value是string的,对于非string的它获取不了,就像这样:

get只能用来返回string类型的。

现在我们介绍mget和mset命令,它们其实就是get和set的plus版本,因为对于Redis网络服务来说,能减少使用网络通信的次数就减少,所以Redis提供了一次操作多个键值对的命令,分别是mget和mset:

就像这样。它们的时间复杂度是O(N),不过这里的N不是Redis有多少键值对的N,而是操作了多少键值对的N,但是因为本身是通过哈希映射的方式操作的键值对,我们认为是N个O(1)也是完全没有问题的。

incr decr

前文我们提及了Redis可以充当计数的功能,那么自然有对value值进行加减的操作,对应的命令有incr,decr,incrby,decrby,incrbyfloat。

我们一个一个来尝试就可以:

首先是incr和incrby,一个是加1,一个是我们可以指定加数字。不过这里我们可以有一个逆向思维,既然可以加,那么我们加负数也是允许的吧?所以其实incr*和decr*的功能是有一定的重合的,同理,我们也可以使用decr完成incr的操作。

这里其实都是incr和decr的基本操作,我们来看看这个:

如果我们操作一个不存在的key,是默认这个key从0开始的,并且也会给它创建好。这也算是一点点不同了。

对于incrbyfloat的使用也是同理。

上述的时间复杂度都是O(1)。

字符串常用操作

这里我们要涉及的操作有append,setrange,getrange,strlen等。

其实我们有了字符串函数的基础,我们对于这里也是能理解的,比如append,就是

它的返回值是追加之后的总字节数,这里Redis的基本单位是字节,而非字符,对比MySQL中的varchar就是用字符作为基本单位的,这里就不是了。

同样的,如果key不存在,就会创建一个key。

那么我们可以通过append探讨一下编码问题,

我们发现key2是我们没有创建过的,返回的值是6,也就代表了一个汉字是3字节,这是为什么呢?因为我们依稀记得平常使用vs的时候汉字一个是2字节,这其实是因为xshell的终端采用的是utf-8字符集,一个汉字对应的是3字节。

可是,为什么get查出来的是这种16进制的数字呢?

因为我们在前文就提到了,对于Redis来说存放的string就是二进制数据的,所以经过转换之后,我们看到的就是16进制的数据了,那么我们可以验证验证:

对于你好来说,转换之后就是我们方才get出来的结果,那么记住咯,因为string本身就是二进制数据存储的,这后面是一个坑。

对于strlen来说,就非常简单,返回字符串的长度,返回的是字节数,和C++中的一样。

对于setrange和getrange来说,就有点像substr这种函数了:

比如getrange来说,就是返回字符串中的一段,不过有意思的是Redis允许负数作为下标,比如-1就是最后一个元素,以此类推。

对于setrange来说,两个参数,一个是offset一个是value,一个偏移量,一个是改变的字符串,那么:

相信覆盖的现象咱们也是都清楚的。

好了,坑来了,如果我们对中文value操作呢?

因为存储是靠二进制存储的,所以更改的时候,如果我们不是极其幸运的更改到了刚好一个汉字的地方,那么我们大概率就会变成上面的情况了。

应用场景

Redis 中 String 类型的应用场景主要有:

  1. 缓存常用数据 通过 String 类型存储常用数据如页面内容、用户信息等,提高系统性能。
  2. 计数器 / 递增递减 使用 INCR / DECR 命令实现计数功能,适用于网站访问量、点赞数、评论数等计数场景。
  3. 限流控制 使用 SET 命令结合 EXPIRE 设置键值对的过期时间,控制接口请求次数,防止系统过载。
  4. 分布式锁 使用 SET key value NX PXSETEX 等命令实现分布式锁,避免多个进程/线程同时操作同一资源。
  5. 共享 token / session / 验证码 使用 String 存储临时凭证,如用户登录状态、验证码等,支持过期设置。
  6. 字符串拼接或修改 利用 APPENDGETRANGESETRANGE 等命令进行字符串拼接、截取或修改。
  7. 简单队列(不推荐) 虽然 List 类型更适合做队列,但 String 类型也可用于简单的任务队列场景。

以上就是对Redis中string的简单介绍了

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-14,如有侵权请联系 cloudcommunity@tencent 删除数据数据类型字符串redisstring

初识Redis · 数据类型和初识string

前言:

前文我们已经介绍了基本的命令,涉及到的命令有set,get, expire, ttl, type等命令,并且从中引入了过期策略的实现,涉及到了惰性清除和定期清楚,并且我们还介绍了为什么不引入定时器,因为大概率会有另一个线程的参与,而对于Redis来说,它是一个单线程模型,这就违背了。

那么今天我们介绍Redis中的数据类型,虽然说对于Redis存的键值对中的Key都是雷打不动的string,但是对于value来说的话类型就多了,如下图:

但是光有图还不够,因为对于Redis来说,数据类型有那么多,但是和平常学习的C++ MySQL不一样的是它会根据数据类型的长度等因素确定这个数据应该是哪种编码方式,所以我们首先应该先再认识认识基本的数据类型。


编码方式

首先我们不妨思考一个问题:为什么不同的数据它要用不同的编码方式来表示?

你想,对于Redis来说它缓存的是热点数据,那么既然是热点数据也就代表了它能存放的数据容量并不是那么多,所以对于Redis来说,它要保持快的这个点,就一定会有一个操作是自适应,也就是根据不同的应用场景使得自己能够存放更多的数据。

不过啊,对于程序员来说,Redis底层的这种优化咱们是感觉不到的,不过咱们一定要知道。

以上就是不同的数据类型对应的内部编码了。

对于字符串来说,它一共有三种对应的内部编码,一种是raw,一种是int,一种是embstr。对于raw来说,它的底层就相当于C++里面的char数组,一个字符数组,对于int来说,因为Redis有的时候是可以有计数的功能的,并且也有对应的指令,所以Redis在计数的时候,就会直接使用int来保存,也就更方便计数了。对于embstr来说,就是针对短字符串的特殊优化了,咱们知道就行。那么具体是多短的字符串呢?默认的是39字节,但是我们记这种数字没有意义,最好别记,因为对于这种一般都是可以在配置文件里面修改的。

对于hash来说,它的实现方式有两种一种是hashtable,也就是最基本的哈希表,但是当hash里面的元素比较少的时候,就优化成了ziplist了,通过名字我们也能了解,ziplist翻译的话应该是压缩列表,能够节省空间的。不过你要是问Redis内部是怎么实现哈希表的,咱们可能是不太清楚了,就像你吃肉一样,你别管什么做的,味道是那个就行。对于这里也是一样的,咱们知道Redis给我们提供了对应的数据类型就行,内部编码如何实现的,欸咱们就别管了。

往下看的话咱们也能发现我们之前学习的影子,比如linkedlist,skiplist,也就是链表和跳表了,咱们有一定的基础,所以这里咱也就不再赘述了。

不过从Redis3.2开始,列表就引入了新的实现方式,叫做quicklist,这个点其实是结合了linkedlist和ziplist的,就像双端队列是结合了vector和list的了。

那么现在我们知道了有这么多的编码方式,我们如何进行查看呢?我们可以使用命令object encoding key查看:

这里我们有一个小小的命令没有介绍到,即flushall,这个命令的杀伤力其实是和key * 一样的。它的作用是用来清除所有的键值对,那么就和抖音上传的删库跑路是一样的。所以咱们以后在生产环境上可是千万不要轻易使用这个命令,一不小心,欸就完啦。

那么根据上面实际情况的演示,也是符合我们刚才理论的,但是咱们可以延申一点出去,如果咱们使用的浮点数呢?你说是哪种类型?

相信还是大多数人认为的是int,但是实际情况演示的话,我们发现是embstr类型,这就跟Redis自身的优化有联系了。

那么既然它是转换成了短字符串,在运算的时候就相对来说麻烦一点了,运算的时候Redis会把数据从字符串转换成浮点数,然后再运算,接着再转换回来,相对来说是比较麻烦的。

单线程模型

前面我们提及到Redis是可以用来作为计数器的,那么这里我们通过Redis的单线程模型来讨论一个问题----Redis是否存在线程安全的问题

当然是不存在的,比如C++中对一个变量++ 两次,使用不同的线程加1,那么因为++的操作不是原子性的,所以就会导致线程安全的问题。

那么Redis能够使用单线程完成任务,主要还是因为大多数任务都是操作内存,操作是简单的,但是对于Redis的单线程也是有自己的弊端的,如果一条命令阻塞太久了,是会导致整个系统崩溃的,这点是要尤其注意的。

那么这里存在一个经典的面试题:Redis既然是单线程模型,为什么效率那么高呢?

首先,它操作的是内存,数据库访问的是硬盘,这两个的访问时间肯定是差的非常多的。

其次,Redis的核心功能是比较简单的,它不像数据库一样,数据库增删查改涉及到的限制是比较多的,比如外键约束,主键约束啊什么的,即Redis和MySQL如果做同样的工作的话,数据库的开销肯定是很大的,Redis就不会,它没有那么多的约束。

然后,Redis因为是单线程模型,所以自然就免去了线程竞争的开销,这也是一个原因。

最后,虽然Redis是一个CS模式,基于网络通信,但是使用了IO多路复用技术,即一个线程管理多个Sokcet,因为大多数时候的Socket都是没有什么操作的,可以用上菜举例,所以每个客户端和服务端通信就没有想的那么频繁,所以可以利用IO多路复用的技术提高效率。

对于IO多路复用也是有很多API的,有select,poll,epoll,其中效率最高的是epoll。


基本数据类型·string

好了,从编码方式到了单线程模型,我们可以开始string类型的学习了。

首先,对于string我们要有一个认识就是:Redis中的字符串直接就是按照二进制的方式存储的,也就是说不会进行任何的编码方式的转换,存的是什么取的就是什么。这点和MySQL是不一样的,MySQL中默认的字符集是拉丁文,这点在插入中文的时候就会有点冲突了。

那么Redis没事,对于Redis来说,它因为是直接按照二进制的方式存储的,那么像一般的数据类型,图片,json串,xml都是可以直接存储的,像图片这种二进制数据也是可以直接存储,视频也是同理。

不过不太建议直接存储视频类型,因为视频一般都比较大,要是从Redis访问这种大型数据,免不了会造成阻塞,所以其实很少用Redis存储视频的。毕竟都希望Redis快嘛。

重识Set Get

在之前我们介绍了set和get的一般用法,但是它们如果只有这么点用法就太挫了,我们本文介绍它们的其他用法。

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

在我们敲出set的是时候,我们是可以发现提示符提示了后面的一大串,这其实就是set命令的一些选项。因为二者之间隔了中括号,所以就是EX和PX不能同时存在,NX和XX不能同时存在。

EX其实就是Expire的缩写,PX同样是ex的意思,不过设置的时间变成了毫秒了;NX的意思就是只有key不存在的时候才设置,如果key存在就不设置,XX就是反过来的,如果key存在了才设置,不存在就不设置,这里的XX其实是一种覆盖value的行为。

那么有意思的来了,我们其实是可以合并这几个命令的,并且顺便提一下,因为Redis本质是网络服务,那么我们能一次处理多个数据就多次处理,不然网络的开销也是比较大的。

对于毫秒级别的我们可以使用pttl来查看,不过使用了缩写之后,相应的写法也会进行一点改变。

有意思的是nx有缩写,xx反而没有。不过以上就是ex px xx nx的用法。

简单的穿插一下get的用法,这里我们只需要记住,get是只能获取value是string的,对于非string的它获取不了,就像这样:

get只能用来返回string类型的。

现在我们介绍mget和mset命令,它们其实就是get和set的plus版本,因为对于Redis网络服务来说,能减少使用网络通信的次数就减少,所以Redis提供了一次操作多个键值对的命令,分别是mget和mset:

就像这样。它们的时间复杂度是O(N),不过这里的N不是Redis有多少键值对的N,而是操作了多少键值对的N,但是因为本身是通过哈希映射的方式操作的键值对,我们认为是N个O(1)也是完全没有问题的。

incr decr

前文我们提及了Redis可以充当计数的功能,那么自然有对value值进行加减的操作,对应的命令有incr,decr,incrby,decrby,incrbyfloat。

我们一个一个来尝试就可以:

首先是incr和incrby,一个是加1,一个是我们可以指定加数字。不过这里我们可以有一个逆向思维,既然可以加,那么我们加负数也是允许的吧?所以其实incr*和decr*的功能是有一定的重合的,同理,我们也可以使用decr完成incr的操作。

这里其实都是incr和decr的基本操作,我们来看看这个:

如果我们操作一个不存在的key,是默认这个key从0开始的,并且也会给它创建好。这也算是一点点不同了。

对于incrbyfloat的使用也是同理。

上述的时间复杂度都是O(1)。

字符串常用操作

这里我们要涉及的操作有append,setrange,getrange,strlen等。

其实我们有了字符串函数的基础,我们对于这里也是能理解的,比如append,就是

它的返回值是追加之后的总字节数,这里Redis的基本单位是字节,而非字符,对比MySQL中的varchar就是用字符作为基本单位的,这里就不是了。

同样的,如果key不存在,就会创建一个key。

那么我们可以通过append探讨一下编码问题,

我们发现key2是我们没有创建过的,返回的值是6,也就代表了一个汉字是3字节,这是为什么呢?因为我们依稀记得平常使用vs的时候汉字一个是2字节,这其实是因为xshell的终端采用的是utf-8字符集,一个汉字对应的是3字节。

可是,为什么get查出来的是这种16进制的数字呢?

因为我们在前文就提到了,对于Redis来说存放的string就是二进制数据的,所以经过转换之后,我们看到的就是16进制的数据了,那么我们可以验证验证:

对于你好来说,转换之后就是我们方才get出来的结果,那么记住咯,因为string本身就是二进制数据存储的,这后面是一个坑。

对于strlen来说,就非常简单,返回字符串的长度,返回的是字节数,和C++中的一样。

对于setrange和getrange来说,就有点像substr这种函数了:

比如getrange来说,就是返回字符串中的一段,不过有意思的是Redis允许负数作为下标,比如-1就是最后一个元素,以此类推。

对于setrange来说,两个参数,一个是offset一个是value,一个偏移量,一个是改变的字符串,那么:

相信覆盖的现象咱们也是都清楚的。

好了,坑来了,如果我们对中文value操作呢?

因为存储是靠二进制存储的,所以更改的时候,如果我们不是极其幸运的更改到了刚好一个汉字的地方,那么我们大概率就会变成上面的情况了。

应用场景

Redis 中 String 类型的应用场景主要有:

  1. 缓存常用数据 通过 String 类型存储常用数据如页面内容、用户信息等,提高系统性能。
  2. 计数器 / 递增递减 使用 INCR / DECR 命令实现计数功能,适用于网站访问量、点赞数、评论数等计数场景。
  3. 限流控制 使用 SET 命令结合 EXPIRE 设置键值对的过期时间,控制接口请求次数,防止系统过载。
  4. 分布式锁 使用 SET key value NX PXSETEX 等命令实现分布式锁,避免多个进程/线程同时操作同一资源。
  5. 共享 token / session / 验证码 使用 String 存储临时凭证,如用户登录状态、验证码等,支持过期设置。
  6. 字符串拼接或修改 利用 APPENDGETRANGESETRANGE 等命令进行字符串拼接、截取或修改。
  7. 简单队列(不推荐) 虽然 List 类型更适合做队列,但 String 类型也可用于简单的任务队列场景。

以上就是对Redis中string的简单介绍了

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-14,如有侵权请联系 cloudcommunity@tencent 删除数据数据类型字符串redisstring

本文标签: 初识Redis数据类型和初识string