戳上方蓝字“可为编程” 点击右上角选择“设为星标”,好文不错过!
上回我们介绍了Redis的部署及简单的原理性介绍,今天总结一下Redis的数据类型以及操作命令包括String、List、Set,下一篇为Hash和ZSet,喜欢的记得点赞收藏。
概述
这里说的数据类型是value的数据类型,key的类型都是字符串。
5种数据类型:
哪里去获取redis常用数据类型操作命令:http://redis.cn/commands.html
这里我采用的Docker容器化部署Redis,后面会单独出一篇文章详细介绍容器化Redis的部署和操作文章。
首先连接并登录Redis,执行以下命令:
redis-cli -h host -p port -a password
redis-cli -h 192.168.30.198 -p 6379 -a 1234 //这是我本地IP
root@391f6fd11521:/data# redis-cli -h 192.168.30.198 -p 6379 -a 1234
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.30.198:6379>
提示我们直接使用密码形式是不安全的,直接忽略即可,因为我们一般操作Redis都是在程序中采用,将密码配置在项目的配置文件当中,诚然在实际的生产环境中不能将Redis密码设置的这么简单,因为很可能被不法分子将你的Redis当成肉鸡,攻击者可以通过向Redis发送大量的请求,导致其负载过高,进而使Redis崩溃。
Redis通用操作命令
通用Redis操作命令
192.168.30.198:6379> exists kewei
(integer) 0
192.168.30.198:6379> set kewei keweibiancheng
OK
192.168.30.198:6379> exists kewei
(integer) 1
192.168.30.198:6379> type kewei
string
192.168.30.198:6379>
192.168.30.198:6379> unlink kewei
(integer) 1
192.168.30.198:6379> get kewei
(nil)
192.168.30.198:6379>
192.168.30.198:6379> set kewei keweibiancheng
OK
192.168.30.198:6379> ttl kewei
(integer) -1
192.168.30.198:6379> ttl kewei
(integer) -1
192.168.30.198:6379> expire kewei 10
(integer) 1
192.168.30.198:6379> ttl kewei
(integer) 7
192.168.30.198:6379> ttl kewei
(integer) 6
192.168.30.198:6379> ttl kewei
(integer) 5
192.168.30.198:6379> ttl kewei
(integer) 4
192.168.30.198:6379> ttl kewei
(integer) 3
192.168.30.198:6379> ttl kewei
(integer) 2
192.168.30.198:6379> ttl kewei
(integer) 1
192.168.30.198:6379> ttl kewei
(integer) 0
192.168.30.198:6379> ttl kewei
(integer) -2
192.168.30.198:6379> select 6
OK
192.168.30.198:6379[6]>
192.168.30.198:6379> dbsize
(integer) 3532
192.168.30.198:6379>
上面这俩就不演示了,不然数据都没了
Redis字符串(String)
String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。同时String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M大小的字符串。
127.0.0.1:6379>set key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL][NX|XX][GET]
get<key>
示例
127.0.0.1:6379>set name kewei
OK
127.0.0.1:6379>get name
"kewei"
append <key><value>
将给定的value追加到原值的末尾。
示例
127.0.0.1:6379>set k1 hello
OK
127.0.0.1:6379> append k1 " world"
(integer)11
127.0.0.1:6379>get k1
"hello world"
strlen <key>
示例
127.0.0.1:6379>set name ready
OK
127.0.0.1:6379> strlen name
(integer)5
setnx <key><value>
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> setnx site "kewei"#kewei不存在,返回1,表示设置成功
(integer)1
127.0.0.1:6379> setnx kewei "可为编程"
(integer) 1
127.0.0.1:6379> get kewei
"\xe5\x8f\xaf\xe4\xb8\xba\xe7\xbc\x96\xe7\xa8\x8b"
127.0.0.1:6379>
127.0.0.1:6379> setnx kewei "可为编程"#再次通过setnx设置kewei,由于已经存在了,所以设置失败,返回0
(integer)0
incr <key>
将key中存储的值增1,只能对数字值操作,如果key不存在,则会新建一个,值为1,注意我们设置自增的key的value需是Integet数值类型,小数和字符串都不可以设置自增。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379>set age 30#age值为30
OK
127.0.0.1:6379> incr age #age增加1,返回31
(integer)31
127.0.0.1:6379>get age #获取age的值
"31"
127.0.0.1:6379> incr salary #salary不存在,自动创建一个,值为1
(integer)1
127.0.0.1:6379>get salary #获取salary的值
"1"
decr <key>
将key中存储的值减1,只能对数字值操作,如果为空,新增值为-1
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379>set age 30#age值为30
OK
127.0.0.1:6379> decr age #age递减1,返回29
(integer)29
127.0.0.1:6379>get age #获取age的值
"29"
127.0.0.1:6379> decr salary #salary不存在,自动创建一个,值为-1
(integer)-1
127.0.0.1:6379>get salary #获取salary
"-1"
incrby/decrby <key><步长>
将key中存储的数字值递增指定的步长,若key不存在,则相当于在原值为0的值上递增指定的步长。
示例
127.0.0.1:6379>set salary 10000#设置salary为10000
OK
127.0.0.1:6379> incrby salary 5000#salary添加5000,返回15000
(integer)15000
127.0.0.1:6379>get salary #获取salary
"15000"
127.0.0.1:6379> decrby salary 800#salary减去800,返回14200
(integer)14200
127.0.0.1:6379>get salary #获取salary
"14200"
mset <key1><value1><key2><value2>...
示例
127.0.0.1:6379> mset name ready age 30
OK
127.0.0.1:6379>get name
"ready"
127.0.0.1:6379>get age
"30"
mget <key1><key2>...
示例
127.0.0.1:6379> mset name ready age 30#同时设置name和age
OK
127.0.0.1:6379> mget name age #同时读取name和age的值
1)"ready"
2)"30"
msetnx <key1><value1><key2><value2>...
原子性的,要么都成功,或者都失败。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379>set k1 v1 #设置k1
OK
127.0.0.1:6379> msetnx k1 v1 k2 v2 #当k1和k2都不存在的时候,同时设置k1和k2,由于k1已存在,所以这个操作失败
(integer)0
127.0.0.1:6379> mget k1 k2 #获取k1、k2,k2不存在
1)"v1"
2)(nil)
127.0.0.1:6379> msetnx k2 v2 k3 v3 #当k2、h3都不存在的时候,同时设置k2和k3,设置成功
(integer)1
127.0.0.1:6379> mget k1 k2 k3 #获取k1、k2、k3的值
1)"v1"
2)"v2"
3)"v3"
getrange key start end
获取[start,end]返回为的字符串
示例
1、127.0.0.1:6379> set kewei keweibiancheng
2、OK
3、127.0.0.1:6379>
4、127.0.0.1:6379> get kewei 0 1
5、"ke"
setrange <key><起始位置><value>
示例
127.0.0.1:6379>set k1 helloworld
OK
127.0.0.1:6379>get k1
"helloworld"
127.0.0.1:6379> setrange k1 1 java
(integer)10
127.0.0.1:6379>get k1
"hjavaworld"
setex <key><过期时间(秒)><value>
示例
127.0.0.1:6379> setex k1 100 v1 #设置k1的值为v1,有效期100秒
OK
127.0.0.1:6379>get k1 #获取k1的值
"v1"
127.0.0.1:6379> ttl k1 #获取k1还有多少秒失效
(integer)96
getset <key><value>
127.0.0.1:6379>set name ready #设置name为ready
OK
127.0.0.1:6379> getset name tom #设置name为tom,返回name的旧值
"ready"
127.0.0.1:6379> getset age 30#设置age为30,age未设置过,返回age的旧值为null
(nil)
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构上类似于Java的ArrayList,采用分配冗余空间的方式来减少内存的频繁分配。
如图所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次会多扩容1M的空间。我们要注意的是字符串最大长度为512M。
Redis列表(List)
Redis的列表可以说是单键多值,其只是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。其实它的底层实际上是使用双向链表实现的,对两端的操作性能很高,通过索引下标操作中间节点性能会较差。后面我会针对数据结构再出一系列文章,再去讨论双向链表的原理。
lpush/rpush <key1> <value1> <key2> <value2> ...
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> rpush name java spring "springboot" "spring cloud" #列表name的右边插入4个元素
(integer) 4
127.0.0.1:6379> lrange name 1 2 #从左边取出索引位于[1,2]范围内的元素
1) "spring"
2) "springboot"
lrange <key> <star> <stop>
返回列表
key
中指定区间内的元素,区间以偏移量start
和stop
指定。 下标(index)参数start
和stop
都以0
为底,也就是说,以0
表示列表的第一个元素,以1
表示列表的第二个元素,以此类推。 你也可以使用负数下标,以-1
表示列表的最后一个元素,-2
表示列表的倒数第二个元素,以此类推。 返回值: 一个列表,包含指定区间内的元素。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush course java c c++ php js nodejs #course集合的右边插入6个元素
(integer) 6
127.0.0.1:6379> lrange course 0 -1 #取出course集合中所有元素
1) "java"
2) "c"
3) "c++"
4) "php"
5) "js"
6) "nodejs"
127.0.0.1:6379> lrange course 1 3 #获取course集合索引[1,3]范围内的元素
1) "c"
2) "c++"
3) "php"
lpop/rpop <key> <count>
count:可以省略,默认值为1 lpop/rpop 操作之后,弹出来的值会从列表中删除 值在键在,值光键亡。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush course java c++ php js node js #集合course右边加入6个元素
(integer) 6
127.0.0.1:6379> lpop course #从左边弹出1个元素
"java"
127.0.0.1:6379> rpop course 2 #从右边弹出2个元素
1) "js"
2) "node"
rpoplpush source destination
从source的右边弹出一个元素放到destination列表的左边
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush k1 1 2 3 #列表k1的右边添加3个元素[1,2,3]
(integer) 3
127.0.0.1:6379> lrange k1 0 -1 #从左到右输出k1列表中的元素
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> rpush k2 4 5 6 #列表k2的右边添加3个元素[4,5,6]
(integer) 3
127.0.0.1:6379> lrange k2 0 -1 #从左到右输出k2列表中的元素
1) "4"
2) "5"
3) "6"
127.0.0.1:6379> rpoplpush k1 k2 #从k1的右边弹出一个元素放到k2的左边
"3"
127.0.0.1:6379> lrange k1 0 -1 #k1中剩下2个元素了
1) "1"
2) "2"
127.0.0.1:6379> lrange k2 0 -1 #k2中变成4个元素了
1) "3"
2) "4"
3) "5"
4) "6"
lindex key index
这样就返回列表
key
中,下标为index
的元素。下标(index)参数start
和stop
都以0
为底,以0
表示列表的第一个元素,以1
表示列表的第二个元素,以此类推,这个和java中的list也是一样的。你也可以使用负数下标,以-1
表示列表的最后一个元素,-2
表示列表的倒数第二个元素,以此类推。但是需要注意的是如果key
不是列表类型,返回一个错误。 返回值: 列表中下标为index
的元素。如果index
参数的值不在列表的区间范围内的话回报超出范围异常(out of range),返回nil。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush course java c c++ php #列表course中放入4个元素
(integer) 4
127.0.0.1:6379> lindex course 2 #返回索引位置2的元素
"c++"
127.0.0.1:6379> lindex course 200 #返回索引位置200的元素,没有
(nil)
127.0.0.1:6379> lindex course -1 #返回最后一个元素
"php"
llen key
返回列表
key
的长度。如果key
不存在,则key
被解释为一个空列表,返回0
。如果key
不是列表类型,返回一个错误。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush name ready tom jack
(integer) 3
127.0.0.1:6379> llen name
(integer) 3
linsert <key> before|after <value> <newvalue>
将值
newvalue
插入到列表key
当中,位于值value
之前或之后,当value
不存在于列表key
时,不执行任何操作。但是当key
不存在时,key
被视为空列表,不执行任何操作指令。如果key
不是列表类型,返回一个错误。 返回值: 如果命令执行成功,返回插入操作完成之后,列表的长度。 如果没有找到value
,返回-1
。 如果key
不存在或为空列表,返回0
。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> rpush name ready tom jack #列表name中添加3个元素
(integer) 3
127.0.0.1:6379> lrange name 0 -1 #name列表所有元素
1) "ready"
2) "tom"
3) "jack"
127.0.0.1:6379> linsert name before tom lily #tom前面添加lily
(integer) 4
127.0.0.1:6379> lrange name 0 -1 #name列表所有元素
1) "ready"
2) "lily"
3) "tom"
4) "jack"
127.0.0.1:6379> linsert name before xxx lucy # 在元素xxx前面插入lucy,由于xxx元素不存在,插入失败,返回-1
(integer) -1
127.0.0.1:6379> lrange name 0 -1
1) "ready"
2) "lily"
3) "tom"
4) "jack"
LREM key count value
根据参数
count
的值,移除列表中与参数value
相等的元素。count
的值可以是以下几种:
count > 0
: 从表头开始向表尾搜索,移除与 value
相等的元素,数量为 count
。count < 0
: 从表尾开始向表头搜索,移除与 value
相等的元素,数量为 count
的绝对值。count = 0
: 移除表中所有与 value
相等的值。返回值:
被移除元素的数量。
因为不存在的 key
被视作空表(empty list),所以当 key
不存在时,总是返回 0
。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> rpush k1 v1 v2 v3 v2 v2 v1 #k1列表中插入6个元素
(integer) 6
127.0.0.1:6379> lrange k1 0 -1 #输出k1集合中所有元素
1) "v1"
2) "v2"
3) "v3"
4) "v2"
5) "v2"
6) "v1"
127.0.0.1:6379> lrem k1 2 v2 #k1集合中从左边删除2个v2
(integer) 2
127.0.0.1:6379> lrange k1 0 -1 #输出列表,列表中还有1个v2,前面2个v2干掉了
1) "v1"
2) "v3"
3) "v2"
4) "v1"
lset <key> <index> <value>
将列表
key
下标为index
的元素的值设置为value
。当index
参数超出范围,或对一个空列表(key
不存在)进行lset时,返回一个错误。 返回值: 操作成功返回ok
,否则返回错误信息。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> rpush name tom jack ready #name集合中放入3个元素
(integer) 3
127.0.0.1:6379> lrange name 0 -1 #输出name集合元素
1) "tom"
2) "jack"
3) "ready"
127.0.0.1:6379> lset name 1 lily #将name集合中第2个元素替换为liy
OK
127.0.0.1:6379> lrange name 0 -1 #输出name集合元素
1) "tom"
2) "lily"
3) "ready"
127.0.0.1:6379> lset name 10 lily #索引超出范围,报错
(error) ERR index out of range
127.0.0.1:6379> lset course 1 java #course集合不存在,报错
(error) ERR no such key
List的数据结构为快速链表quickList,快速链表是一个双向链表,首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存,这样节省空间。当存储的数据比较多的时候才会改成quickList。
redis3 .2版本之前,List类型数据使用的底层数据结构是压缩列表(ziplist)或双向链表(linkedlist),当列表元素个数比较少并且每个元素占用空间比较小时使用压缩列表;当列表元素个数比较多或者某个元素占用空间比较大的时使用双向链表,redis3 .2版本开始,List类型数据使用的底层数据结构是快速列表,快速列表是以压缩列表为节点的双向链表,将双向链表按段切分,每一段使用压缩列表进行内存的连续存储,多个压缩列表通过prev和next指针组成的双向链。其实快速列表实质就是压缩列表+双向链表的组合,结合了压缩列表和双向链表各自的优点,因为普通的链表需要的附加指针空间太大,会比较浪费空间,比如这个列表里存储的只是int类型的数据,结构上还需要2个额外的指针prev和next。
Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用,这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
Redis集合(Set)
在Redis中的Set对外提供的功能和list很是类似,也是一个列表的功能,主要不同在于set是可以自动去重的,当需要存储一个不希望出现重复数据的时候,直接使用Set会事半功倍。Redis的set是String类型的无序集合,他的底层实际是一个value为null的hash表,所以其时间复杂度在添加、删除、查找都是O(1)。那就很香了,数据增加查找的时间还是不变的,这多香。
sadd <key> <value1> <value2> ...
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd k1 v1 v2 v1 v3 v2 #k1中放入5个元素,会自动去重,成功插入3个
(integer) 3
smembers <key>
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd k1 v1 v2 v1 v3 v2
(integer) 3
127.0.0.1:6379> smembers k1
1) "v2"
2) "v1"
3) "v3"
sismember <key> <value>
判断集合key中是否包含元素value,1:有,0:没有
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd k1 v1 v2 v1 v3 v2 #k1集合中成功放入3个元素[v1,v2,v3]
(integer) 3
127.0.0.1:6379> sismember k1 v1 #判断k1中是否包含v1,1:有
(integer) 1
127.0.0.1:6379> sismember k1 v5 #判断k1中是否包含v5,0:无
(integer) 0
scard <key>
返回集合
key
的基数(集合中元素的数量) 返回值: 集合的基数。 当key
不存在时,返回0
。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd k1 v1 v2 v1 v3 v2
(integer) 3
127.0.0.1:6379> scard k1
(integer) 3
srem key member [member ...]
移除集合
key
中的一个或多个member
元素,不存在的member
元素会被忽略。 当key
不是集合类型,返回一个错误。 返回值: 被成功移除的元素的数量,不包括被忽略的元素。
示例
127.0.0.1:6379> flushdb #清空db,方测试
OK
127.0.0.1:6379> sadd course java c c++ python #集合course中添加4个元素
(integer) 4
127.0.0.1:6379> smembers course #获取course集合所有元素
1) "python"
2) "java"
3) "c++"
4) "c"
127.0.0.1:6379> srem course java c #删除course集合中的java和c
(integer) 2
127.0.0.1:6379> smembers course #获取course集合所有元素,剩下2个了
1) "python"
2) "c++"
spop <key> <count>
随机从key集合中弹出count个元素,count默认值为1 返回值: 被移除的随机元素。 当
key
不存在或key
是空集时,返回nil
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd course java c c++ python #course集合中添加4个元素
(integer) 4
127.0.0.1:6379> smembers course #获取course集合中所有元素
1) "python"
2) "java"
3) "c++"
4) "c"
127.0.0.1:6379> spop course #随机弹出1个元素,被弹出的元素会被删除
"c++"
127.0.0.1:6379> spop course 2 #随机弹出2个元素
1) "java"
2) "python"
127.0.0.1:6379> smembers course #输出剩下的元素
1) "c"
srandmember <key> <count>
从key指定的集合中随机返回count个元素,count可以不指定,默认值是1。 srandmember 和 spop的区别: 都可以随机获取多个元素,srandmember 不会删除元素,而spop会删除元素。 返回值: 只提供
key
参数时,返回一个元素;如果集合为空,返回nil
。 如果提供了count
参数,那么返回一个数组;如果集合为空,返回空数组。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> sadd course java c c++ python #course中放入5个元素
(integer) 4
127.0.0.1:6379> smembers course #输出course集合中所有元素
1) "python"
2) "java"
3) "c++"
4) "c"
127.0.0.1:6379> srandmember course 3 #随机获取3个元素,元素并不会被删除
1) "python"
2) "c++"
3) "c"
127.0.0.1:6379> smembers course #输出course集合中所有元素,元素个数未变
1) "python"
2) "java"
3) "c++"
4) "c"
smove <source> <destination> member
将
member
元素从source
集合移动到destination
集合。 smove 是原子性操作。 如果source
集合不存在或不包含指定的member
元素,则 smove 命令不执行任何操作,仅返回0
。否则,member
元素从source
集合中被移除,并添加到destination
集合中去。 当destination
集合已经包含member
元素时,smove 命令只是简单地将source
集合中的member
元素删除。 当source
或destination
不是集合类型时,返回一个错误。 返回值: 如果member
元素被成功移除,返回1
。 如果member
元素不是source
集合的成员,并且没有任何操作对destination
集合执行,那么返回0
。
示例
127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> sadd course1 java php js #集合course1中放入3个元素[java,php,js]
(integer) 3
127.0.0.1:6379> sadd course2 c c++ #集合course2中放入2个元素[c,c++]
(integer) 2
127.0.0.1:6379> smove course1 course2 js #将course1中的js移动到course2
(integer) 1
127.0.0.1:6379> smembers course1 #输出course1中的元素
1) "java"
2) "php"
127.0.0.1:6379> smembers course2 #输出course2中的元素
1) "js"
2) "c++"
3) "c"
sinter key [key ...]
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd course1 java php js #集合course1:[java,php,js]
(integer) 3
127.0.0.1:6379> sadd course2 c c++ js #集合course2:[c,c++,js]
(integer) 3
127.0.0.1:6379> sadd course3 js html #集合course3:[js,html]
(integer) 2
127.0.0.1:6379> sinter course1 course2 course3 #返回三个集合的交集,只有:[js]
1) "js"
sinterstore destination key [key ...]
这个命令类似于
sinter
命令,但它将结果保存到destination
集合,而不是简单地返回结果集。 返回值: 结果集中的成员数量。
sunion key [key ...]
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd course1 java php js #集合course1:[java,php,js]
(integer) 3
127.0.0.1:6379> sadd course2 c c++ js #集合course2:[c,c++,js]
(integer) 3
127.0.0.1:6379> sadd course3 js html #集合course3:[js,html]
(integer) 2
127.0.0.1:6379> sunion course1 course2 course3 #返回3个集合的并集,会自动去重
1) "php"
2) "js"
3) "java"
4) "html"
5) "c++"
6) "c"
sinterstore destination key [key ...]
这个命令类似于 sunion 命令,但它将结果保存到
destination
集合,而不是简单地返回结果集。 返回值: 结果集中的成员数量。
SDIFF key [key ...]
返回一个集合的全部成员,该集合是所有给定集合之间的差集。 不存在的
key
被视为空集。
示例
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd course1 java php js #集合course1:[java,php,js]
(integer) 3
127.0.0.1:6379> sadd course2 c c++ js #集合course2:[c,c++,js]
(integer) 3
127.0.0.1:6379> sadd course3 js html #集合course3:[js,html]
(integer) 2
127.0.0.1:6379> sdiff course1 course2 course3 #返回course1中有的而course2和course3中都没有的元素
1) "java"
2) "php"
sdiffstore destination key [key ...]
这个命令类似于 sdiff 命令,但它将结果保存到
destination
集合,而不是简单地返回结果集。 返回值: 结果集中的成员数量。
又到了数据结构环节,Redis中的Set数据结构是字典,字典是用hash表实现的。Java中的HashSet的内部实现使用HashMap,实际上,HashSet 使用了 HashMap 的 key 来存储元素,而并没有使用 HashMap 的 value 来存储数据。HashSet 中,所有的元素都是作为 HashMap 的 key 来存储的,而 HashMap 的 value 则是一个固定的对象,通常称为“存在性标记”(presence bit)。这个存在性标记对象仅仅用于表示 key 存在于 HashSet 中,并不存储实际的元素数据。Redis的set结构也是一样的,它的内部也使用hash结构。