3.2 字符串类型
作为一个爱造轮子的资深极客,小白每次看到自己博客最下面的“Powered byWordPress”都觉得有些不舒服,终于有一天他下定决心要开发一个属于自己的博客。但是用腻了MySQL数据库的小白总想尝试一下新技术,恰好上次参加Node Party时听人介绍过Redis数据库,便想着趁机试一试。可小白只知道Redis是一个键值对数据库,其他的一概不知。抱着试一试的态度,小白找到了自己大学时教计算机的宋老师,一问之下欣喜地发现宋老师竟然对Redis颇有研究。宋老师有感于小白的好学,决定给小白开个小灶。
小白:
宋老师您好,我最近听别人介绍过Redis,当时就对它很感兴趣。恰好最近想开发一个博客,准备尝试一下它。有什么能快速学会Redis的方法吗?
宋老师笑着说:
心急吃不了热豆腐,要学会Redis就要先掌握Redis的键值数据类型和相关的命令。Redis不仅支持多种数据类型,而且还为每种数据类型提供了丰富实用的命令。作为开始,我先来讲讲Redis中最基本的数据类型——字符串类型。
字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用其存储用户的邮箱、JSON化的对象甚至是一张图片。一个字符串类型键允许存储的数据的最大容量是512MB。
字符串类型是其他4种数据类型的基础,其他数据类型和字符串类型的差别从某种角度来说只是组织字符串的形式不同。例如,列表类型是以列表的形式组织字符串,而集合类型是以集合的形式组织字符串。学习过本章后面几节后相信读者对此会有更深的理解。
3.2.2 命令1.赋值与取值
SET key value GET key
SET
S和GET
是Redis中最简单的两个命令,它们实现的功能和编程语言中的读写变量相似,如key="hello"在Redis中是这样表示的:
redis>SET key hello OK
想要读取键值则更简单:
redis>GET key "hello"
当键不存在时会返回空结果。
为了节约篇幅,同时避免读者过早地被编程语言的细节困扰,本书大部分章节将只使用redis-cli进行命令演示(必要的时候会配合伪代码),第5章会专门介绍在编程语言Java中使用Redis的方法。
2.递增数字
INCR key
前面说过字符串类型可以存储任何形式的字符串,当存储的字符串是整数形式时,Redis提供了一个实用的命令INCR
,其作用是让当前键值递增,并返回递增后的值,用法为:
redis>INCR num (integer) 1 redis>INCR num (integer) 2
当要操作的键不存在时会默认键值为0,所以第一次递增后的结果是1。当键值不是整数时Redis会提示错误:
redis>SET foo lorem OK redis>INCR foo (error) ERR value is not an integer or out of range3.2.3 实践
1.文章访问量统计
博客的一个常见的功能是统计文章的访问量,我们可以为每篇文章使用一个名为post:文章ID:page.view
的键来记录文章的访问量,每次访问文章的时候使用INCR命令使相应的键值递增。
提示 Redis对于键的命名并没有强制的要求,但比较好的实践是用“对象类型:对象ID:对象属性”来命名一个键,如使用键user:1:friends来存储ID为1的用户的好友列表。对于多个单词则推荐使用“.”分隔,一方面是沿用以前的习惯(Redis以前版本的键名不能包含空格等特殊字符),另一方面是在redis-cli中容易输入,无需使用双引号包裹。另外为了日后维护方便,键的命名一定要有意义,如u:1:f的可读性显然不如user:1:friends好(虽然采用较短的名称可以节省存储空间,但由于键值的长度往往远远大于键名的长度,所以这部分的节省大部分情况下并不如可读性来得重要)。
2.生成自增ID
那么怎么为每篇文章生成一个唯一ID呢?在关系数据库中我们通过设置字段属性为AUTO_INCREMENT来实现每增加一条记录自动为其生成一个唯一的递增ID的目的,而在Redis中可以通过另一种模式来实现:对于每一类对象使用名为对象类型(复数形式):count
①的键(如users:count)来存储当前类型对象的数量,每增加一个新对象时都使用INCR命令递增该键的值。由于使用INCR命令建立的键的初始键值是1,所以可以很容易得知,INCR命令的返回值既是加入该对象后的当前类型的对象总数,又是该新增对象的ID。
注释:这个键名只是参考命名,实际使用中可以使用任何容易理解的名称。
3.存储文章数据
由于每个字符串类型键只能存储一个字符串,而一篇博客文章是由标题、正文、作者与发布时间等多个元素构成的。为了存储这些元素,我们需要使用序列化函数(如PHP中的serialize和JavaScript中的JSON.stringify)将它们转换成一个字符串。
3.2.4 命令拾遗
1.增加指定的整数
INCRBY key increment
INCRBY
命令与INCR命令基本一样,只不过前者可以通过increment参数指定一次增加的数值,如:
redis>INCRBY bar 2 (integer) 2 redis>INCRBY bar 3 (integer) 5
2.减少指定的整数
DECR key DECRBY key decrement
DECR
命令与INCR命令用法相同,只不过是让键值递减,例如:
redis>DECR bar (integer)4
而DECRBY
命令的作用不用介绍想必读者就可以猜到,DECRBY key 5 相当于INCRBY key -5。
3.增加指定浮点数
INCRBYFLOAT key increment
INCRBYFLOAT
命令类似INCRBY命令,差别是前者可以递增一个双精度浮点数,如:
redis>INCRBYFLOAT bar 2.7 "6.7" redis>INCRBYFLOAT bar 5E+4 "50006.69999999999999929"
4.向尾部追加值
APPEND key value
APPEND
作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于SET key value。返回值是追加后字符串的总长度。例如:
redis>SET key hello OK redis>APPEND key " world!" (integer) 12
此时key的值是"hello world!"。APPEND命令的第二个参数加了双引号,原因是该参数包含空格,在redis-cli中输入需要双引号以示区分。
5.获取字符串长度
STRLEN keySTRLEN
ST命令返回键值的长度,如果键不存在则返回0。例如:
redis>STRLEN key (integer)12 redis>SET key 你好 OK redis>STRLEN key (integer)6
前面提到了字符串类型可以存储二进制数据,所以它可以存储任何编码的字符串。例子中Redis接收到的是使用UTF-8编码的中文,由于“你”和“好”两个字的UTF-8编码的长度都是3,所以此例中会返回6。
6.同时获得/设置多个键值
MGET key [key …] MSET key value [key value …]
MGET
M/MSET
与GET/SET相似,不过MGET/MSET可以同时获得/设置多个键的键值。例如:
redis>MSET key1 v1 key2 v2 key3 v3 OK redis>GET key2 "v2" redis>MGET key1 key3 1) "v1" 2) "v3"
7.位操作
GETBIT key offset SETBIT key offset value BITCOUNT key [start] [end] BITOP operation destkey key [key …]
一个字节由8个二进制位组成,Redis提供了4个命令可以直接对二进制位进行操作。为了演示,我们首先将foo键赋值为bar:
redis>SET foo bar OK
bar的3个字母对应的ASCII码分别为98、97和114,转换成二进制后分别为1100010、1100001和1110010,所以foo键中的二进制位结构如图3-3所示。
GETBIT
命令可以获得一个字符串类型键指定位置的二进制位的值(0或1),索引从0开始:
redis>GETBIT foo 0 (integer) 0 redis>GETBIT foo 6 (integer) 1
如果需要获取的二进制位的索引超出了键值的二进制位的实际长度则默认位值是0:
redis>GETBIT foo 100000 (integer) 0
SETBIT
命令可以设置字符串类型键指定位置的二进制位的值,返回值是该位置的旧值。如我们要将foo键值设置为aar,可以通过位操作将foo键的二进制位的索引第6位设为0,第7位设为1:
redis>SETBIT foo 6 0 (integer) 1 redis>SETBIT foo 7 1 (integer) 0 redis>GET foo "aar"
如果要设置的位置超过了键值的二进制位的长度,SETBIT命令会自动将中间的二进制位设置为0,同理设置一个不存在的键的指定二进制位的值会自动将其前面的位赋值为0:
redis>SETBIT nofoo 10 1 (integer) 0 redis>GETBIT nofoo 5 (integer) 0
BITCOUNT
B命令可以获得字符串类型键中值是1的二进制位个数,例如:
redis>BITCOUNT foo (integer)10
可以通过参数来限制统计的字节范围,如我们只希望统计前两个字节(即"aa"):
redis>BITCOUNT foo 0 1 (integer)6
BITOP
B命令可以对多个字符串类型键进行位运算,并将结果存储在destkey参数指定的键中。BITOP命令支持的运算操作有AND
、OR、XOR
和NOT
。如我们可以对bar和aar进行OR运算:
redis>SET foo1 bar OK redis>SET foo2 aar OK redis>BITOP OR res foo1 foo2 (integer) 3 redis>GET res "car"
运算过程如图3-4所示。
利用位操作命令可以非常紧凑地存储布尔值。比如某网站的每个用户都有一个递增的整数ID,如果使用一个字符串类型键配合位操作来记录每个用户的性别(用户ID作为索引,二进制位值1和0表示男性和女性),那么记录100万个用户的性别只需占用100 KB多的空间,而且由于GETBIT和SETBIT的时间复杂度都是0(1),所以读取二进制位值性能很高。