Redis学习笔记(2) Redis基础类型及命令之一

时间:2023-03-08 18:29:40
Redis学习笔记(2) Redis基础类型及命令之一

1. 基础命令

  (1) 获取符合规则的键名列表

  格式为:KEYS pattern

  其中pattern表示支持通配符

# 建立一个名为bar的键
127.0.0.1:> SET bar
OK
# 获取Redis所有键
127.0.0.1:> KEYS *
) "bar"

  注意:KEYS命令需要遍历Redis中所有键,因此当键的数量较多时会影响性能。

  (2) 判断一个键是否存在

  格式为:EXISTS key

  如果存在则返回1,否则返回0.

127.0.0.1:> EXISTS bar
(integer)
127.0.0.1:> EXISTS noexists
(integer)

  (3) 删除键

  格式为:DEL key (key ...)

  可以删除一个或多个键,返回值是删除键的个数。

127.0.0.1:> DEL bar
(integer)
127.0.0.1:> DEL bar
(integer)

  DEL不支持通配符,可结合管道符和xargs命令实现删除符合规则的键。如删除一"user:"开头的键:

  redis-cli KEYS "user:*" | xargs redis-cli DEL,或

  redis-cli DEL 'redis-cli KEYS "user:*"'(测试未通过,o(╯□╰)o)

  redis-cli -n 1 keys "user*" | xargs redis-cli -n 1 del  #删除指定数据库1中"user*"的键

  (4) 获得键值的数据类型

  格式为:TYPE key

  返回的值可能是字符串类型string,散列类型hash,列表类型list,集合类型set及有序集合类型zset。

127.0.0.1:> SET foo
OK
127.0.0.1:> TYPE foo
string
127.0.0.1:> LPUSH bar
(integer)
127.0.0.1:> TYPE bar
list

2. 数据类型

  (1) 字符串类型

  字符串类型可以存储用户邮箱、JSON化的对象甚至是一张图片,一个字符串键存储的数据的最大容量是512MB。字符串是其他4中数据类型的基础。

  1) 命令

  ① 赋值和取值

  格式为:SET key value    GET key

  ② 递增数字

  格式为:INCR key

  作用是让当前键值递增,并返回递增后的值。

127.0.0.1:> INCR num
(integer)
127.0.0.1:> INCR num
(integer)

  ③ 增加指定的整数

  格式为:INCRBY key increment

  ④ 减少指定的整数

  格式为:DECR key 或 DECRBY key value

  ⑤ 增加指定浮点数

  格式为:INCRBYFLOAT key increment

  INCRBYFLOAT 与INCRBY类似,差别是前者可以递增一个双精度浮点数

127.0.0.1:> INCRBYFLOAT bar 2.7
"2.7"
127.0.0.1:> INCRBYFLOAT bar 5E+
"50002.69999999999999929"

  ⑥ 向尾部追加值

  格式为:APPEND key value

  键不存在则将该键的值设为value,返回值是追加后字符串的总长度。

127.0.0.1:> SET key hello
OK
127.0.0.1:> APPEND key " world!"
(integer)

  ⑦ 获取字符串长度

  格式为:STRLEN key

  STRLEN命令返回键值的长度,不存在则为0.

127.0.0.1:> STRLEN key
(integer)
127.0.0.1:> SET key 你好
OK
127.0.0.1:> STRLEN key
(integer) 4
# UTF-8编码的中文长度为2(不同系统的字符集有所差别)

  ⑧ 同时获得/设置多个键值

  格式为:MGET key {key ...}     MSET key value [key value...]

127.0.0.1:> MSET key1 v1 key2 v2 key3 v3
OK
127.0.0.1:> GET key2
"v2"
127.0.0.1:> MGET key1 key3
) "v1"
) "v3"

  ⑨ 位操作

  格式为:GETBIT key offset    SETBIT key offset value

      BITCOUNT key [start] [end]  BITOP operation destkey key [key ...]

  GETBIT命令可以获得一个字符串类型键在指定位置的二进制位的值,索引从0开始;

  SETBIT可以设置字符串类型指定位置的二进制位的值,返回值是该位置的旧值。

  BITCOUNT可以获得字符串类型中值是1的二进制位个数。通过参数限制统计的字节范围:BITCOUNT foo 0 1。

  BITOP命令可以对多个字符串类型键进行位运算,并将结果存储在destkey参数指定的键中。支持的运算符有AND,OR,XOR及NOT。

127.0.0.1:> SET foo1 bar
OK
127.0.0.1:> SET foo2 aar
OK
127.0.0.1:> BITOP OR res foo1 foo2
(integer)
127.0.0.1:> GET res
"car"  

  2) 示例

  博客常见功能是统计文章的访问量,可以为文章设置键post:文章 ID:page.view来记录文章的访问量,每次访问时使用INCR使相应的键值递增。Redis命名一般采用"对象类型:对象ID:对象属性"格式,当出现多个单词时则使用"."。

  Redis自增ID的生成:对于每一类对象使用名为对象类型(复数形式):count的键(如users:count)来存储当前类型对象的数量,每增加一个新对象时都使用INCR递增该键的值。INCR返回的值既是加入该对象后的当前类型的对象总数,又是该新增对象的ID。

  每个字符串类型键只能存储一个字符串,而一篇文章由标题、正文、作者等多个元素构成,此时需要通过序列化(如JSON)将其转换为字符串。因此发布新文章时与Redis操作相关的伪代码如下:

# 首先获取新文章的ID
$postID = INCR posts:count
# 将文章的多元素序列化成字符串(PHP)
$serializedPost = serialize($title, $content, $author, $time)
# 把序列化后的字符串存入一个字符串类型的键中
SET post:$postID:data, $serializedPort

  获取文章数据的伪代码:

# 从Redis中读取文章数据(以ID为42的文章为例)
$serializedPost = GET post::data
# 将文章数据反序列化成文章的各个元素
$titlt, $content,$author, $time = unserialize($serializedPost)
# 获取并递增文章的访问数量
#count = INCR post::page.view

  (2) 散列类型

  散列类型存储了字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型(集合类型不能嵌套)。散列类型适合存储对象:使用对象类别和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。

  1) 命令

  ① 赋值与取值

  其格式为:HSET key field value  HGET key field  HMSET key filed value [field value...]  

       HMGET key field [field...]  HGETALL key

  例:

127.0.0.1:> HSET car price
(integer)
127.0.0.1:> HSET car name BMW
(integer)
127.0.0.1:> HGET car name
"BMW"

  注意,HSET命令不区分插入和更新操作。当执行的是插入操作时,HSET命令返回1,当执行的是更新操作时,HSET命令返回0.Redis中每个键都属于一个明确的数据类型。

  当需同时设置多个字段的值事,可以使用HMSET命令,如:

  HSET key field1 value1 与 HSET key field2 value2可改写为HMSET key field1 value1 field2 value2

  HMGET命令则可同时获得多个字段的值。HGETALL命令适用于获取键中所有字段和字段值却不知道键中有哪些字段的情况。

127.0.0.1:> HMGET car price name
) ""
) "BMW" #返回结果是字段和字段值组成的列表
127.0.0.1:> HGETALL car
) "price"
) ""
) "name"
) "BMW"

  ② 判断字段是否存在

  格式为:HEXISTS key field,存在则返回1,否则返回0。

127.0.0.1:> HEXISTS car model
(integer)
127.0.0.1:> HSET car model c200
(integer)
127.0.0.1:> HEXISTS car model
(integer)

  ③ 当字段不存在时赋值

  格式为:HSETNX key field value,HSETNX与HSET命令的区别在于:如果字段已存在,HSETNX不会执行任何操作。

  ④ 增加数字

  格式为:HINCRBY key field increment,散列类型没有HINCR命令,但可通过HINCRBY key field 1实现。

#HINCRBY命令会自动创建person键,且score在执行命令前值为0
127.0.0.1:> HINCRBY person score
(integer)

  ⑤ 删除命令

  格式为:HDEL key field [field ...],可以删除一个或多个字段,返回值为被删除的字段个数。

127.0.0.1:> HDEL car price name
(integer)
127.0.0.1:> HDEL car price
(integer)

  ⑥ 只获取字段名或字段值

  格式为:HKEYS key   HVALS ket

127.0.0.1:> HKEYS car
) "model"
127.0.0.1:> HVALS car
) "c200"

  ⑦ 获取字段数量

  格式为:HLEN key

127.0.0.1:> HLEN car
(integer)

  2) 示例

  在涉及到存储文章缩略名时,可使用一个散列类型的键slug.to.id来存储文件缩略名和ID之间的映射关系,其中字段用来记录缩略名,字段值用来记录缩略名对应的ID。发布文章的代码修改:

$postID = INCR posts:count

# 判断用户输入的slug是否可用,可用则记录
$isSlugAvaliable = HSETNX slug.to.id, $slug, $postID if $isSlugAvailable is
# slug已经用过,提示用户更换slug
exit
HMSET post:$postID, title, $title, content, $content, slug, $slug...

  当用户访问文章时,从网址中得到缩略名,并查询slug.to.id键来获取文章ID:

$postID = HGET slug.to.id , $slug

if not $posyID
print 文章不存在
exit
$post = HGETALL post:$posyID
print 文章标题:$post.title

  需要注意:如果要修改文章的缩略名,一定不能忘了修改slug.to.id键对应的字段。

# 判断新的slug是否可用,可用则记录
$isSlugAvaliable = HSETNX slug.to.id, #newSlug,
if $isSlugAvaliable is
exit # 获取旧的缩略图
$oldSlug = HGET post:, slug # 设置新的缩略名
HSET post:, slug, $newSlug # 删除旧的缩略名
HDEL slug.to.id $oldSlug

  (3) 列表类型

  列表类型可以存储一个有序的字符串列表(双向链表实现),常用的操作是向列表两端添加元素或获得列表的某一个片段。

  1) 命令

  ① 向列表两端增加元素

  格式为:LPUSH key value [value... ]  RPUSH key value [value...]

  LPUSH向列表左边增加元素,RPUSH向列表右边增加元素,返回值表示增加元素后列表的长度。

127.0.0.1:> LPUSH numbers
(integer)
127.0.0.1:> LPUSH numbers
(integer)
RPUSH numbers -
(integer)

  ② 从列表两端弹出元素

  格式为:LPOP key    RPOP key

  LPOP执行两步操作:第一步是将列表左端的元素从列表中移除,第二步是返回被移除的元素值。

127.0.0.1:> LPOP numbers
""
127.0.0.1:> RPOP numbers
"-1"

  如果想把列表当做栈,则搭配使用LPUSH和LPOP或RPUSH和RPOP,如果相当成队列,则搭配使用LPUSH和RPOP或RPUSH和LPOP。

  ③ 获取列表元素个数

  格式为:LLEN key

127.0.0.1:> LLEN numbers
(integer)

  ④ 获取列表片段 

  格式为:LRANGE key start end,返回索引从start和stop之间的所有元素(包含两端的元素),起始索引为0

127.0.0.1:> LRANGE numbers
) ""
) ""
) ""

  LRANGE也支持负索引,表示从右边开始计算序数,-1指最右边第一个元素,-2指最右边第二个元素。因此,LRANGE numbers 0 -1可以获取列表中所有元素。

  此外,如果start的索引位置比stop的索引位置靠后,则会返回空列表;如果stop大于实际的索引范围,则会返回列表最右边的元素:

127.0.0.1:> LRANGE numbers - -
) ""
) ""
127.0.0.1:> LRANGE numbers
) ""
) ""

  ⑤ 删除列表中指定的值

  格式为:LREM key count value,LREM会删除列表中前count个值为value的元素,返回值是实际删除的元素个数。

  当count>0时LREM会从列表左端开始删除前count个值为value的元素;当count<0时LREM会从列表右边开始删除前|count|个值为value的元素;当count=0时LREM会删除所有值为value的元素。

127.0.0.1:> RPUSH numbers
(integer)
127.0.0.1:> LRANGE numbers -
) ""
) ""
) ""
) "" #从右边开始删除第一个值为""的元素
127.0.0.1:> LREM numbers -
(integer) 127.0.0.1:> LRANGE numbers -
) ""
) ""
) ""

  ⑥ 获得/设置指定索引的元素值

  格式为:LINDEX key index    LSET key index value

# 列表当做数组来用,LINDEX必不可少,LINDEX用于返回指定索引的元素,从0开始
127.0.0.1:> LINDEX numbers
"" # index为负数表示从右边开始计算的索引
127.0.0.1:> LINDEX numbers -
"" # LSET会将索引为index的元素赋值为value
127.0.0.1:> LSET numbers
OK
127.0.0.1:> LINDEX numbers
""

  ⑦ 只保留列表指定片段

  格式为:LTRIM key start end

  该命令可以删除指定索引范围之外的所有元素,其指定列表范围的方法同LRANGE。

127.0.0.1:> LRANGE numbers
) ""
) "" 127.0.0.1:> LTRIM numbers
OK 127.0.0.1:> LRANGE numbers
) ""
) ""

  LTRIM命令和LPUSH命令一起使用来限制列表中的元素的数量,如记录日志时希望保留最近的100条日志,则每次加入新元素时调用一次LTRIM命令即可:LPUSH logs $newLog  LTRIM logs 0 99

  ⑧ 向列表中插入元素

  格式为:LINSERT key BEFORE|AFTER pivot value

  该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。LINSERT返回值是插入后列表的元素个数。

127.0.0.1:> lrange numbers  -
) ""
) ""
) "" 127.0.0.1:> LINSERT numbers AFTER
(integer) 127.0.0.1:> LINSERT numbers BEFORE
(integer) 127.0.0.1:> LRANGE numbers -
) ""
) ""
) ""
) "3"

  ⑨ 将元素从一个列表转到另一个列表

  格式为:RPOPLPUSH source destination

  先执行RPOP命令,再执行LPUSH命令,即先从source列表类型键的右边弹出一个元素,然后将其加入到destination列表类型键的左边,并返回这个元素的值。伪代码如下:

def rpoplpush ($source, $destination)
$value = RPOP $source
LPUSH $destination, $value
return $value

  当把列表类型作为队列使用时,RPOPLPUSH可以很直观地在多个队列中传递数据。当source和destination相同时,RPOPLPUSH不断将队尾的元素移到队首。(该特性可以实现网站监控系统:使用一个队列存储需要监控的网址,然后监控程序不断的使用RPOPLPUSH循环取出一个网址来测试可用性,且在程序执行过程中仍然可以不断向网址列表中加入新网址,整个系统容易扩展,并允许多个客户端同时处理队列)。  

  2) 示例:

  使用列表类型键posts:list记录文章ID列表,当发布新文章时使用LPUSH命令把新文章ID加入该列表中,此外,删除文章时也要记得把列表中的文章ID删除。

  有了文章ID列表,可使用LRANGE命令实现文章的分页显示:

$postsPerPage=
$start=($currentPage-)*$postsPerPage
$end=$currentPage*$postsPerPage-
$postsID=LRANGE posts:list, $start, $end #获得此页需要显示的文章列表,接通过循环读取文章
for each $id in $postsID
$post=HGETALL post:$id
print 文章标题:$post.title

  这样显示的文章列表是根据加入列表的顺序倒序的。

  列表类新存储文章的评论,适合将一条评论的各个元素序列化成字符串后作为列表类型键中的元素来存储。下面使用列表类型键post:文章ID:comments来存储某个文章的所有评论:

# 将评论序列化成字符串
$serializedComment=serialize($author, $mail, $time, $content)
LPUSH post::comments, $serializedComment