读redis官方《A fifteen minute introduction to Redis data types》的笔记

本文深入探讨了Redis支持的多种数据类型,包括字符串、列表、集合、有序集合,并提供了实际应用场景与操作方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近在给缓存系统做选型,memcached和redis是目前比较火的;相对而言memcached支持的数据类型比较多单一,使用和运维也比较简单;redis支持多种数据类型,但由于其支持数据持久化,所以在运维方面相对复杂一些。有时间,再深入学习下redis的相关知识,为以后的使用做准备。
本文是我阅读了官方文档《A fifteen minute introduction to Redis data types》( 点此打开原文)后记下的笔记,以备后用。

笔记

概要

redis不只是一个key-value数据库,ta更是一个支持多种不同数据结构的服务器。redis支持的数据类型包括:
  • String--字符串
  • List--数组(redis中其实指的是链表)
  • Set--集合,即一个key不允许有重复的value
  • Sorted Set--排序集合,每一个key-value元素都关联一个浮点数,用来做元素的排序

redis的key

redis的key都是binary-safe的,即key即可以使用“foo”这种字符串,也可以使用JPEG文件内容流!
redis在key的使用上有一些约定,如下:
  • 不要使用太长的key。比如一个长度为1024bytes的key既浪费内存,又导致在key的查找时耗费较多的comparison操作
  • 不要使用太短的key。最好使用“user:1000:password”作为key,而不要使用“u:1000:pswd”;因为前者可读性更强,而且耗费的空间跟key、value对象本身相比可以忽略不计(当然后者还是能省那么一点点内存的)
  • 设计的key最好能够数据库的schema对应。比如使用“object-type:id:field”是个比较好的key的设计策略,像上面的“user:1000:password”就是一个很好的示例。redis官方推荐用逗号作为多词的分割,比如“comment:1234:reply.to”的中replyto

key-string类型

string类型是redis中最简单的key的类型。如果只用这个string类型作为key, 那这个redis跟memcached的使用就完全一样了,只是一个能持久化的memcached。
string类型的key可以用 GETSET命令,redis的简单体验可参考 这里(一个try-redis工具)。value类型的值可以是string(或者其他binary-safe数据格式),比如一个jpeg文件。 一个value最大不能超过512M
INCR命令可将string类型的value解析成为integer,然后自增1,执行INCR命令会返回自增之后的新值。跟INCR命令类似,还有 INCRBY(自增指定值)、 DESR(自减1)、 DECRBY(自减指定值)命令。其实这些命令在redis内部处理流程很类似,只是有少许的不同。
INCR命令是原子性的,即使多个客户端同时对同一个key做INCR操作,也不会导致任何冲突。
还有一个 GETSET命令,作用是SET一个新值,并且返回旧值。一个很好的应用场景就是:系统每隔一小时得到该小时内用户的访问次数,那么在临界时间可以执行GETSET key 0,即可得到上个小时的旧值,同时清0。

key-List类型

redis官方的这个文档提到,很多技术都没有很正确的使用List-链表,比如python中的List类型实际上是array(数组),而不是真正的链表。
List其实就是一个插入有序,并支持重复的element集合。但用array实现的list,和用linkedlist实现的list,在list的属性方面有本质区别。
redis使用linked list来实现的List类型。这就意味了在redis中,添加一个element的时间复杂度是 O(1)的,不会与element总数有关系。在List中添加新element可使用 LPUSHRPUSH命令。但linked list实现的List类型,随机读取element时就不如array那么快速了。
redis使用linked list来实现List类型,这样就可以做到在海量数据下插入新element的延迟非常低,另外一个优点就是redis的List类型可以在常量时间内被替换。

学习redis的List类型第一步

LPUSH命令可以向List的左侧(linked list的head方向)添加一个新元素, RPUSH命令向List的右侧(linked list的tail方向)添加一个新元素。 LRANGE命令可获得指定index范围的所有元素。
LRANGE命令使用两个index参数,分别是要返回的第一个和最后一个index。两个index都可以是负值,-1表示最后一个,-2表示倒数第二个。

redisList类型,内部排序是按照添加的先后顺序,所以可以用来实现消息队列,而这个是不需要Orderby来排序的,所以速度非常快;其还可以使用LRANGE命令来实现分页,非常方便。

例如在一个blog系统中,可以将某个arctilecomment放到一个List里,也是一个好的应用场景。

在List中存储ID而不是原始数据

redis官方不推荐在List中存储整个对象,因为一个对象很有可能多次被redis引用:比如在一个List中按照时间顺序保存对象,在一个Set中保存其属于某个category(分类),在另外一个List中保存着其是满足某些限制条件的。redis推荐的一个更好的方式如下:
$INCR next.news.id
(integer 1)
$SET news:1:title "redis is simple"
OK
$SET news:1:url "http://code.google.com/p/redis"
OK
$lpush submitted.news 1
OK
在上面的例子中,首先通过INCR命令简单得到新news的id,然后用这个ID来构建一个“news:1:”的key的前缀,然后用这个前缀为每个field建立一个<key, value>对象。然后将这个新news的id放入到submitted.news的List中。

可以通过阅读command reference来了解List的其他命令,诸如LPOPRPOPLREM(删除元素)、旋转List、读写指定index的元素,以及LLEN命令获取List的长度。

key-Set类型

redis中的Set类型对应的value是未排序的binary-safe的String。 SADD 命令完成向Set中添加一个element。另外还有一些命令可完成 检测一个元素是否存在、多Set间的交集、并集和差别等。例子如下:
$sadd myset 1
(integer) 1
$sadd myset 2
(integer) 1
$sadd myset 3
(integer) 1
$smembers myset
1. 3
2. 1
3. 2
可以看到Set是无序的,所以执行 SMEMBERS 返回的元素列表与插入顺序是无关的。
通过调用SISMEMBER命令来检测某元素是否存在,例子如下:
$sismember myset 3
(integer) 1
$sismember myset 30
(integer) 0
Redis的Set类型很适合用来表示对象之间的关系,比如可以用ta来实现article对应的 tags功能 。如果有一个ID是1000的article有tag1/2/5/77,就可以采用如下方式实现:
$ redis-cli sadd news:1000:tags 1
(integer) 1
$ redis-cli sadd news:1000:tags 2
(integer) 1
$ redis-cli sadd news:1000:tags 5
(integer) 1
$ redis-cli sadd news:1000:tags 77
(integer) 1
$ redis-cli sadd tag:1:objects 1000
(integer) 1
$ redis-cli sadd tag:2:objects 1000
(integer) 1
$ redis-cli sadd tag:5:objects 1000
(integer) 1
$ redis-cli sadd tag:77:objects 1000
(integer) 1
如果要获得指定对象的所有tag,就可以执行:
$ redis-cli smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2
Set还可以实现一些不常用的功能,比如要求获得同时具有tag1/2/10/27的所有对象,那么就可以使用SINTER 命令(即实现多Set的交集)来实现。
$ redis-cli sinter tag:1:objects tag:2:objects tag:10:objects tag:27:objects
... no result in our dataset composed of just one object ;) ...
可以通过阅读 command reference 来了解Set的其他命令,可以发现很多有趣的命令。比如SORT命令就可以对Set类型和List类型做排序操作!

题外话:如何获取String的唯一标识符

在上面的tags例子中,并没有提到tag的ID是如何生成的。每个被加入到系统中的tag,都需要一个唯一标识符;并且要做到多个Client同时添加tag不会出现ID的冲突;同时,如果一个tag的ID已存在,则直接返回;如果不存在,则返回新ID应该生成并与新tag绑定。
redis 1.4版本会加入hash类型,也就可以很方便的实现唯一标识符。在当前redis版本下实现方式如下:
  • 为了使key是binary-safe的,所以首先将tag的string值进行SHA1编码,例如:SHA1(tagname)=saljdkfjsaldjflksajdkflksadf
  • 检查对应此tagname对应tag的id是否存在,GET tag:saljdkfjsaldjflksajdkflksadf:id
  • 如果上一步获得id,则返回给用户;如果没有,则调用INCR next.tag.id来获得一个唯一的ID(比如返回123456)
  • 将最新的id与tag进行绑定,SETNX tag:saljdkfjsaldjflksajdkflksadf:id 123456。SETNX可以实现如果此key已经存在,则返回0;设置成功则返回1。这里千万不能使用SET命令
  • 如果SETNX命令返回1,则返回的123456就是一个新tag ID;否则调用GET tag:saljdkfjsaldjflksajdkflksadf:id 即可获得tag的新ID

key-Sorted Sets类型

Set类型非常方便,但由于Set完全无序,也导致了一些问题。所以redis 1.2版本引入了Sorted Set类型。ta类似于Set类型,但为每个vaue附加一个浮点型的score作为排序依据,ZRANGE 操作可返回排序的元素列表(与List的LRANGE 命令类似)。
redis的sorted set类型,有点类似与传统db的index。比如在sorted set中加入hacker,用各位hacker的出生年份作为score。
$ redis-cli zadd hackers 1940 "Alan Kay"
(integer) 1
$ redis-cli zadd hackers 1953 "Richard Stallman"
(integer) 1
$ redis-cli zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
$ redis-cli zadd hackers 1916 "Claude Shannon"
(integer) 1
$ redis-cli zadd hackers 1969 "Linus Torvalds"
(integer) 1
$ redis-cli zadd hackers 1912 "Alan Turing"
(integer) 1
redis使用了一个skip list和一个hash table来实现sorted set,每次的ZADD操作的时间复杂度都是O(log(N)) 。所以只要执行ZRANGE 命令即可获得已排序的元素列表。
$ redis-cli zrange hackers 0 -1
1. Alan Turing
2. Claude Shannon
3. Alan Kay
4. Richard Stallman
5. Yukihiro Matsumoto
6. Linus Torvalds
redis中有ZREVRANGE 命令 可以获得倒序排列的元素列表。
redis-cli zrevrange hackers 0 -1
1. Linus Torvalds
2. Yukihiro Matsumoto
3. Richard Stallman
4. Alan Kay
5. Claude Shannon
6. Alan Turing
另外一个获得倒序的方法,就是对sorted set调用 SORT 命令。另外一个获得不同排序的方法是同时将每个element添加到多个sorted set中

sorted set对range的操作

redis的sorted set类型支持对range的操作。比如在上例中,获取在1950年及之前出生的元素列表,可以使用ZRANGEBYSCORE 命令。
$ redis-cli zrangebyscore hackers -inf 1950
1. Alan Turing
2. Claude Shannon
3. Alan Kay
在上例中,redis是返回了负无穷到1950之间的score的元素(包含两个边界)。
redis支持ZREMRANGEBYSCORE 命令,来实现删除在某个range之间的所有元素。
$ redis-cli zremrangebyscore hackers 1940 1960
(integer) 2
在上例中,删除了score在1940和1960之间的所有元素,返回的是删除元素的个数。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值