Redis不是一个简单的键值对存储,它其实是一个支持各类类型数据结构的存储。在传统的键值存储中,是将字符串键关联到字符串值,可是在Redis中,这些值不只限于简单的字符串,还能够支持更复杂的数据结构。下面就是Redis支持的数据结构:redis
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。算法
键
是二进制安全的,这意味着您可使用任何二进制序列做为键,能够是OneMoreStudy
这样的字符串,也可使图片文件的内容,空字符串也是有效的键
。不过,还有一些其余规则:数组
键
,好比一个1KB的键。不只是多占内存方面的问题,而是在数据集中查找键
可能须要进行一些耗时的键
比较。若是真的有比较大的键
,先对它进行哈希(好比:MD5
、SHA1
)是一个好主意。键
,好比:OMS100f
,相对于one-more-study:100:fans
,后者更具备可读性。可能会占用更多内存,可是相对于值所占的内存,键
所增长的内存仍是小不少的。咱们要找到一个平衡点,不长也不短。one-more-study:100:fans
,或者one.more.study:100:fans
。键
容许的最大值为512MB。欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。缓存
字符串类型是和键
关联的最简单的类型。它是Memcached中惟一的数据类型,所以对于新手来讲,在Redis中使用它也是很容易的。键
是字符串类型,当咱们也使用字符串类型做为值时,咱们会能够从一个字符串映射到另外一个字符串。 字符串数据类型有不少应用场景,例如缓存HTML片断或页面。安全
下面简单介绍一下字符串的命令(在redis-cli中使用):服务器
> set one-more-key OneMoreStudy OK > get one-more-key "OneMoreStudy"
使用SET
和GET
命令来设置和查询字符串值的方式。须要注意的是,若是当前键
已经和字符串值相关联,SET
命令将会替换已存储在键
中的现有值。字符串能够是任意的二进制数据,好比jpeg图像。字符串最多不能大于512MB。SET
命令还有一些实用的可选参数,好比:微信
> set one-more-key Java nx #若是key存在,则设置失败。 (nil) > set one-more-key Java xx #若是key存在,才设置成功。 OK
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。数据结构
虽然字符串是Redis的基本值,但也可使用它们执行一些实用的操做。 好比:app
> set one-more-counter 50 OK > incr one-more-counter #自增长1 (integer) 51 > incr one-more-counter #自增长1 (integer) 52 > incrby one-more-counter 5 #自增长5 (integer) 57
INCR
命令将字符串值解析为整数,将其自增长1,最后将得到的值设置为新值。 还有其余相似的命令,例如INCRBY
,DECR
和DECRBY
等命令。 INCR
命令是原子操做,即时有多个客户端同时同一个key的INCR
命令,也不会进入竞态条件。好比,上面的例子先设置one-more-counter
的值为50,即便两个客户端同时发出INCR命令,那么最后的值也确定是52。性能
可使用MSET
和MGET
命令在单个命令中设置或查询多个键
的值,对于减小延迟也颇有用。好比:
> mset a 1 b 2 c 3 OK > mget a b c 1) "1" 2) "2" 3) "3"
使用MGET
命令时,Redis返回一个值的数组。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
使用DEL命令能够删除键
和相关联的值,存在指定的键
则返回1,不存在指定的键
则返回0。使用EXISTS
命令判断Redis中是否存在指定的键
,存在指定的键
则返回1,不存在指定的键
则返回0。好比:
> set one-more-key OneMoreStudy OK > exists one-more-key (integer) 1 > del one-more-key (integer) 1 > exists one-more-key (integer) 0
使用TYPE
命令,能够返回存储在指定key的值的数据类型,好比:
> set one-more-key OneMoreStudy OK > type one-more-key string > del one-more-key (integer) 1 > type one-more-key none
在讨论更复杂的数据结构以前,咱们须要讨论另外一个功能,该功能不管值类型是什么都适用,它就是EXPIRE
命令。 它能够为键
设置到期时间,当超过这个到期时间后,该键
将自动销毁,就像对这个键
调用了DEL
命令同样。好比:
> set one-more-key OneMoreStudy OK > expire one-more-key 5 (integer) 1 > get one-more-key #马上调用 "OneMoreStudy" > get one-more-key #5秒钟后调用 (nil)
上面的例子,适用了EXPIRE
命令设置了过时时间,也可使用PERSIST
命令移除键
的过时时间,这个键
将持久保持。除了EXPIRE
命令,还可使用SET命令设置过时时间,好比:
> set one-more-key OneMoreStudy ex 10 #设置过时时间为10秒 OK > ttl one-more-key (integer) 9
上面的例子,设置了一个字符串值OneMoreStudy
的one-more-key
,该键
的到期时间为10秒。以后,调用TTL
命令以检查该键
的剩余生存时间。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
到期时间可使用秒或毫秒精度进行设置,但到期时间的分辨率始终为1毫秒。实际上,Redis服务器上存储的不是到期时间长度,而是该键
到期的时间。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
Redis列表是使用链表实现的,这就意味着在头部或尾部增长或删除一个的元素的时间复杂度是O(1),很是快的。不过,按索引查询对应元素的时间复杂度就是O(n),慢不少。若是想快速查询大量数据,可使用有序集合,后面会有介绍。
LPUSH
命令将一个新元素添加到列表的左侧(顶部),而RPUSH
命令将一个新元素添加到列表的右侧(底部)。最后,LRANGE
命令能够从列表中按范围提取元素。好比:
> rpush one-more-list A (integer) 1 > rpush one-more-list B (integer) 2 > lpush one-more-list first (integer) 3 > lrange one-more-list 0 -1 1) "first" 2) "A" 3) "B"
LRANGE
命令须要另外两个参数,要返回的第一个元素的索引和最后一个元素的索引。若是索引为负值,Redis将从末尾开始计数,-1是列表的最后一个元素,-2是列表的倒数第二个元素,依此类推。
LPUSH
和RPUSH
命令支持多个参数,可使用一次命令添加多个元素,好比:
> rpush one-more-list 1 2 3 4 5 "last" (integer) 9 > lrange one-more-list 0 -1 1) "first" 2) "A" 3) "B" 4) "1" 5) "2" 6) "3" 7) "4" 8) "5" 9) "last"
在Redis列表上,也能够移除并返回元素。 与LPUSH
和RPUSH
命令,对应的就是LPOP
和RPOP
命令,LPOP
命令是将列表的左侧(顶部)的元素移除并返回,RPOP
命令是将列表的右侧(底部)的元素移除并返回。好比:
> rpush one-more-list a b c (integer) 3 > rpop one-more-list "c" > rpop one-more-list "b" > rpop one-more-list "a"
咱们添加了三个元素,并移除并返回了三个元素,此时列表为空,没有任何元素。若是再使用RPOP
命令,会返回一个NULL
值:
> rpop one-more-list (nil)
使用RPUSH
和RPOP
命令,或者LPUSH
和LPOP
命令能够实现栈的功能,使用LPUSH
和RPOP
命令,或者RPUSH
和LPOP
命令能够实现队列的功能。也能够实现生产者和消费者模式,好比多个生产者使用LPUSH
命令将任务添加到列表中,多个消费者使用RPOP
命令将任务从列表中取出。可是,有时列表可能为空,没有任何要处理的任务,所以RPOP
命令仅返回NULL
。在这种状况下,消费者被迫等待一段时间,而后使用RPOP
命令重试。这就暴露了有几个缺点:
NULL
。NULL
以后会等待一段时间,所以会增长任务处理的延迟。为了减少延迟,咱们能够在两次调用RPOP
之间等待更少的时间,这就扩大了更多对Redis的无用调用。有什么办法能够解决呢?使用BRPOP
和BLPOP
的命令,它们和RPOP
和LPOP
命令相似,惟一的区别是:若是列表为空时,命令会被阻塞,直到有新元素添加到列表中,或指定的超时时间到了时,它们才会返回到调用方。好比:
> brpop tasks 5
它含义是,列表为空时,等待列表中的元素,但若是5秒钟后没有新的元素被添加,则返回。您能够将超时时间传入0,表示永远等待元素添加。也能够传入多个列表,这时会按参数前后顺序依次检查各个列表,返回第一个非空列表的尾部元素。另外还有如下3点须要注意的:
RPOP
命令相比有所不一样,它是一个包含两个元素的数组,包含key和对应的元素,由于BRPOP
和BLPOP
命令可以阻止等待来自多个列表的元素。NULL
。欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
列表的建立和删除都是由Redis自动完成的,当尝试向不存在的键
添加元素时,Redis会自动建立一个空的列表;当最后一个元素被移除时,Redis会自动删除这个列表。这不是特定于列表的,它适用于由多个元素组成的全部Redis数据类型,好比集合、有序集合、哈希,它们都有3条规则:
键
不存在,则在添加元素以前会建立一个空的聚合数据类型。好比:> del one-more-list (integer) 1 > lpush one-more-list 1 2 3 (integer) 3
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
可是,在键
存在时,就不能操做错误的数据类型了,好比:
> set one-more-key OneMoreStudy OK > lpush one-more-key 1 2 3 (error) WRONGTYPE Operation against a key holding the wrong kind of value > type one-more-key string
> lpush one-more-list 1 2 3 (integer) 3 > exists one-more-list (integer) 1 > lpop one-more-list "3" > lpop one-more-list "2" > lpop one-more-list "1" > exists one-more-list (integer) 0
LLEN
命令,获取列表长度)或写命令(如LPOP
命令)时,都会返回空聚合数据类型的结果。好比:> del one-more-list (integer) 0 > llen one-more-list (integer) 0 > lpop one-more-list (nil)
Redis为了追求高性能,列表的内部实现不是一个简单的链表,这里先卖个关子,后续的文章会详细介绍。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
集合是一个字符串的无序集合,SADD
命令能够将新元素添加到集合中。还能够对集合进行许多其余操做,例如:判断给定元素是否已存在、执行多个集合之间的交集、并集或差等等。好比:
> sadd one-more-set 1 2 3 (integer) 3 > smembers one-more-set 1) "1" 2) "3" 3) "2"
在上面的例子中,在集合中添加了三个元素,并让Redis返回全部元素。 正如你所见,返回的元素是没有排序的。在每次调用时,元素的顺序都有可能不同。
还可使用SISMEMBER
命令判断给定元素是否已存在,好比:
> sismember one-more-set 3 (integer) 1 > sismember one-more-set 30 (integer) 0
在上面的例子中,3在集合中,因此返回1;而30不在集合中,因此返回0。
可使用SINTER
命令,计算出多个集合的交集;使用SUNION
命令,计算多个集合的并集;使用SPOP
命令,移除并返回集合中的一个随机元素;使用SCARD
命令,计算集合中的元素的数量。好比:
> sadd one-more-set1 1 2 3 (integer) 3 > sadd one-more-set2 2 3 4 (integer) 3 > sinter one-more-set1 one-more-set2 #交集 1) "3" 2) "2" > sunion one-more-set1 one-more-set2 #并集 1) "1" 2) "3" 3) "2" 4) "4" > spop one-more-set1 #随机移除一个元素 "3" > scard one-more-set1 #元素数量 (integer) 2
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
有序集合是一种相似于集合和哈希之间混合的数据类型。像集合同样,有序集合中由惟一的、非重复的字符串元素组成,所以从某种意义上说,有序集合也是一个集合。可是集合中的元素是没有排序的,而有序集合中的每一个元素都与一个称为分数
(score)的浮点值相关联,这就是为何有序集合也相似于哈希的缘由,由于每一个元素都映射到一个值。有序集合的排序规则以下:
咱们来举个例子,把王者荣耀战队的名字和积分添加到有序集合中,其中把战队的名字做为值,把战队的积分做为分数。
> zadd kpl 12 "eStarPro" (integer) 1 > zadd kpl 12 "QGhappy" (integer) 1 > zadd kpl 10 "XQ" (integer) 1 > zadd kpl 8 "EDG.M" (integer) 1 > zadd kpl 8 "RNG.M" (integer) 1 > zadd kpl 4 "TES" (integer) 1 > zadd kpl 2 "VG" (integer) 1
如上所示,ZADD
命令和SADD
命令类似,可是多了一个额外的参数(在要添加的元素的前面)做为分数。ZADD
命令也支持多个参数,虽然在上面的例子中未使用它,但你也能够指定多个分数和值对。使用有序集合,快速地返回按其积分排序的战队列表,由于实际上它们已经被排序了。
须要注意的是,为了快速获取有序集合中的元素,每次添加元素的时间复杂度都为O(log(N)),这是由于有序集合是同时使用跳跃表和字典来实现的,具体原理这里先卖个关子,后续的文章会详细介绍。
可使用ZRANGE
命令按照升序获取对应的值,好比:
> zrange kpl 0 -1 1) "VG" 2) "TES" 3) "EDG.M" 4) "RNG.M" 5) "XQ" 6) "QGhappy" 7) "eStarPro"
0和-1表明查询从第一个到最后一个的元素。还可使用ZREVRANGE
命令按照降序获取对应的值,好比:
> zrevrange kpl 0 -1 1) "eStarPro" 2) "QGhappy" 3) "XQ" 4) "RNG.M" 5) "EDG.M" 6) "TES" 7) "VG"
加上WITHSCORES
参数,就能够连同分数一块儿返回,好比:
> zrange kpl 0 -1 withscores 1) "VG" 2) "2" 3) "TES" 4) "4" 5) "EDG.M" 6) "8" 7) "RNG.M" 8) "8" 9) "XQ" 10) "10" 11) "QGhappy" 12) "12" 13) "eStarPro" 14) "12"
有序集合还有更强大的功能,好比在分数范围内操做,让咱们获取小于10(含)的战队,使用ZRANGEBYSCORE
命令:
> zrangebyscore kpl -inf 10 1) "VG" 2) "TES" 3) "EDG.M" 4) "RNG.M" 5) "XQ"
这就是获取分数从负无穷到10所对应的值,一样的咱们也能够获取分数从4到10所对应的值:
> zrangebyscore kpl 4 10 1) "TES" 2) "EDG.M" 3) "RNG.M" 4) "XQ"
另外有用的命令:ZRANK
命令,它能够返回指定值的升序排名(从0开始);ZREVRANK
命令,它能够返回指定值的降序排名(从0开始),好比:
> zrank kpl "EDG.M" (integer) 2 > zrevrank kpl "EDG.M" (integer) 4
有序集合的分数是随时更新的,只要对已有的有序集合调用ZADD
命令,就会以O(log(N))时间复杂度更新其分数和排序。这样,当有大量更新时,有序集合是合适的。因为这种特性,常见的场景是排行榜,能够方便地显示排名前N位的用户和用户在排行榜中的排名。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
Redis的哈希和人们指望的“哈希”结构是同样的,它是一个无序哈希,内部存储了不少键值对,好比:
> hmset one-more-fans:100 name Lily age 25 OK > hget one-more-fans:100 name "Lily" > hget one-more-fans:100 age "25" > hgetall one-more-fans:100 1) "name" 2) "Lily" 3) "age" 4) "25"
尽管哈希很容易用来表示对象,可是实际上能够放入哈希中的字段数是没有实际限制的,所以您能够以更多种的不一样方式使用哈希。除了HGET
命令获取单个字段对应的值,也可使用HMSET
命令获取多个字段及对应的值,它返回的是一个数组,好比:
> hmget one-more-fans:100 name age non-existent-field 1) "Lily" 2) "25" 3) (nil)
还可使用HINCRBY
命令,为指定字段的值作增量,好比:
> hget one-more-fans:100 age "25" > hincrby one-more-fans:100 age 3 (integer) 28 > hget one-more-fans:100 age "28"
Redis哈希的实现结构,和Java中的HashMap是同样的,也是“数组+链表”的结构,当发生数组位置碰撞是,就会将碰撞的元素用链表串起来。不过Redis为了追求高性能,rehash的方式不太同样,这里先卖个关子,后续的文章会详细介绍。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
位图不是实际的数据类型,而是在String类型上定义的一组面向位的操做。 因为字符串是二进制安全的,而且最大长度为512MB,所以能够设置多达2^32个不一样的位。位图操做分为两类:固定单个位操做,好比将一个位设置为1或0或获取其值;对位组的操做,好比计算给定位范围内设置位的数量。
位图的最大优势之一是,它们在存储信息时一般能够节省大量空间。例如,在以增量用户ID位标识表示用户是否要接收新闻通信,仅使用512 MB内存就能够记住40亿用户的一位信息。
使用SETBIT
和GETBIT
命令来设置和获取指定位,好比:
> setbit one-more-key 10 1 (integer) 0 > getbit one-more-key 10 (integer) 1 > getbit one-more-key 11 (integer) 0
SETBIT
命令将位号做为其第一个参数,将其设置为1或0的值做为其第二个参数。若是位号超出当前字符串长度,该命令将会自动扩大字符串。GETBIT
命令只是返回指定位号的位的值,若是位号超出存储的字符串长度则会返回0。
对位组的操做有如下3个命令:
BITOP
命令能够在不一样的字符串之间执行按位运算,提供的位运算有与、或、非和异或。BITCOUNT
命令能够统计指定范围内位数为1的个数。BITPOS
命令能够查找指定范围内为0或1的第一位。> set one-more-key "\x13\x7f" #二进制为0001 0011 0111 1111 OK > bitcount one-more-key #整个字符串中1的位数 (integer) 10 > bitcount one-more-key 0 0 #第一个字符(0001 0011)中1的位数 (integer) 3 > bitcount one-more-key 1 1 #第二个字符(0111 1111)中1的位数 (integer) 7 > bitpos one-more-key 0 #整个字符串中第一个0位 (integer) 0 > bitpos one-more-key 1 #整个字符串中第一个1位 (integer) 3 > bitpos one-more-key 1 0 0 #第一个字符(0001 0011)中第一个1位 (integer) 3 > bitpos one-more-key 1 1 1 #第二个字符(0111 1111)中第一个1位 (integer) 9
位图能够应用于各种实时分析,也能够节省空间高效地存储位信息。好比,记录用户天天的签到数据,每个位表示用户是否签到过,这样就能够计算出某个时间段用户签到了几回,某个时间段用户第一次签到是哪一天。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
HyperLogLog是一种几率数据结构,用于统计惟一元素的数量,也能够理解为估计集合中元素的个数。
一般状况下,对惟一元素进行统计数量时,须要使用与要统计的元素数量成比例的内存量,由于须要记住过去已经看到的元素,以免屡次对其进行统计。可是,有一组算法能够之内存换取精度,最终会获得带有标准偏差的估计数量,在Redis的HyperLogLogs中,该偏差小于1%。
这个算法的神奇之处在于,再也不须要使用与所统计元素数量成比例的内存量,而可使用恒定数量的内存。在最坏的状况下占据12KB的内存空间,Redis对HyperLogLog的存储进行了优化,在计数比较少时,占据的内存空间会更小,这里先卖个关子,后续的文章会详细介绍其中原理。
在集合中,能够将每一个元素添加到集合中,并使用SCARD
命令获取集合中的元素数量,由于SADD
命令不会从新添加现有元素,因此元素都是惟一的。HyperLogLog的操做和集合比较相似,使用PFADD
命令将元素添加到HyperLogLog中,相似于集合的SADD
命令;使用PFCOUNT
命令获取HyperLogLog中的惟一元素的当前近似值数量,相似于集合的SCARD
命令。好比:
> pfadd one-more-hll a b c d e (integer) 1 > pfcount one-more-hll (integer) 5
Redis中的HyperLogLog尽管在技术上是不一样的数据结构,但被编码为字符串,所以能够调用GET
命令来序列化HyperLogLog,而后调用SET
命令来将其反序列化回服务器。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。
Redis提供更加丰富的数据结构,键(Key)和字符串(String),都是二进制安全的字符串;列表(List),根据插入顺序排序的字符串元素列表,基于链表实现;集合(Set),惟一的乱序的字符串元素的集合;有序集合(Sorted Set),与集合相似,可是每一个字符串元素都与一个称为score的数字相关联;哈希(Hash),由字段与值相关联组成的映射,字段和值都是字符串;位图(Bitmap),像操做位数组同样操做字符串值,能够设置和清除某个位,对全部为1的位进行计数,找到第一个设置1的位,找到第一个设置0的位等等;HyperLogLogs,一种几率数据结构,使用较小的内存空间来统计惟一元素的数量,偏差小于1%。
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货。