一、概述
Redis有5个基本数据结构,string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构,把这5个数据结构都吃透了,你就掌握了Redis应用知识的一半了。在Redis 5.0版本又新增了一个stream数据类型,另起文章介绍。
掌握数据类型是怎么工作的并不是非常简单,从command reference获取解决问题的命令用法,所以这篇文章是介绍Redis数据类型的速成班和它们最常见的模式。下面所有的例子我们将使用redis-cli客户端演示。
Redis命令介绍中文网:http://doc.redisfans.com/index.html
二、Key(键)
Redis是一个开源的使用ANSI C语言编写的key-value数据库,我们可能会较为主观的认为Redis中的字符串就是采用了C语言中的传统字符串表示,但其实不然,Redis没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,可变的字节数组;并将SDS用作Redis 的默认字符串表示:
1 2 |
redis> SET msg "hello world" OK |
设置一个key = msg,value = hello world的新键值对,他们底层是数据结构将会是:
- 键(key)是一个字符串对象,对象的底层实现是一个保存着字符串“msg” 的SDS;
- 值(value)也是一个字符串对象,对象的底层实现是一个保存着字符串“hello world” 的SDS;
SDS的定义
Redis中定义动态字符串的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * 保存字符串对象的结构 */ struct sdshdr { // buf 中已占用空间的长度 int len; // buf 中剩余可用空间的长度 int free; // 数据空间 char buf[]; }; |
用图描述如下:
len变量,用于记录 buf 中已经使用的空间长度(这里指出Redis 的长度为5)。
free变量,用于记录 buf 中还空余的空间(初次分配空间,一般没有空余,在对字符串修改的时候,会有剩余空间出现)。
buf字符数组,用于记录我们的字符串(记录Redis)。
二进制安全
Redis keys是二进制安全的,这意味着你可以使用任意的二进制序列作为key,从像‘foo’这样的字符串到JPEG文件的内容。空字符串也是有效的key。
在C字符串中的字符必须符合某种编码,并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存想图片,音频,视频,压缩文件这样的二进制数据。
但是在Redis中,不是靠空字符来判断字符串的结束的,而是通过len这个属性。那么,即便是中间出现了空字符对于SDS来说,读取该字符仍然是可以的。
关于key的一些规则
1. 非常长的key并不好,例如一个1024字节的key,不仅内存不合理,而且因为数据中查找key也是非常昂贵的。即使手头的任务有很大的值要匹配,最好是去哈希它,特别是从内存和带宽的角度。
2. 非常短的key一般也不好。有一个小点‘u1000flw’作为一个key,不如写成‘user:1000:followers’。后者更具有可读性,前者更省空间。这要在这中间做一个权衡。
3. 试着坚持使用schema,例如”object-type:id“,就行像”user:1000“。点或破折号经常用于多词字段,像”comment:1234:reply.to“或”comment:1234:reply-to“。
4. key最大允许的长度是512MB。
对于Key(字符串)操作的其他常用命令:
KEYS
语法:KEYS pattern
查找所有符合给定模式的key。
1 2 3 4 |
KEYS * #匹配数据库中所有key; KEYS h?llo #匹配hello,hallo和hxllo等; KEYS h*llo #匹配hllo和heeeeello等; KEYS h[ae]llo #匹配hello和hallo,但不匹配hillo; |
DEL
语法:DEL key [key …]
删除给定的一个或多个key,不存在的key会被忽略。
1 2 3 4 5 6 |
127.0.0.1:6379> SET name ywnds OK 127.0.0.1:6379> DEL name (integer) 1 127.0.0.1:6379> DEL name (integer) 0 |
MOVE
语法:MOVE key db
将当前数据库的key移动到给定的数据库db(redis默认使用数据库0)当中,如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定key,或者key不存在于当前数据库,那么MOVE没有任何效果。因此,也可以利用这一特性,将MOVE当作锁(locking)原语(primitive)。
1 2 3 4 5 6 7 8 9 10 11 12 |
127.0.0.1:6380> SELECT 0 #redis默认使用数据库0,为了清晰起见,这里再显式指定一次; OK 127.0.0.1:6380> SET song "secret base - Zone" OK 127.0.0.1:6380> MOVE song 1 #将song移动到数据库1; (integer) 1 127.0.0.1:6380> EXISTS song #song已经被移走; (integer) 0 127.0.0.1:6380> SELECT 1 #使用数据库1; OK 127.0.0.1:6380> EXISTS song #证实song被移到了数据库1 (注意命令提示符变成了"redis:1",表明正在使用数据库1); (integer) 1 |
EXPIRE
语法:EXPIRE key seconds
为给定key设置生存时间,当key过期时(生存时间为0 ),它会被自动删除。
EXPIREAT
语法:EXPIREAT key timestamp
EXPIREAT的作用和EXPIRE类似,都用于为key设置生存时间。不同在于EXPIREAT命令接受的时间参数是UNIX时间戳(unix timestamp)。
PERSIST
语法:PERSIST key
移除给定key的生存时间,将这个key从『易失的』(带生存时间key )转换成『持久的』(一个不带生存时间、永不过期的key )。
PEXPIRE
语法:PEXPIRE key milliseconds
这个命令和EXPIRE命令的作用类似,但是它以毫秒为单位设置key的生存时间,而不像EXPIRE命令那样,以秒为单位。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> SET mykey "Hello" OK 127.0.0.1:6379> PEXPIRE mykey 5000 (integer) 1 127.0.0.1:6379> TTL mykey #TTL的返回值以秒为单位; (integer) 3 127.0.0.1:6379> PTTL mykey #PTTL可以给出准确的毫秒数; (integer) 1466 |
PEXPIREAT
语法:PEXPIREAT key milliseconds-timestamp
这个命令和EXPIREAT命令类似,但它以毫秒为单位设置key的过期unix时间戳,而不是像EXPIREAT那样,以秒为单位。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> SET mykey "Hello" OK 127.0.0.1:6379> PEXPIREAT mykey 1555555555005 (integer) 1 127.0.0.1:6379> TTL mykey (integer) 67293991 127.0.0.1:6379> PTTL mykey (integer) 67293985041 |
TTL
语法:TTL key
检查key的存活时间,永久存活为-1。
Redis key有效期是指定key的存活时间,它限制key的存活时间。当指定是时间过后,key就会自动销毁,就好像用户调用DEL命令一样。设置一个有效期非常简单:
1 2 3 4 5 6 7 8 |
127.0.0.1:6380> set key some-value OK 127.0.0.1:6380> expire key 5 (integer) 1 127.0.0.1:6380> get key (immediately) "some-value" 127.0.0.1:6380> get key (after some time) (nil) |
key在两个Get之间消失了,从第二次调用已经延迟超过了5秒钟。在上面的实例中,我们使用了EXPIRE为了设置超时时间(也可以设置不同的超时时间在存在的key上,像PERSIST可用于移除超时时间并永远的持久化key)。然而我们也可以使用其他的命令设置有效期,例如使用SET选项:
1 2 3 4 |
127.0.0.1:6380> set key 100 ex 10 OK 127.0.0.1:6380> ttl key (integer) 9 |
上面的例子设置了一个value=100的字符串,有一个10秒的超时时间。然后TTL命令被调用,用于检查key剩下的存活时间。
生存时间可以通过使用DEL命令来删除整个key来移除,或者被SET和GETSET命令覆写(overwrite),这意味着,如果一个命令只是修改(alter)一个带生存时间的key的值而不是用一个新的key值来代替(replace)它的话,那么生存时间不会被改变。
比如说,对一个key执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改key本身的生存时间。
另一方面,如果使用RENAME对一个key进行改名,那么改名后的key的生存时间和改名前一样。RENAME命令的另一种可能是,尝试将一个带生存时间的key改名成另一个带生存时间的another_key ,这时旧的another_key (以及它的生存时间)会被删除,然后旧的key会改名为another_key,因此,新的another_key的生存时间也和原本的key 一样。
使用PERSIST命令可以在不删除key的情况下,移除key的生存时间,让key重新成为一个『持久的』(persistent) key 。
PTTL
语法:PTTL key
这个命令类似于TTL命令,但它以毫秒为单位返回key的剩余生存时间,而不是像TTL命令那样,以秒为单位。
TYPE
语法:TYPE key
返回key所储存的值的类型,返回值如下:
none (key不存在)
string (字符串)
list (列表)
set (集合
zset (有序集)
hash (哈希表)
1 2 |
127.0.0.1:6379> type mykey string |
RENAME
语法:RENAME key newkey
将key改名为newkey,当key和newkey相同,或者key不存在时,返回一个错误。当newkey已经存在时,RENAME命令将覆盖旧值。可以用来防止隐藏某些命令。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> SET message "hello world" OK 127.0.0.1:6379> RENAME message greeting OK 127.0.0.1:6379> EXISTS message #message不复存在; (integer) 0 127.0.0.1:6379> EXISTS greeting #greeting取而代之; (integer) 1 |
当key不存在时,返回错误。
1 2 |
127.0.0.1:6379> RENAME fake_key never_exists (error) ERR no such key |
newke已存在时,RENAME会覆盖旧newkey。
1 2 3 4 5 6 7 8 9 10 |
127.0.0.1:6379> SET pc "lenovo" OK 127.0.0.1:6379> SET personal_computer "dell" OK 127.0.0.1:6379> RENAME pc personal_computer OK 127.0.0.1:6379> GET pc (nil) 127.0.0.1:6379> GET personal_computer "lenovo" |
RENAMENX
语法:RENAMENX key newkey
仅当newkey不存在时,将key改名为newkey,当key不存在时,返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# newkey不存在,改名成功; 127.0.0.1:6379> SET player "MPlyaer" OK 127.0.0.1:6379> EXISTS best_player (integer) 0 127.0.0.1:6379> RENAMENX player best_player (integer) 1 # newkey存在时,失败; 127.0.0.1:6379> SET animal "bear" OK 127.0.0.1:6379> SET favorite_animal "butterfly" OK 127.0.0.1:6379> RENAMENX animal favorite_animal (integer) 0 127.0.0.1:6379> get animal "bear" 127.0.0.1:6379> get favorite_animal "butterfly" |
SORT
语法:SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination]
返回或保存给定列表、集合、有序集合key中经过排序的元素。排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。
最简单的SORT使用方法是SORT key(返回键值从小到大排序的结果)和SORT key DESC(返回键值从大到小排序的结果)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
127.0.0.1:6379> LPUSH today_cost 30 1.5 10 8 (integer) 4 # 升序; 127.0.0.1:6379> SORT today_cost 1) "1.5" 2) "8" 3) "10" 4) "30" #降序; 127.0.0.1:6379> SORT today_cost DESC 1) "30" 2) "10" 3) "8" 4) "1.5" |
DUMP
语法:DUMP key
序列化给定key,并返回被序列化的值,使用RESTORE命令可以将这个值反序列化为Redis键。
序列化生成的值有以下几个特点:
1)它带有64位的校验和,用于检测错误, RESTORE在进行反序列化之前会先检查校验和。
2)值的编码格式和RDB文件保持一致。
3)RDB版本会被编码在序列化值当中,如果因为Redis的版本不同造成RDB格式不兼容,那么Redis会拒绝对这个值进行反序列化操作。
序列化的值不包括任何生存时间信息。
1 2 3 4 |
127.0.0.1:6379> SET greeting "hello, dumping world!" OK 127.0.0.1:6379> DUMP greeting "\x00\x15hello, dumping world!\a\x00,\x7f\xe7\xf1%\xed(W" |
RESTORE
语法:RESTORE key ttl serialized-value
反序列化给定的序列化值,并将它和给定的key关联。
参数ttl以毫秒为单位为key设置生存时间;如果ttl为0,那么不设置生存时间。
RESTORE在执行反序列化之前会先对序列化值的RDB版本和数据校验和进行检查,如果RDB版本不相同或者数据不完整的话,那么RESTORE会拒绝进行反序列化,并返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 |
127.0.0.1:6379> SET greeting "hello, dumping world!" OK 127.0.0.1:6379> DUMP greeting "\x00\x15hello, dumping world!\a\x00,\x7f\xe7\xf1%\xed(W" 127.0.0.1:6379> RESTORE greeting-again 0 "\x00\x15hello, dumping world!\a\x00,\x7f\xe7\xf1%\xed(W" OK 127.0.0.1:6379> GET greeting-again "hello, dumping world!" 127.0.0.1:6379> 127.0.0.1:6379> RESTORE fake-message 0 "hello moto moto blah blah" (error) ERR DUMP payload version or checksum are wrong |
SCAN
语法:SCAN cursor [MATCH pattern] [COUNT count]
SCAN命令及其相关的SSCAN命令、HSCAN命令和ZSCAN命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
SCAN命令用于迭代当前数据库中的数据库键。
SSCAN命令用于迭代集合键中的元素。
HSCAN命令用于迭代哈希键中的键值对。
ZSCAN命令用于迭代有序集合中的元素(包括元素成员和元素分值)。
以上列出的四个命令都支持增量式迭代, 它们每次执行都只会返回少量元素,所以这些命令可以用于生产环境, 而不会出现像KEYS命令、SMEMBERS命令带来的问题 —— 当KEYS命令被用于处理一个大的数据库时, 又或者SMEMBERS命令被用于处理一个大的集合键时, 它们可能会阻塞服务器达数秒之久。
不过,增量式迭代命令也不是没有缺点的: 举个例子,使用SMEMBERS命令可以返回集合键当前包含的所有元素, 但是对于SCAN这类增量式迭代命令来说,因为在对键进行增量式迭代的过程中,键可能会被修改,所以增量式迭代命令只能对被返回的元素提供有限的保证 (offer limited guarantees about the returned elements)。
因为SCAN 、SSCAN 、HSCAN和ZSCAN四个命令的工作方式都非常相似, 所以这个文档会一并介绍这四个命令, 但是要记住:SSCAN命令、HSCAN命令和ZSCAN命令的第一个参数总是一个数据库键。而SCAN命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。
SCAN命令的基本用法
SCAN命令是一个基于游标的迭代器(cursor based iterator): SCAN命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为SCAN命令的游标参数,以此来延续之前的迭代过程。
当SCAN命令的游标参数被设置为0时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为0的游标时, 表示迭代已结束。
以下是一个SCAN命令的迭代过程示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
127.0.0.1:6379> scan 0 1) "1" 2) 1) "key2" 2) "key4" 3) "key8" 4) "key6" 5) "key7" 6) "key5" 7) "personal_computer" 8) "favorite_animal" 9) "today_cost" 10) "key1" 11) "best_player" 127.0.0.1:6379> scan 1 1) "0" 2) 1) "greeting-again" 2) "greeting" 3) "animal" 4) "key9" 5) "key3" 6) "mykey" |
在上面这个例子中,第一次迭代使用0作为游标, 表示开始一次新的迭代。第二次迭代使用的是第一次迭代时返回的游标,也即是命令回复第一个元素的值 —— 1。
从上面的示例可以看到, SCAN命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。
在第二次调用SCAN命令时, 命令返回了游标0 , 这表示迭代已经结束,整个数据集(collection)已经被完整遍历过了。以0作为游标开始一次新的迭代,一直调用SCAN命令, 直到命令返回游标0,我们称这个过程为一次完整遍历(full iteration)。
三、String(字符串)
Redis字符串类型是可以用键关联的最简单的值类型,它是Memcached的唯一数据类型,所以它是新来者Redis很自然的支持类型。由于Redis的键是字符串,当我们也用字符串作为值的时候,我们从一个字符串映射另一个字符串。
Redis字符串表示的是一个可变的字节数组,也被称之为简单动态字符串(simple dynamic string)SDS,我们初始化字符串的内容、可以拿到字符串的长度,可以获取string的字串,可以覆盖string的字串内容,可以追加子串。
Redis的字符串是动态字符串,是可以修改的字符串,内部结构实现上采用预分配冗余空间的方式来减少内存的频繁分配,如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。
让我们测试一下字符串类型,初始化字符串需要提供「变量名称」和「变量的内容」:
1 2 3 4 5 6 |
127.0.0.1:6379> set mykey somevalue OK 127.0.0.1:6379> get mykey "somevalue" 127.0.0.1:6379> del mykey (integer) 1 |
就像你看到的一样我们使用SET和GET可以设置和检索字符串值。
SET
语法:SET key value [EX seconds] [PX milliseconds] [NX|XX]
将字符串值value关联到key,如果key已经持有其他值,SET就覆写旧值,无视类型。对于某个原本带有生存时间(TTL)的键来说, 当SET命令成功在这个键上执行时,这个键原有的TTL将被清除。值可以是各式各样的字符串,例如你可以存储一个jpeg图像进一个key,但一个值最大不能超过512MB。
从Redis 2.6.12版本开始,SET命令的行为可以通过一系列参数来修改,它提供附加的参数。例如,如果key已经存在就失败,或者如果key已经存在就成功。
1 2 3 4 |
127.0.0.1:6379> set mykey newval nx (nil) 127.0.0.1:6379> set mykey newval xx OK |
NX :只在键不存在时,才对键进行设置操作。SET key value NX效果等同于SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
EX second :设置键的过期时间为second秒。SET key value EX second效果等同于SETEX key second value 。
PX millisecond :设置键的过期时间为millisecond毫秒。SET key value PX millisecond效果等同于PSETEX key millisecond value 。
PS:因为SET命令可以通过参数来实现和SETNX 、SETEX和PSETEX三个命令的效果,所以将来的Redis版本可能会废弃并最终移除SETNX 、SETEX和PSETEX这三个命令。
GET
语法:GET key
返回key所关联的字符串值,如果key不存在那么返回特殊值nil。假如key储存的值不是字符串类型,返回一个错误,因为GET只能用于处理字符串值。
即使字符串是Redis的基本值,你可以使用有趣的操作选项执行,例如,一个是原子增量。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> set counter 100 OK 127.0.0.1:6379> incr counter (integer) 101 127.0.0.1:6379> incr counter (integer) 102 127.0.0.1:6379> incrby counter 50 (integer) 152 |
INCR
语法:INCR key
将key中储存的数字值加一,如果key不存在,那么key的值会先被初始化为0,然后再执行INCR操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
1 2 3 4 5 6 |
127.0.0.1:6379> SET page_view 20 OK 127.0.0.1:6379> INCR page_view (integer) 21 127.0.0.1:6379> GET page_view "21" |
INCRBY
语法:INCRBY key increment
将key所储存的值加上增量元素值。
1 2 3 4 5 6 |
127.0.0.1:6379> SET rank 50 OK 127.0.0.1:6379> INCRBY rank 20 (integer) 70 127.0.0.1:6379> GET rank "70" |
DECR
语法:DECR key
将key中储存的数字值减一,如果key不存在,那么key的值会先被初始化为0,然后再执行DECR操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
DECRBY
语法:DECRBY key decrement
将key所储存的值减去增量元素值。
INCRBYFLOAT
语法:INCRBYFLOAT key increment
为key中所储存的值加上浮点数增量元素值。
原子意味着什么?即使多个客户端使用INCR执行相同的key,也不会竞争。例如,client1和client2不会同时读取到‘10’,两个都增加到11,并设置新值为11。当其他的客户端没有执行相同命令的情况下,最终的值将保持12。
有一些操作字符串的命令。例如GETSE命令设置一个新值,返回原来的值。你可以使用这个命令,例如,如果你有个系统每次接收到访客就调用INCR增加Redis key。你可能想每小时收集一次这个信息,在不损伤增量的情况下。你可以GETSET key,将Key设置为0,并读取旧值。
为了减少延迟,一条命令执行设置或获取多个key是非常有用的。基于这种情况有MSET和MGET命令:
1 2 3 4 5 6 7 8 9 10 11 |
127.0.0.1:6380> SET redis redis.com OK 127.0.0.1:6380> SET mongodb mongodb.org OK 127.0.0.1:6380> MGET redis mongodb 1) "redis.com" 2) "mongodb.org" 127.0.0.1:6380> MGET redis mongodb mysql #不存在的MySQL返回nil; 1) "redis.com" 2) "mongodb.org" 3) (nil) |
MSET
语法:MSET key value [key value …]
同时设置一个或多个key-value对,如果某个给定key已经存在,那么MSET会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX命令,它只会在所有给定key都不存在的情况下进行设置操作。MSET是一个原子性(atomic)操作,所有给定key都会在同一时间内被设置,某些给定key被更新而另一些给定key没有改变的情况,不可能发生。
MGET
语法:MGET key [key …]
返回所有(一个或多个)给定 key 的值。如果给定的key里面,有某个key不存在,那么这个key返回特殊值nil。因此,该命令永不失败。
修改和查询key空间,有一些没有定义在特殊类型的命令,但是非常有助于操作key空间,因此可以用于任何的key。例如EXISTS命令返回1或0,表示指定的key是否存在于数据库,当DEL命令删除key和相关的value,无论value是什么。
1 2 3 4 5 6 7 8 9 10 11 |
127.0.0.1:6380> set mykey hello OK 127.0.0.1:6380> exists mykey (integer) 1 127.0.0.1:6380> del mykey (integer) 1 127.0.0.1:6380> exists mykey (integer) 0 |
从上面的例子可以看出,EXITS命令返回1或0,取决于key是否存在。
对于String(字符串)操作的其他常用命令:
STRLEN
语法:STRLEN key
返回key所储存的字符串值的长度,当key储存的不是字符串值时,返回一个错误。
1 2 3 4 5 6 |
127.0.0.1:6379> SET mykey "Hello world" OK 127.0.0.1:6379> STRLEN mykey (integer) 11 127.0.0.1:6379> STRLEN nonexisting (integer) 0 |
APPEND
语法:APPEND key value
如果key已经存在并且是一个字符串,APPEND命令将value追加到key原来的值的末尾。如果key不存在,APPEND就简单地将给定key设为value,就像执行SET key value一样。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> EXISTS myphone #确保myphone不存在; (integer) 0 127.0.0.1:6379> APPEND myphone "nokia" #对不存在的key进行APPEND,等同于SET myphone "nokia"; (integer) 5 127.0.0.1:6379> APPEND myphone " - 1110" #长度从5个字符增加到12个字符; (integer) 12 127.0.0.1:6379> GET myphone "nokia - 1110" |
GETSET
语法:GETSET key value
将给定key的值设为value,并返回key的旧值(old value)。当key存在但不是字符串类型时,返回一个错误。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> GETSET db mongodb #没有旧值,返回nil; (nil) 127.0.0.1:6379> GET db "mongodb" 127.0.0.1:6379> GETSET db redis #返回旧值mongodb; "mongodb" 127.0.0.1:6379> GET db "redis" |
SETRANGE
语法:SETRANGE key offset value
用value参数覆写(overwrite)给定key所储存的字符串值,从偏移量offset开始。不存在的key当作空白字符串处理。SETRANGE命令会确保字符串足够长以便将value设置在指定的偏移量上,如果给定key原来储存的字符串长度比偏移量小(比如字符串只有5个字符长,但你设置的offset是10 ),那么原字符和偏移量之间的空白将用零字节(zerobytes, “\x00” )来填充。
1 2 3 4 5 6 |
127.0.0.1:6379> SET greeting "hello world" OK 127.0.0.1:6379> SETRANGE greeting 6 "Redis" (integer) 11 127.0.0.1:6379> GET greeting "hello Redis" |
GETRANGE
语法:GETRANGE key start end
返回key中字符串值的子字符串,字符串的截取范围由start和end两个偏移量决定(包括start和end在内)。负数偏移量表示从字符串最后开始计数, -1表示最后一个字符,-2表示倒数第二个,以此类推。GETRANGE通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。
1 2 3 4 5 6 7 8 9 10 |
127.0.0.1:6379> SET greeting "hello, my friend" OK 127.0.0.1:6379> GETRANGE greeting 0 4 #返回索引0-4的字符,包括4; "hello" 127.0.0.1:6379> GETRANGE greeting 0 -1 #负数索引; "hello, my friend" 127.0.0.1:6379> GETRANGE greeting -3 -1 #从第一个到最后一个; "end" 127.0.0.1:6379> GETRANGE greeting 0 1008611 #值域范围不超过实际字符串,超过部分自动被符略; "hello, my friend" |
四、List(列表)
Redis将列表数据结构命名为list而不是array,是因为列表的存储结构用的是链表(Linked List)而不是数组(Array),而且链表还是双向链表。因为它是链表,所以随机定位性能较弱,首尾插入删除性能较优(使用LPUSH命令往有10个元素的list里添加一个新元素和往有1000万的list在头部添加一个元素速度一样)。如果list的列表长度很长,使用时我们一定要关注链表相关操作的时间复杂度。
负下标,链表元素的位置使用自然数0,1,2,....n-1
表示,还可以使用负数-1,-2,...-n
来表示,-1
表示「倒数第一」,-2
表示「倒数第二」,那么-n
就表示第一个元素,对应的下标为0
。
队列/堆栈,链表可以从表头和表尾追加和移除元素,结合使用rpush/rpop/lpush/lpop四条指令,可以将链表作为队列或堆栈使用,左向右向进行都可以。
RPUSH
语法:RPUSH key value [value …]
将一个或多个值插入到列表key的表头(最右边)。如果有多个值,那么各个值按从左到右的顺序依次插入到表头: 比如说,对空列表mylist执行命令RPUSH mylist a b c,列表的值将是a b c ,这等同于原子性地执行RPUSH mylist a 、RPUSH mylist b和RPUSH mylist c三个命令。如果key不存在,一个空列表会被创建并执行RPUSH操作。当key存在但不是列表类型时,返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 添加单个元素; redis> RPUSH languages c (integer) 1 # 添加重复元素; redis> RPUSH languages c (integer) 2 redis> LRANGE languages 0 -1 #列表允许重复元素; 1) "c" 2) "c" # 添加多个元素; redis> RPUSH mylist a b c (integer) 3 redis> LRANGE mylist 0 -1 1) "a" 2) "b" 3) "c" |
RPUSHX
语法:RPUSHX key value
将值value插入到列表key的表尾,当且仅当key存在并且是一个列表。和RPUSH命令相反,当key不存在时,RPUSHX命令什么也不做。
LPUSH
语法:LPUSH key value [value …]
将一个或多个值插入到列表key的表头(最左边)。如果有多个值,那么各个值按从左到右的顺序依次插入到表头: 比如说,对空列表mylist执行命令LPUSH mylist a b c,列表的值将是c b a ,这等同于原子性地执行LPUSH mylist a 、LPUSH mylist b和LPUSH mylist c三个命令。如果key不存在,一个空列表会被创建并执行LPUSH操作。当key存在但不是列表类型时,返回一个错误。
1 2 3 4 5 6 |
127.0.0.1:6379> LPUSH mylist a b c (integer) 3 127.0.0.1:6379> LRANGE mylist 0 -1 1) "c" 2) "b" 3) "a" |
LPUSHX
语法:LPUSHX key value
将值value插入到列表key的表头,当且仅当key存在并且是一个列表。和LPUSH命令相反,当key不存在时,LPUSHX命令什么也不做。
LRANGE
语法:LRANGE key start stop
返回列表中指定区间内的元素,区间以偏移量start和stop指定。下标(index)参数start和stop都以0为底,也就是说,以0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。你也可以使用负数下标,以-1表示列表的最后一个元素, -2表示列表的倒数第二个元素,以此类推。
另外注意,LRANGE超出范围的下标值不会引起错误。如果start下标比列表的最大下标end ( LLEN list减去1 )还要大,那么LRANGE返回一个空列表。如果stop下标比end下标还要大,Redis将stop的值设置为end 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# RPUSH加入单个元素; 127.0.0.1:6379> rpush mylist A (integer) 1 # RPUSH加入重复元素,列表允许重复元素; 127.0.0.1:6379> rpush mylist A (integer) 2 # RPUSH加入多个元素; 127.0.0.1:6379> rpush mylist B C (integer) 4 # LPUSH加入单个元素; 127.0.0.1:6379> lpush mylist first (integer) 5 # LRANGE查看元素; 127.0.0.1:6379> lrange mylist 0 -1 1) "first" 2) "A" 3) "A" 4) "B" 5) "C" |
注意LRANGE有两个索引,第一个和最后一个范围内的元素将被返回。索引都可以为负数,告诉Redis从结束开始计数:所以-1是最后一个,-2是list的倒数第二个元素,等等。
在Redis lists上有一个重要的定义是有pop元素的能力。pop元素是从list检索元素同时从list里消除元素的操作。你可以从左边或者右边pop元素,类似于往list里插入元素。
RPOP
语法:RPOP key
移除并返回列表的尾元素。
LPOP
语法:LPOP key
移除并返回列表的头元素。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> rpop mylist "C" 127.0.0.1:6379> rpop mylist "B" 127.0.0.1:6379> lpop mylist "first" 127.0.0.1:6379> lrange mylist 0 -1 1) "A" 2) "A" |
如果我们尝试pop一个空的或者不存在元素,这是我们得到的结果:
1 2 |
127.0.0.1:6379> rpop my (nil) |
LREM
语法:LREM key count value
删除元素,列表的删除操作也不是通过指定下标来确定元素的,你需要指定删除的最大个数以及元素的值。就是根据参数count的值,移除列表中与参数value相等的元素。
count的值可以是以下几种:
count > 0 : 从表头开始向表尾搜索,移除与value相等的元素,数量为count 。
count < 0 : 从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
count = 0 : 移除表中所有与value相等的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
127.0.0.1:6379> LPUSH greet morning hello morning helllo morning (integer) 5 127.0.0.1:6379> LRANGE greet 0 -1 1) "morning" 2) "helllo" 3) "morning" 4) "hello" 5) "morning" 127.0.0.1:6379> LREM greet 4 morning (integer) 3 127.0.0.1:6379> LRANGE greet 0 -1 1) "helllo" 2) "hello" 127.0.0.1:6379> 127.0.0.1:6379> LREM greet 1 hello (integer) 1 127.0.0.1:6379> LRANGE greet 0 -1 1) "helllo" |
LTRIM
语法:LTRIM key start stop
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。举个例子,执行命令LTRIM list 0 2 ,表示只保留列表的前三个元素,其余元素全部删除。下标(index)参数start和stop都以0为底,也就是说,以0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。你也可以使用负数下标,以-1表示列表的最后一个元素, -2表示列表的倒数第二个元素,以此类推。当key不是列表类型时,返回一个错误。
示例会看的更清楚:
1 2 3 4 5 6 7 8 |
127.0.0.1:6380> rpush list 1 2 3 4 5 (integer) 5 127.0.0.1:6380> ltrim my 0 2 OK 127.0.0.1:6380> lrange list 0 -1 1) "1" 2) "2" 3) "3" |
上面的LTRIM命令告诉Redis只展示从0到2元素,其他的将会被丢弃。这可让一个非常简单但有用的模式:做一个列表推送操作和一个列表裁剪操作,以增加新的元素和丢弃超额的元素。
在实际应用场景中,我们有时候会遇到「定长列表」的需求。比如要以走马灯的形式实时显示中奖用户名列表,因为中奖用户实在太多,能显示的数量一般不超过100条,那么这里就会使用到定长列表。
LLEN
语法:LLEN key
返回列表 key 的长度,如果key不存在,则key被解释为一个空列表,返回0。如果key不是列表类型,返回一个错误。
1 2 3 4 5 6 |
127.0.0.1:6379> LPUSH job "cook food" (integer) 1 127.0.0.1:6379> LPUSH job "have lunch" (integer) 2 127.0.0.1:6379> LLEN job (integer) 2 |
LINDEX
语法:LINDEX key index
可以使用LINDEX指令访问指定位置(下标)的元素,下标(index)参数start和stop都以0为底,也就是说,以0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。你也可以使用负数下标,以-1表示列表的最后一个元素, -2表示列表的倒数第二个元素,以此类推。
如果key不是列表类型,返回一个错误。
1 2 3 4 5 6 7 8 9 10 |
127.0.0.1:6379> LPUSH mylist "World" (integer) 1 127.0.0.1:6379> LPUSH mylist "Hello" (integer) 2 127.0.0.1:6379> LINDEX mylist 0 "Hello" 127.0.0.1:6379> LINDEX mylist -1 "World" 127.0.0.1:6379> LINDEX mylist 2 (nil) |
LSET
语法:LSET key index value
使用LSET指令在指定位置(下标)修改元素。当下标index参数超出范围,或对一个空列表(key不存在)进行LSET时,返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 对空列表(key 不存在)进行 LSET redis> EXISTS list (integer) 0 redis> LSET list 0 item (error) ERR no such key # 对非空列表进行 LSET redis> LPUSH job "cook food" (integer) 1 redis> LRANGE job 0 0 1) "cook food" redis> LSET job 0 "play game" OK redis> LRANGE job 0 0 1) "play game" # index 超出范围 redis> LLEN list (integer) 1 redis> LSET list 3 'out of range' (error) ERR index out of range |
RPOPLPUSH
语法:RPOPLPUSH source destination
命令RPOPLPUSH在一个原子时间内,执行以下两个动作:
1)将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
2)将source 弹出的元素插入到列表destination ,作为 destination 列表的的头元素。
举个例子,你有两个列表source和destination,source列表有元素a, b, c ,destination列表有元素 x, y, z ,执行RPOPLPUSH source destination之后,source列表包含元素a, b , destination列表包含元素c, x, y, z ,并且元素c会被返回给客户端。
如果source不存在,值nil被返回,并且不执行其他动作。
如果source和destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
127.0.0.1:6379> LPUSH source a b c (integer) 3 127.0.0.1:6379> LPUSH dest x y z (integer) 3 127.0.0.1:6379> RPOPLPUSH source dest "a" 127.0.0.1:6379> LRANGE source 0 -1 1) "c" 2) "b" 127.0.0.1:6379> LRANGE dest 0 -1 1) "a" 2) "z" 3) "y" 4) "x" |
BRPOPLPUSH
语法:BRPOPLPUSH source destination timeout
BRPOPLPUSH是RPOPLPUSH的阻塞版本,当给定列表source不为空时, BRPOPLPUSH的表现和RPOPLPUSH一样。当列表source为空时, BRPOPLPUSH命令将阻塞连接,直到等待超时,或有另一个客户端对source执行LPUSH或RPUSH命令为止。
超时参数timeout接受一个以秒为单位的数字作为值。超时参数设为0表示阻塞时间可以无限期延长(block indefinitely) 。
1 2 3 |
127.0.0.1:6379> BRPOPLPUSH msg reciver 1 (nil) (1.03s) |
五、Hash(哈希表)
Hash存储结构具有两大优点,首先就是查询飞快,众所周知Hash表的查询复杂度为O(1),可以提供快速查询的业务。第二则是节约内存,业务场景中如果存储的是数据集,则可以存入Hash数组节约内存。
Redis Hash是字符串字段和字符串值之间的映射,因此它们是表示对象的完美数据类型,例如,具有多个字段(如姓名,姓氏,年龄等)的用户信息,用户ID作为key,用户的所有属性序列化成json存储为value,如下:
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> HMSET user:1000 username antirez password P1pp0 age 34 OK 127.0.0.1:6379> HGETALL user:1000 1) "username" 2) "antirez" 3) "password" 4) "P1pp0" 5) "age" 6) "34" |
这样的存储结构,我们就可以直接修改对应的属性了,并且不会带来序列化和反序列化的开销。
并且具有几个字段的哈希以占用很少空间的方式存储,因此你可以将数百万个对象存储在一个小的Redis实例中。虽然Hash主要用于表示对象,但它们能够存储许多元素,因此你还可以使用Hash进行许多其他任务。每个哈希可以存储多达2**32 – 1个字段值对(超过40亿)。
可以使用HSET一次增加一个键值对,也可以使用HMSET一次增加多个键值对。可以通过HGET定位具体key对应的value,可以通过HMGET获取多个key对应的value,可以使用HGETALL获取所有的键值对,可以使用HKEYS和HVALS分别获取所有的key列表和value列表。
HSET
语法:HSET key field value
将哈希表key中的域field的值设为value。如果key不存在,一个新的哈希表被创建并进行HSET操作。如果域field已经存在于哈希表中,旧值将被覆盖。
1 2 3 4 |
127.0.0.1:6379> HSET website google "www.g.cn" (integer) 1 127.0.0.1:6379> HSET website google "www.google.com" (integer) 0 |
HGET
语法:HGET key field
返回哈希表key中给定域field的值。当给定域不存在或是给定key不存在时,返回nil 。
1 2 3 4 5 6 7 8 9 |
# 域存在; 127.0.0.1:6379> HSET site redis redis.com (integer) 1 127.0.0.1:6379> HGET site redis "redis.com" # 域不存在; 127.0.0.1:6379> HGET site mysql (nil) |
HGETALL
语法:HGETALL key
返回哈希表key中,所有的域和值。在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
1 2 3 4 5 6 7 8 9 10 |
127.0.0.1:6379> HSET people jack "Jack Sparrow" (integer) 1 127.0.0.1:6379> HGETALL people 1) "jack" 2) "Jack Sparrow" 127.0.0.1:6379> HGETALL people 1) "jack" 2) "Jack Sparrow" 3) "gump" 4) "Forrest Gump" |
HMSET
语法:HMSET key field value [field value …]
同时将多个field-value (域-值)对设置到哈希表key中。此命令会覆盖哈希表中已存在的域。如果key不存在,一个空哈希表被创建并执行HMSET操作。
1 2 3 4 |
127.0.0.1:6379> HMSET db redis www.redis.com mysql www.mysql.com OK 127.0.0.1:6379> HGET db redis "www.redis.com" |
HMGET
语法:HMGET key field [field …]
返回哈希表key中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个nil值。因为不存在的key被当作一个空哈希表来处理,所以对一个不存在的key进行HMGET操作将返回一个只带有nil值的表。
1 2 3 4 5 |
127.0.0.1:6379> HMSET db redis www.redis.com mysql www.mysql.com OK 127.0.0.1:6379> HMGET db redis mysql 1) "www.redis.com" 2) "www.mysql.com" |
HDEL
语法:HDEL key field [field …]
删除哈希表key中的一个或多个指定域,不存在的域将被忽略。
1 2 3 4 |
127.0.0.1:6379> HMSET site redis redis.com mysql mysql.com OK 127.0.0.1:6379> HDEL site redis mysql (integer) 2 |
HEXISTS
语法:HEXISTS key field
查看哈希表key中,给定域field是否存在。
1 2 3 4 5 6 |
127.0.0.1:6379> HEXISTS phone myphone (integer) 0 127.0.0.1:6379> HSET phone myphone nokia-1110 (integer) 1 127.0.0.1:6379> HEXISTS phone myphone (integer) 1 |
HKEYS
语法:HKEYS key
返回哈希表key中的所有域。
1 2 3 4 5 |
127.0.0.1:6379> HMSET website google www.google.com yahoo www.yahoo.com OK 127.0.0.1:6379> HKEYS website 1) "google" 2) "yahoo" |
HLEN
语法:HLEN key
返回哈希表key中域的数量。
1 2 3 4 |
127.0.0.1:6379> HMSET db redis redis.com mysql mysql.com OK 127.0.0.1:6379> HLEN db (integer) 2 |
HVALS
语法:HVALS key
返回哈希表key中所有域的值。
1 2 3 4 5 |
127.0.0.1:6379> HMSET website google www.google.com yahoo www.yahoo.com OK 127.0.0.1:6379> HVALS website 1) "www.google.com" 2) "www.yahoo.com" |
HINCRBY
语法:HINCRBY key field increment
为哈希表key中的域field的值加上增量increment 。增量也可以为负数,相当于对给定域进行减法操作。
如果key不存在,一个新的哈希表被创建并执行HINCRBY命令。如果域field不存在,那么在执行命令前,域的值被初始化为0 。
对一个储存字符串值的域field执行HINCRBY命令将造成一个错误。本操作的值被限制在 64 位(bit)有符号数字表示之内。
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> HINCRBY counter page_view 200 (integer) 200 127.0.0.1:6379> HGET counter page_view "200" 127.0.0.1:6379> HINCRBY counter page_view +10 (integer) 210 127.0.0.1:6379> HGET counter page_view "210" |
HSETNX
语法:HSETNX key field value
将哈希表key中的域field的值设置为value,当且仅当域field不存在。若域field已经存在,该操作无效。如果key不存在,一个新哈希表被创建并执行HSETNX命令。
扩容:当hash内部的元素比较拥挤时(hash碰撞比较频繁),就需要进行扩容。扩容需要申请新的两倍大小的数组,然后将所有的键值对重新分配到新的数组下标对应的链表中(rehash)。如果hash结构很大,比如有上百万个键值对,那么一次完整rehash的过程就会耗时很长。这对于单线程的Redis里来说有点压力山大。所以Redis采用了渐进式rehash的方案。它会同时保留两个新旧hash结构,在后续的定时任务以及hash结构的读写指令中将旧结构的元素逐渐迁移到新的结构中。这样就可以避免因扩容导致的线程卡顿现象。
缩容:Redis的hash结构不但有扩容还有缩容,从这一点出发,它要比Java的HashMap要厉害一些,Java的HashMap只有扩容。缩容的原理和扩容是一致的,只不过新的数组大小要比旧数组小一倍。
六、Set(集合)
Redis集合是一个无序的字符串集合。Redis集合具有不允许重复成员的属性,多次添加相同的元素将导致一个集合具有该元素的单个副本。实际上这意味着添加成员不需要检查是否存在,然后执行添加操作。
关于Redis集合的一个非常有趣的事情是,它们支持多个服务器端命令来从现有集合中开始计算集合,因此你可以在很短的时间内进行差集、交集、并集的计算。你可以使用Redis Sets做许多有趣的事情,例如你可以使用Redis Sets跟踪独特的事物。想知道访问给定博客文章的所有唯一IP地址?每次处理页面浏览时,只需使用SADD,确定重复的IP不会被插入。
Redis的set结构内部也使用hash结构,所有的value都指向同一个内部值,只不过所有的value都指向同一个对象。
SADD
语法:SADD key member [member …]
将一个或多个member元素加入到集合key当中,已经存在于集合的member元素将被忽略。假如key不存在,则创建一个只包含member元素作成员的集合。
当key不是集合类型时,返回一个错误。
1 2 3 4 5 6 7 8 9 10 |
127.0.0.1:6379> SADD bbs "discuz.net" (integer) 1 127.0.0.1:6379> SADD bbs "discuz.net" (integer) 0 127.0.0.1:6379> SADD bbs "tianya.cn" "groups.google.com" (integer) 2 127.0.0.1:6379> SMEMBERS bbs 1) "groups.google.com" 2) "discuz.net" 3) "tianya.cn" |
SCARD
语法:SCARD key
返回集合key的基数(集合中元素的数量)。
1 2 3 4 |
127.0.0.1:6379> SADD tool pc printer phone (integer) 3 127.0.0.1:6379> SCARD tool (integer) 3 |
SDIFF
语法:SDIFF key [key …]
返回一个集合的全部成员,该集合是所有给定集合之间的差集,不存在的key被视为空集。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> SADD tool pc printer phone (integer) 0 127.0.0.1:6379> 127.0.0.1:6379> SADD dool pc printer telephone (integer) 3 127.0.0.1:6379> SDIFF tool dool 1) "phone" 127.0.0.1:6379> SDIFF dool tool 1) "telephone" |
SDIFFSTORE
语法:SDIFFSTORE destination key [key …]
这个命令的作用和SDIFF类似,但它将结果保存到destination集合,而不是简单地返回结果集。如果destination集合已经存在,则将其覆盖。destination可以是key本身。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> SADD tool pc printer phone (integer) 3 127.0.0.1:6379> SADD dool pc printer telephone (integer) 3 127.0.0.1:6379> 127.0.0.1:6379> SDIFFSTORE dest tool dool (integer) 1 127.0.0.1:6379> SMEMBERS dest 1) "phone" |
七、SortedSet(有序集合)
与Redis Sets类似,Redis SortedSet不是重复的字符串集合。不同之处在于,有序集合的每个成员都与得分相关联,可以给每一个元素赋予一个权重score,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。虽然成员是独一无二的,但可能会重复分数。
有序集合底层实现使用了两个数据结构,第一个是hash,第二个是跳跃列表,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。跳跃列表的目的在于给元素value排序,根据score的范围获取元素列表。
使用有序集合,你可以以非常快速的方式添加,删除或更新元素(在与元素数量的对数成正比的时间内)。由于元素是按顺序进行的,因此你也可以通过分数或排名(位置)以非常快的速度获得范围。访问有序集合的中间位置也非常快,因此你可以使用有序集合作为非重复元素的智能列表,你可以快速访问所需的所有内容:元素顺序,快速存在测试,快速访问中间元素!简而言之,使用有序集合,你可以做很多任务,具有很好的性能,难以在其他类型的数据库中建模。
ZADD
语法:ZADD key score member [[score member] [score member] …]
将一个或多个member元素及其score值加入到有序集key当中。如果某个member已经是有序集的成员,那么更新这个member的score值,并通过重新插入这个member元素,来保证该member在正确的位置上。score值可以是整数值或双精度浮点数。
如果key不存在,则创建一个空的有序集并执行ZADD操作。当key存在但不是有序集类型时,返回一个错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 添加单个元素; 127.0.0.1:6379> ZADD page_rank 10 google.com (integer) 1 # 添加多个元素; 127.0.0.1:6379> ZADD page_rank 9 baidu.com 8 bing.com (integer) 2 127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES 1) "bing.com" 2) "8" 3) "baidu.com" 4) "9" 5) "google.com" 6) "10" # 添加已存在元素,但是改变score值; 127.0.0.1:6379> ZADD page_rank 6 baidu.com (integer) 0 127.0.0.1:6379> ZRANGE page_rank 0 -1 WITHSCORES #baidu.com的score变小,位置发生变化; 1) "baidu.com" 2) "6" 3) "bing.com" 4) "8" 5) "google.com" 6) "10" |
ZCARD
语法:ZCARD key
返回有序集key的个数,当key存在且是有序集类型时,返回有序集的个数。当key不存在时,返回0。
1 2 |
127.0.0.1:6379> ZCARD page_rank (integer) 3 |
ZCOUNT
语法:ZCOUNT key min max
返回有序集key中,score值在min和max之间(默认包括score值等于min或max)的成员的数量。
1 2 3 4 5 6 7 8 9 10 11 |
# 添加数据; 127.0.0.1:6379> ZADD salary 2000 jack 3500 peter 5000 tom (integer) 3 # 计算薪水在2000-5000的人数; 127.0.0.1:6379> ZCOUNT salary 2000 5000 (integer) 3 # 计算薪水在3000-5000的人数; 127.0.0.1:6379> ZCOUNT salary 3000 5000 (integer) 2 |
ZSCORE
语法:ZSCORE key member
返回有序集key中,成员member的score值。如果member元素不是有序集key的成员,或key不存在,返回nil。
1 2 |
127.0.0.1:6379> ZSCORE salary tom "5000" |
ZRANGE
语法:ZRANGE key start stop [WITHSCORES]
返回有序集key中,指定区间内的成员。其中成员的位置按score值递增(从小到大)来排序。具有相同score值的成员按字典序(lexicographical order)来排列。如果你需要成员按score值递减(从大到小)来排列,请使用ZREVRANGE命令。
下标参数start和stop都以0为底,也就是说,以0表示有序集第一个成员,以1表示有序集第二个成员,以此类推。你也可以使用负数下标,以-1表示最后一个成员,-2表示倒数第二个成员,以此类推。超出范围的下标并不会引起错误。比如说,当start的值比有序集的最大下标还要大,或是start > stop时,ZRANGE命令只是简单地返回一个空列表。另一方面,假如stop参数的值比有序集的最大下标还要大,那么Redis将stop当作最大下标来处理。可以通过使用WITHSCORES选项,来让成员和它的score值一并返回,返回列表以value1,score1,…,valueN,scoreN的格式表示。客户端库可能会返回一些更复杂的数据类型,比如数组、元组等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
127.0.0.1:6379> ZADD salary 2000 jack 3500 peter 5000 tom (integer) 0 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "jack" 2) "2000" 3) "peter" 4) "3500" 5) "tom" 6) "5000" 127.0.0.1:6379> ZRANGE salary 1 2 1) "peter" 2) "tom" 127.0.0.1:6379> ZRANGE salary 200000 3000000 WITHSCORES (empty list or set) |
ZRANGEBYSCORE
语法:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score值递增(从小到大)次序排列。
可选的LIMIT参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count),注意当offset很大时,定位offset的操作可能需要遍历整个有序集,此过程最坏复杂度为O(N)时间。可选的WITHSCORES参数决定结果集是单单返回有序集的成员,还是将有序集成员及其score值一起返回。
min和max可以是-inf和+inf,这样一来,你就可以在不知道有序集的最低和最高score值的情况下,使用ZRANGEBYSCORE这类命令。默认情况下,区间的取值使用闭区间(小于等于或大于等于),你也可以通过给参数前增加符号来使用可选的开区间(小于或大于)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# 测试数据; 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "jack" 2) "2500" 3) "tom" 4) "5000" 5) "peter" 6) "12000" # 显示整个有序集; 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf 1) "jack" 2) "tom" 3) "peter" # 显示工资<=5000的所有成员; 127.0.0.1:6379> ZRANGEBYSCORE salary -inf 5000 WITHSCORES 1) "jack" 2) "2500" 3) "tom" 4) "5000" # 显示工资大于5000小于等于400000的成员; 127.0.0.1:6379> ZRANGEBYSCORE salary (5000 400000 WITHSCORES 1) "peter" 2) "12000" |
ZREVRANGE
语法:ZREVRANGE key start stop [WITHSCORES]
返回有序集key中,指定区间内的成员。其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典序的逆序(reverse lexicographical order)排列。除了成员按score值递减的次序排列这一点外,ZREVRANGE命令的其他方面和ZRANGE命令一样。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> ZADD salary 2500 jack 5000 tom 12000 peter (integer) 2 127.0.0.1:6379> ZREVRANGE salary 0 -1 WITHSCORES 1) "peter" 2) "12000" 3) "tom" 4) "5000" 5) "jack" 6) "2500" |
ZREVRANGEBYSCORE
语法:ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
返回有序集key中,score值介于max和min之间(默认包括等于max或min)的所有的成员。有序集成员按score值递减(从大到小)的次序排列。除了成员按score值递减的次序排列这一点外,ZREVRANGEBYSCORE命令的其他方面和ZRANGEBYSCORE命令一样。
ZRANK
语法:ZRANK key member
返回有序集key中成员member的排名,其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底,也就是说,score值最小的成员排名为0。使用ZREVRANK命令可以获得成员按score值递减(从大到小)排列的排名。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "jack" 2) "2000" 3) "peter" 4) "3500" 5) "tom" 6) "5000" 127.0.0.1:6379> ZRANK salary peter (integer) 1 |
ZREVRANK
语法:ZREVRANK key member
返回有序集key中成员member的排名,其中有序集成员按score值递减(从大到小)排序。排名以0为底,也就是说,score值最大的成员排名为0 。使用ZRANK命令可以获得成员按score值递增(从小到大)排列的排名。
ZINCRBY
语法:ZINCRBY key increment member
为有序集key的成员member的score值加上增量increment。可以通过传递一个负数值increment,让score减去相应的值,比如ZINCRBY key -5 member ,就是让member的score值减去5。
当key不存在,或member不是key的成员时, ZINCRBY key increment member等同于ZADD key increment member。当key不是有序集类型时,返回一个错误。score 值可以是整数值或双精度浮点数。
1 2 3 4 |
127.0.0.1:6379> ZSCORE salary tom "5000" 127.0.0.1:6379> ZINCRBY salary 2000 tom "7000" |
ZREM
语法:ZREM key member [member …]
移除有序集key中的一个或多个成员,不存在的成员将被忽略。当key存在但不是有序集类型时,返回一个错误。
1 2 3 4 5 6 7 8 9 |
127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "jack" 2) "2000" 3) "peter" 4) "3500" 5) "tom" 6) "5000" 127.0.0.1:6379> ZREM salary jack peter (integer) 2 |
ZREMRANGEBYRANK
语法: ZREMRANGEBYRANK key start stop
移除有序集key中,指定排名(rank)区间内的所有成员。区间分别以下标参数start和stop指出,包含start和stop在内。下标参数start和stop都以0为底,也就是说,以0表示有序集第一个成员,以1表示有序集第二个成员,以此类推。你也可以使用负数下标,以-1表示最后一个成员,-2表示倒数第二个成员,以此类推。
1 2 3 4 5 6 7 |
127.0.0.1:6379> ZADD salary 2500 jack 5000 tom 12000 peter (integer) 0 127.0.0.1:6379> ZREMRANGEBYRANK salary 0 1 (integer) 2 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "peter" 2) "12000" |
ZREMRANGEBYSCORE
语法:ZREMRANGEBYSCORE key min max
移除有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。自版本2.1.6开始,score值等于min或max的成员也可以不包括在内,详情请参见ZRANGEBYSCORE命令。
1 2 3 4 5 6 7 |
127.0.0.1:6379> ZADD salary 2500 jack 5000 tom 12000 peter (integer) 2 127.0.0.1:6379> ZREMRANGEBYSCORE salary 1500 5000 (integer) 2 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES 1) "peter" 2) "12000" |
<扩展>
<使用场景>