Redis-2-五种基本类型及相关命令

1.字符串类型:string

字符串类型是Redis中最基本的数据类型,他能存储任何形式的字符串,包括二进制数据。 **一个字符串类型的键允许存储的数据最大容量是512MB。**
字符串类型可以用来存储用户的信息如邮箱、JSON化的对象甚至一张图片。

1.1 命令

首先需要说明的是,在Redis中,所有的redis命令都是原子操作,包括后面提到的递增命令 incr 等。

1.取值与复制:

    set key value
    get key
重新给存在的key赋值时会覆盖原有value;取值时,当key不存在时会返回空结果。

例子:

127.0.0.1:6379[1]> set name hello
OK
127.0.0.1:6379[1]> get name
"hello"
127.0.0.1:6379> set name okok
OK
127.0.0.1:6379> get name
"okok"
127.0.0.1:6379> get newName
(nil)

2.递增数字

当存储的字符串是整数的形式时,其实Redis提供了一个实用的命令 incr ,其作用是让当前的键值递增,并返回递增后的值。
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> get num
"2"
当 incr 的 key 不存在时,默认的键值是 0 ,所以第一次递增后的结果是 1 。当 incr 的 key 的值 不是整数时会报错。
127.0.0.1:6379> set eg hello
OK
127.0.0.1:6379> get eg
"hello"
127.0.0.1:6379> incr eg
(error) ERR value is not an integer or out of range

3.其他命令

1.增加指定的整数:incrby key num

2.让值递减:decr key

3.减少指定的整数:decrby key num  (相当于:incrby key -num)

4.向尾部追加值:append key add_value  (key不存在相当于set操作,key存在相当于字符串尾部拼接,返回值是字符串拼接后长度)

5.获取字符串长度:strlen key  (键不存在返回 0 )
127.0.0.1:6379> set word 你好
OK
127.0.0.1:6379> strlen word
(integer) 6
127.0.0.1:6379> get word
"\xe4\xbd\xa0\xe5\xa5\xbd"

由于可存储任何类型的字符串,如上若utf-8编码的中文,“你”、“好”两个字的utf-8编码长度都是3,所以返回的长度是6。

6.同时设置/获取多个键值:
    mset key1 v1 key2 v2 key3 v3
    mget key1 key2 key3

7.位操作:
    getbit key offset
    setbit key offset value
    bitcount key [start] [end]
    bitop operation destkey key [key key ...]

1个字节是8个二进制位,Redis提供了4个命令可以直接对二进制位进行操作。具体说明见博客:https://www.cnblogs.com/lxhyty/p/11364557.html

利用为操作命令可以非常紧凑的存储布尔值。比如某网站的每个用户都有一个递增的整数ID,如果使用一个字符串类型的键配合位造作来记录每个用户的性别(或是否在线等),用户ID作为索引,对应二进制位的1、0表示男、女(在线、离线)。那么记录100万个用户的信息只需要100多KB的空间,且由于getbit、setbit的时间复杂度是O(1),所以读取操作二进制位的性能很高。

1.2 实践

1.文章访问量统计

很容易想到,做访问量统计的时候,我们可以使用 incr 来递增访问量的值。

Redis对于key的命名没有强制的要求,但比较好的命名格式是“对象类型:对象ID:对象属性” ,如使用键user:1:friends来存储ID为1的用户的好友列表。在需要有多个单词来表示时推荐使用“.”来分割,而不是在编程时的驼峰命名,如文章的访问量的key我们是post:24:page.view ,每次访问量递增使用命令 incr post:24:page.view 。

2.生成自增ID

在关系型数据库中我们设置文章的ID字段属性为AUTO_INCREAMENT来实现自动为每篇文章生成唯一ID,而在Redis中可以通过另一种方式来实现,对每一类对象设置键为“对象类型(复数形式):count”来存储当前类型对象的数量,然后使用 incr 来增加该键的值,返回值即是新增对象的ID。

3.存储文章数据

一篇文章是由标题、作者、正文等多个元素组成的。为了用字符串类型的键值存储这些元素,我们需要用到序列化函数将他们转换成一个字符串。(因为字符串类型可以存储二进制数据,所以也可以使用MessagePack进行序列化,速度更快,占用空间也更小。)

至此,我们在某一门编程语言中可以有如下操作:(伪代码)

# 获取新文章的ID
postID = incr posts:count
# 将博客文章多个元素序列化成字符串
postData = serialize(title,content,autor,createdtime)
# 把序列化的字符串存入一个字符串类型的键中
set post:postID:data postData

2.散列类型:hash

散列类型的键值也是一种字典结构,其存储了字段和字段值的映射,一个散列类型键最多可以包含 (2^32)-1 个字段。字段值只能是字符串,不能是其他类型。同所有的redis数据类型一样,散列类型也不能嵌套其他数据类型。

散列类型适合存储对象。使用对象类别和对象ID构成 key ,使用 字段和字段值 表示对象的属性。另外在Redis中同一类对象可以有不同的字段。如对象 user:1 有字段 name、passward、money,而对象user:2除此之外还可以单独有字段email等。

2.1命令

1.复制与取值

hset 用来给新建 哈希键值,也可以用来给字段赋值,hget 用来获取字段的值。

    hset key field1 value1 [field2 value2 field3 value3 ...]    # 新建哈希键值
    hset key field value    # 给哈希设置单个字段
    hmset key field1 value1 [field2 value2 ...]     # 同时设置多个字段
    hget key field     # 获取单个字段值
    hgetall key     # 获取所有字段值
    hmget key field1 [field2 field3 ...]    # 同时获取多个字段值
hset 命令不区分更新和插入操作,所以无需额外判断字段是否存在来决定进行插入还是更新操作。当字段不存在,执行的是插入操作,hset 命令会返回 1 ,字段存在,执行的是更新操作,hset 命令返回 0 。甚至当哈希键本身不存在的时候还会自动建立它。

2.判断字段存在

    hexists key field   (判断字段是否存在:存在则返回 1 ,不存在则会返回 0 )
    hsetnx key field value   (当字段存在,不执行任何操作,返回 0 。字段不存在则赋值,返回 1 )

再次声明,这些命令都是原子操作,不必担心竞争操作导致出错。

3.增加数字

    hincrby key field num
    # 与字符串类型的增加数字的 incrby 相似,散列类型没有 hincr ,要实现加一的功能可以用 hincrby key field 1
    # 当key或者字段field不存在时,都会自动的建立它,建立的字段在执行命令前初始整数值是 0 ,返回值是增加值之后的值。

4.删除字段

    hdel key field1 [field2 field3 ...]
    # 这条命令可以删除一个或者多个字段,返回值是被删除的字段的个数。删除不存在的字段,返回值是 0 。

5.其他命令

    1.只获取字段名:hkeys key
    2.只获取字段值:hvals key
    3.获取字段数量:hlen key

2.2 实践

1.存储文章数据

在上面我们将整个文章对象序列化后用一个字符串类型来存储,他无法对文章的某个属性做原子操作,如两个客户端同时获取并反序列化一篇文章,然后修改不同的属性后存入,那么后一个修改会覆盖前一个修改,最后只有一个属性被修改成功。另外在我们只需要对象的部分属性时,我们也不得不把整个对象取出来操作,比较消耗资源。

这里我们就可以使用哈希类型来存储文章对象了。如:

hset post:1001 title 第一篇文章 author admin content 这是我的第一篇文章 time 2019-8-15
# 只获取文章标题
hget post:1001 title
# 修改文章标题
hset post:1001 title 修改后的第一篇文章
其实存储同样的数据,散列类型往往比字符串类型更加节约空间,这和redis数据类型的底层实现有关。

3.列表类型:list

列表类型可以存储一个有序的字符串列表,常用的操作是向列表的两端添加元素,或者获得列表的某一个片段。与散列类型一样,一个列表类型最多能容纳 (2^32)-1 个元素。

Redis的列表内部是使用双向链表实现的,所以在列表两端操作元素的时间复杂度为O(1),获取越接近两端的元素速度越快。不过使用链表的代价是通过索引访问元素比较慢。由于可以在两端操作,我们可以将其作为队列或者栈来使用。

3.1 命令

1.向列表两端添加元素:

    lpush key value [value value ...]   # 在列表左端添加元素
    rpush key value [value value ...]   # 在列表右端添加元素
    # 返回值是 增加元素后的列表长度。

2.从列表两端弹出元素:

    lpop key    # 从列表左端弹出元素
    rpop key    # 从列表右端弹出元素
    # 返回值是被弹出的元素

3.获取列表元素个数:

    llen key    # 注意是两个"l"字母
这个命令的时间复杂度是O(1),因为列表直接存储了当前元素的个数,使用时Redis会直接读取现成的值,而不需要像部分关系数据库那样遍历一次数据表。

4.获取列表片段:

    lrange key start stop   # 从列表左端开始获取列表片段
    # 返回索引从start到stop之间的所有元素,包括了start和stop本身的元素(即闭区间)。
列表的起始索引是0,lrange命令也支持负索引,表示从列表右端开始计算次序,如"-1"表示最右边第一个元素,"-2"表示最右边倒数第二个元素,以此类推。如:lrange key -3 -1 表示返回列表的最右边的三个元素。

注意:
(1) 如果start比stop大,则返回空列表。
(2) 如果stop大于实际索引范围,则会返回到列表最右边的元素。

5.删除列表中指定的值:

    lrem key count value
    # 删除列表中前count个值为value的元素,返回实际被删除的元素的个数。
当 count > 0 时,从列表左端开始删除。
当 count < 0 时,从列表右端开始删除。
当 count = 0 时,删除列表所有值为value的元素。

6.其他命令:

    1.获取指定索引的元素值:lindex key index
    2.设置指定索引的元素值:lset key index value
    3.只保留列表的指定片段:ltrim key start stop    (删除start之前和stop之后的所有元素)
    4.向列表插入元素:linsert key before|after pivot value
        在列表中从左开始查找值为pivot的元素,然后根据是before还是after决定在它前面还是后面插入value,返回插入后元素的个数。
    5.将元素从一个列表转移到另一个列表:rpoplpush lista listb   (从lista弹出最右端的元素添加到listb的左边,返回值是这个元素的值)

3.2 实践

列表可以用来存储如文章ID,以实现显示文章列表、文章列表分页的目的,存储评论列表等。因为是双向链表实现,有队列栈的特性,在一些场合也非常适用,如“新鲜事”,“日志”等,他们很少访问中间元素。

4.集合类型:set

集合中每个元素都不相同,且没有顺序。一个集合类型最多可以存储 (2^32)-1 个字符串。

集合类型在Redis内部是使用值为空的散列表(hash table)实现的,所以添加、删除、判断等操作时间复杂度都是O(1)。多个集合之间也可以进行并集、交集、差集运算。

4.1 命令

1.增加/删除元素:

    sadd key value [value ...]  # 添加元素,key不存在时自动创建。返回值是成功加入的元素个数。
    srem key value [value ...]  # 删除元素,返回值是成功删除的元素个数。

2.获取集合所有元素:

    smembers key

3.判断某个元素是否在集合中:

    sismember key value     # 存在时返回 1 ,不存在时返回 0

4.集合运算:

    sdiff key1 [key2 ...]   # 差集运算。运算结果为:key1 - key2 - ...
    sinter key1 [key2 ...]  # 交集运算。运算结果为:key1 n key2 n ...
    sunion key1 [key2 ...]  # 并集运算。运算结果为:key1 u key2 u ...

5.其他命令:

1.获取集合元素的个数:scard key
2.从集合弹出一个元素:spop key  (随机弹出一个元素)
3.随机获取集合中的元素:srandmember key [count]    (count可以指定获取的元素个数,不写则默认获取一个元素)
4.进行集合运算并将结果存储:
    sdiffstore res key1 [key2 ...]   # 差集运算。运算结果存储在集合 res 中
    sinterstore res key1 [key2 ...]  # 交集运算。运算结果存储在集合 res 中
    sunionstore res key1 [key2 ...]  # 并集运算。运算结果存储在集合 res 中

4.2 实践

集合可以存储如文章标签等数据,通过标签来查询文章。

5.有序集合类型:zset

在集合的基础上为每个元素都关联了一个分数,以此来实现有序。可以获取分数最高(最低)的前N个元素、获取指定分数范围内的元素等与分数有关的操作。虽然集合中每个元素不同但他们的分数可以相同。

列表通过链表实现,所以获取中间元素较慢,但有序集合类型是使用散列表和跳跃表实现的,所以即使读取位于中间的数据速度也会很快(时间复杂度O(log(N)))。有序集合可以通过调整元素的分数来简单的改变元素的位置。有序集合比列表更加耗费内存。

5.1 命令

1.增加元素:

    zadd key score member [score member ...]
若加入的元素已经存在,则只会更新它的分数,zadd 返回值是成功新加入有序集合的元素个数(更新分数不算)。这个分数可以是整数也可以是双精度浮点数,另外"+inf"和"-inf"表示正无穷和负无穷。

2.获取元素的分数:

    zscore key member

3.获取在某个位置区间的元素列表:

    zrange key start stop [withscore]   # 结果按照分数从小到大排列
    zrerange key start stop [withscore]     # 结果按照分数从大到小排列
zrange命令的时间复杂度是O(logn+m)。它与lrange命令相似,都是闭区间,可用负索引。如果排列结果要加上分数则命令尾部加上 withscore 。

4.获取指定分数范围的元素:

    zrangebyscore key min max [withscore] [limit offset count]
该命令按从小到大的顺序返回分数在min和max(包括min、max)之间的元素。如果希望不包含端点值,即开区间,可以在分数前加上"("符号,如:zrangebyscore grad 80 (100

与sql语句一样,limit offset count 表示在获取到的元素列表的基础上向后便宜offset个元素,且只获取前count个元素。

5.增加某个元素的分数:

    zincrby key num member  # num表示给member增加的分数,可以是负数表示减分数。

6.其他命令:

    1.获取集合中元素的数量:zcard key
    2.获取指定分数范围内的元素个数:zcount key min max  (闭区间)
    3.删除一个或多个元素:zrem key member [member ...]  (返回值是成功删除元素的个数)
    4.删除指定位置内的元素:zremrangebyrank key start stop  (命令按照分数从小到大的顺序删除第start个到stop的元素,返回成功删除的元素个数)
    5.删除指定分数范围内的元素:zremrangebyscore key min max    (返回成功删除的元素个数)
    6.获取元素的位置:
        zrank key member    (从小到大的顺序排列的位置)
        zrevrank key member (从大到小的顺序排列的位置)
    7.有序集合的集合运算:
        zunionscore res numkeys key [key ...]   (计算给定的一个或多个有序集的并集,并存储在 res 中)
        zinterscore res numkeys key [key ...]   (计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 res 中)

5.2 实践

有序集合类型,很方便的实现如热评,点击量排序,时间排序等。
posted @ 2019-11-29 16:52  长安223  阅读(610)  评论(0编辑  收藏  举报