127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> set k2 222
OK
127.0.0.1:6379> set k3 333
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
keys *命令会将所有的键输出,如上所示。
dbsize命令会返回当前数据库中键的总数 .
127.0.0.1:6379> dbsize
(integer) 3
dbsize命令在计算键总数时不会遍历所有键, 而是直接获取Redis内置的键总数变量, 所以dbsize命令的时间复杂度是O(1) 。 而keys命令会遍历所有键, 所以它的时间复杂度是O(n) , 当Redis保存了大量键时, 线上环境禁止使用。
EXISTS key [key …]
127.0.0.1:6379> EXISTS k1
(integer) 1
127.0.0.1:6379> EXISTS k4
(integer) 0
如上结果所示:键存在会返回1,不存在会返回0
DEL key [key …]
del是一个通用命令, 无论值是什么数据结构类型, del命令都可以将其删除 。
如下演示,删除k1,然后执行检测k1结果是0不存在。
127.0.0.1:6379> DEL k1
(integer) 1
127.0.0.1:6379> EXISTS k1
(integer) 0
EXPIRE key seconds
ttl命令会返回键的剩余过期时间, 它有3种返回值:
127.0.0.1:6379> set k1 11111
OK
#k1 设置10s过期时间
127.0.0.1:6379> EXPIRE k1 10
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 6
127.0.0.1:6379> ttl k1
(integer) 4
127.0.0.1:6379> ttl k1
(integer) 2
127.0.0.1:6379> ttl k1
(integer) -2
TYPE key
127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> type k1
string
127.0.0.1:6379> LPUSH l1 1 2 3 4
(integer) 4
127.0.0.1:6379> type l1
list
如果键不存在, 则返回none:
127.0.0.1:6379> TYPE k2
none
字符串类型是Redis最基础的数据结构。 首先键都是字符串类型, 而且其他几种数据结构都是在字符串类型基础上构建的, 所以字符串类型能为其他四种数据结构的学习奠定基础。 字符串类型的值实际可以是字符串(简单的字符串、 复杂的字符串(例如JSON、 XML) ) 、 数字(整数、 浮点数) , 甚至是二进制(图片、 音频、 视频) , 但是值最大不能超过512MB。
SET key value [EX seconds] [PX milliseconds] [NX|XX]
参数选项:
ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功, 用于添加。
xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。
如:
127.0.0.1:6379> SET h1 1111
OK
简化set操作,设置值并设置过期时间,SETEX key seconds value (三个参数一个)
127.0.0.1:6379> SETEX h2 2222
(error) ERR wrong number of arguments for 'setex' command
127.0.0.1:6379> SETEX h2 10 2222
OK
扩展:基于set命令设置的复杂性,redis提供了setxx 和setnx两个命令用于简化操作。
setnx和setxx在实际使用中有什么应用场景吗? 以setnx命令为例子, 由于Redis的单线程命令处理机制, 如果有多个客户端同时执行setnx key value,根据setnx的特性只有一个客户端能设置成功, setnx可以作为分布式锁的一种实现方案, Redis官方给出了使用setnx实现分布式锁的方法: http://redis.io/topics/distlock。
SETNX key value
设置key,只有key不存在才会设置成功;如下演示k1已经存在setnx通过设置不成功,k2不存在则可以设置
127.0.0.1:6379> KEYS *
1) "h1"
2) "l1"
3) "k1"
127.0.0.1:6379> SETNX k1 4444
(integer) 0
127.0.0.1:6379> SETNX k2 5555
(integer) 1
GET key
127.0.0.1:6379> GET h1
"1111"
如果要获取的键不存在, 则返回nil(空)
127.0.0.1:6379> GET K4
(nil)
MSET key value [key value …]
127.0.0.1:6379> MSET k3 333 k4 444 k5 555
OK
127.0.0.1:6379> keys *
1) "h1"
2) "k4"
3) "l1"
4) "k5"
5) "k3"
6) "k1"
7) "k2"
MGET key [key …]
127.0.0.1:6379> MGET k3 k4 k5
1) "333"
2) "444"
3) "555"
批量操作命令可以有效提高开发效率, 假如没有mget这样的命令, 要执行n次get命令 具体耗时如下:
n次get时间 = n次网络时间 + n次命令时间
使用mget命令后, 要执行n次get命令操 具体耗时如下:
n次get时间 = 1次网络时间 + n次命令时间
使用批量操作, 有助于提高业务处理效率, 但是要注意的是每次批量操作所发送的命令数不是无节制的, 如果数量过多可能造成Redis阻塞或者网络拥塞。
INCR key
incr命令用于对值做自增操作, 返回结果分为三种情况:
除了incr命令, Redis提供了decr(自减) 、 incrby(自增指定数字) 、decrby(自减指定数字) 、 incrbyfloat(自增浮点数)
APPEND key value
APPEND命令,向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 SET key value。返回值是追加后字符串的总长度。
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> APPEND k1 world
(integer) 10
127.0.0.1:6379> APPEND k1 redis
(integer) 15
127.0.0.1:6379> get k1
"helloworldredis"
STRLEN key
STRLEN命令,返回键值的长度,如果键不存在则返回0。
127.0.0.1:6379> get k1
"helloworldredis"
127.0.0.1:6379> STRLEN k1
(integer) 15
假设存储User对象,它有id,username、password、age、name等属性,需要转成JSON格式存储到Redis中,存储的过程如下:
User对象->json(string)->redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢?如果仍然采用上边的方法在传输、处理时会造成资源浪费。
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
哈希类型中的映射关系叫作field-value, 注意这里的value是指field对应的值, 不是键对应的值.
HSET key field value
127.0.0.1:6379> HSET user name zhangsan
(integer) 1
127.0.0.1:6379> HSET user age 28
(integer) 1
127.0.0.1:6379> HSET user sex man
(integer) 1
HGET key field
127.0.0.1:6379> HGET user name
"zhangsan"
127.0.0.1:6379> HGET user age
"28"
127.0.0.1:6379> HGET user sex
"man"
如果键或field不存在, 会返回nil
127.0.0.1:6379> HGET user phone
(nil)
HDEL key field [field …]
hdel会删除一个或多个field, 返回结果为成功删除field的个数
127.0.0.1:6379> HDEL user sex
(integer) 1
127.0.0.1:6379> HDEL user name age
(integer) 2
HLEN key
127.0.0.1:6379> HLEN user
(integer) 0
127.0.0.1:6379> HSET user name 1
(integer) 1
127.0.0.1:6379> HSET user age 2
(integer) 1
127.0.0.1:6379> HSET user phone 1306666666
(integer) 1
127.0.0.1:6379> HLEN user
(integer) 3
HMGET key field [field …]
127.0.0.1:6379> HMGET user name age phone
1) "1"
2) "2"
3) "1306666666"
127.0.0.1:6379> HMGET user name age sex
1) "1"
2) "2"
3) (nil)
如果某个field不存在,则会返回nil
HMSET key field value [field value …]
127.0.0.1:6379> HMSET user1 name lisi age 28 phone 12345
OK
HEXISTS key field
127.0.0.1:6379> HEXISTS user1 name
(integer) 1
127.0.0.1:6379> HEXISTS user1 sex
(integer) 0
存在返回1,不存在返回0
HKEYS key
127.0.0.1:6379> HKEYS user1
1) "name"
2) "age"
3) "phone"
HVALS key
127.0.0.1:6379> HVALS user1
1) "lisi"
2) "28"
3) "12345"
HGETALL key
127.0.0.1:6379> HGETALL user1
1) "name"
2) "lisi"
3) "age"
4) "28"
5) "phone"
6) "12345"
在使用hgetall时, 如果哈希元素个数比较多, 会存在阻塞Redis的可能。如果开发人员只需要获取部分field, 可以使用hmget, 如果一定要获取全部field-value, 可以使用hscan命令, 该命令会渐进式遍历哈希类型.
存储用户信息或商品信息
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
Redis的列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素(push)和弹出(pop),或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。
列表是一种比较灵活的数据结构, 它可以充当栈和队列的角色, 在实际开发上有很多应用场景
列表类型有两个特点: 第一、 列表中的元素是有序的, 这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表.第二、 列表中的元素可以是重复的。
操作类型 | 命令 |
---|---|
添加 | rpush lpush linsert |
查 | lrange lindex llen |
删除 | lpop rpop lrem ltrim |
修改 | lset |
阻塞 | blpop brpop |
LPUSH key value [value …]
127.0.0.1:6379> LPUSH list1 1 2 3 4 5 6
(integer) 6
lrange 0-1 命令可以从右到左获取列表的所有元素
127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
RPUSH key value [value …]
127.0.0.1:6379> RPUSH list2 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> LRANGE list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
LRANGE key start stop
lrange操作会获取列表指定索引范围所有的元素。 索引下标有两个特 第一, 索引下标从左到右分别是0到N-1, 但是从右到左分别是-1到-N。第二, lrange中的end选项包含了自身
LINDEX key index
127.0.0.1:6379> LINDEX list2 1
"2"
127.0.0.1:6379> LINDEX list2 0
"1"
LLEN key
127.0.0.1:6379> LLEN list2
(integer) 6
LPOP key
127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> LPOP list1
"6"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
RPOP key
127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> LPOP list1
"6"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> RPOP list1
"1"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
LREM key count value
lrem命令会从列表中找到等于value的元素进行删除, 根据count的不同
分为三种情况:
count>0, 从左到右, 删除最多count个元素。
count<0, 从右到左, 删除最多count绝对值个元素。
count=0, 删除所有。
LTRIM key start stop
start 和 stop都是下标索引
127.0.0.1:6379> LRANGE list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
127.0.0.1:6379> LTRIM list2 1 3
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "2"
2) "3"
3) "4"
LSET key index value
127.0.0.1:6379> LRANGE list2 0 -1
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> LSET list2 0 222
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "222"
2) "3"
3) "4"
set类型即集合类型,其中的数据是不重复且没有顺序。
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。
Redis还提供了多个集合之间的交集、并集、差集的运算。
SADD key member [member …]
127.0.0.1:6379> EXISTS s1
(integer) 0
127.0.0.1:6379> SADD s1 a b c d
(integer) 4
127.0.0.1:6379> SADD s1 a b #因为上一条命令已经添加了 a b,所以第二次执行时,返回0表示没有添加成功
(integer) 0
SREM key member [member …]
127.0.0.1:6379> SREM s1 a b
(integer) 2
SCARD key
127.0.0.1:6379> SCARD s1
(integer) 2
scard的时间复杂度为O(1) , 它不会遍历集合所有元素, 而是直接用Redis内部的变量 .
SISMEMBER key member
127.0.0.1:6379> SISMEMBER s1 a
(integer) 0
127.0.0.1:6379> SISMEMBER s1 c
(integer) 1
如果给定元素element在集合内返回1, 反之返回0
SMEMBERS key
127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
smembers和lrange、 hgetall都属于比较重的命令, 如果元素过多存在阻塞Redis的可能性, 这时候可以使用sscan来完成.
SINTER key [key …]
127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "d"
2) "r"
3) "c"
4) "y"
5) "e"
127.0.0.1:6379> SINTER s1 s2
1) "c"
2) "d"
SUNION key [key …]
127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "c"
2) "y"
3) "e"
4) "d"
5) "r"
127.0.0.1:6379> SUNION s1 s2
1) "e"
2) "d"
3) "c"
4) "y"
5) "r"
SDIFF key [key …]
127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "c"
2) "y"
3) "e"
4) "d"
5) "r"
127.0.0.1:6379> SDIFF s1 s2
(empty list or set)
127.0.0.1:6379> SDIFF s2 s1
1) "y"
2) "e"
3) "r"
注意以上s1和s2的操作顺序,顺序不同结果也会不同
集合间的运算在元素较多的情况下会比较耗时, 所以Redis提供了上面 ,集合间的运算在元素较多的情况下会比较耗时, 所以Redis提供了上面 destination key中 。
sinterstore destination key [key …]
suionstore destination key [key …]
sdiffstore destination key [key …]
集合类型比较典型的使用场景是标签(tag)
在集合类型的基础上,有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
1、二者都是有序的。
2、二者都可以获得某一范围的元素。
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存。
数据结构 | 是够允许重复元素 | 是否有序 | 有序实现方式 | 应用场景 |
---|---|---|---|---|
列表(list) | 是 | 是 | 索引下标 | 消息队列 |
集合(set) | 否 | 否 | 无 | 标签 |
有序集合(zset) | 是 | 是 | 分值 | 排行榜系统 |
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
ZADD key [NX|XX] [CH] [INCR] score member [score member …]
Redis3.2为zadd命令添加了nx、 xx、 ch、 incr四个选项:
nx: member必须不存在, 才可以设置成功, 用于添加。
xx: member必须存在, 才可以设置成功, 用于更新。
ch: 返回此次操作后, 有序集合元素和分数发生变化的个数
incr: 对score做增加, 相当于后面介绍的zincrby。
返回添加成员个数
127.0.0.1:6379> ZADD z1 80 zhangsan 81 lisi 82 wangwu
(integer) 3
ZCARD key
127.0.0.1:6379> ZCARD z1
(integer) 3
ZSCORE key member
127.0.0.1:6379> ZSCORE z1 zhangsan
"80"
ZRANK key member
127.0.0.1:6379> ZRANK z1 zhangsan
(integer) 0
127.0.0.1:6379> ZRANK z1 wangwu
(integer) 2
127.0.0.1:6379> ZRANK z1 lisi
(integer) 1
ZREM key member [member …]
127.0.0.1:6379> ZREM z1 zhangsan
(integer) 1
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]
zrange是从低到高返回, zrevrange反之
127.0.0.1:6379> ZRANGE z1 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
127.0.0.1:6379> ZREVRANGE z1 0 -1
1) "zhangsan"
2) "wangwu"
3) "lisi"
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"
ZINCRBY key increment member
127.0.0.1:6379> ZINCRBY z1 10 zhangsan
"109"
ZCOUNT key min max
127.0.0.1:6379> ZCOUNT z1 90 100
(integer) 0
127.0.0.1:6379> ZCOUNT z1 90 110
(integer) 1
127.0.0.1:6379> ZCOUNT z1 80 110
(integer) 3
ZREMRANGEBYSCORE key min max
删除分数为80-90之间的成员
127.0.0.1:6379> ZREMRANGEBYSCORE z1 80 90
(integer) 2
127.0.0.1:6379> ZRANGE z1 0 -1
1) "zhangsan"
等等
Redis从2.8版本后, 提供了一个新的命令scan, 它能有效的解决keys命令存在的问题。 和keys命令执行时会遍历所有键不同, scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题, 每次scan命令的时间复杂度是。
SCAN cursor [MATCH pattern] [COUNT count]
cursor是必需参数, 实际上cursor是一个游标, 第一次遍历从0开始, 每
次scan遍历完都会返回当前游标的值, 直到游标值为0, 表示遍历结束。
match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的
模式匹配很像
count number是可选参数, 它的作用是表明每次要遍历的键个数, 默认
值是10, 此参数可以适当增大
如以下演示
127.0.0.1:6379> MSET a a b b c c d d e e f f g g h h i i j j k k l l m m n n o o p p q q r r s s t t u u v v w w x x y y z z
OK
127.0.0.1:6379> keys *
1) "r"
2) "o"
3) "t"
4) "h"
5) "l"
6) "p"
7) "u"
8) "s"
9) "q"
10) "d"
11) "j"
12) "b"
13) "n"
14) "a"
15) "y"
16) "x"
17) "f"
18) "v"
19) "c"
20) "z"
21) "w"
22) "e"
23) "k"
24) "i"
25) "m"
26) "g"
127.0.0.1:6379> DBSIZE
(integer) 26
scan演示
127.0.0.1:6379> SCAN 0
1) "30"
2) 1) "r"
2) "y"
3) "p"
4) "k"
5) "o"
6) "f"
7) "s"
8) "i"
9) "m"
10) "j"
11) "b"
12) "n"
127.0.0.1:6379> SCAN 30
1) "27"
2) 1) "x"
2) "u"
3) "v"
4) "c"
5) "d"
6) "g"
7) "t"
8) "h"
9) "l"
10) "q"
127.0.0.1:6379> SCAN 27
1) "0"
2) 1) "z"
2) "w"
3) "e"
4) "a"
除了scan以外, Redis提供了面向哈希类型、 集合类型、 有序集合的扫描遍历命令, 解决诸如hgetall、 smembers、 zrange可能产生的阻塞问题, 对应的命令分别是hscan、 sscan、 zscan, 它们的用法和scan基本类似.
渐进式遍历可以有效的解决keys命令可能产生的阻塞问题, 但是scan并非完美无瑕, 如果在scan的过程中如果有键的变化(增加、 删除、 修改) , 那么遍历效果可能会碰到如下问题: 新增的键可能没有遍历到, 遍历出了重
复的键等情况, 也就是说scan并不能保证完整的遍历出来所有的键, 这些是我们在开发时需要考虑的。
微信公众号