Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
Redis教程
-> 第2篇:Redis 5大数据类型
1、第1篇:Redis概述和安装
2、第2篇:Redis 5大数据类型
3、第3篇:Redis的发布和订阅
4、第4篇:Redis新的3种数据类型
5、第5篇:Jedis操作Redis6
6、第6篇:SpringBoot2整合Redis
7、第7篇:Redis事务操作
8、第8篇:Redis持久化之RDB(Redis DataBase)
9、第9篇:Redis持久化之AOF(Append Only File)
10、第10篇:Redis主从复制
11、第11篇:Redis集群(Cluster)
12、第12篇:Redis应用问题解决(缓存穿透、击穿、雪崩、分布式锁)
上一篇:第1篇:Redis概述和安装
下一篇:第3篇:Redis的发布和订阅
<div style="display:none"></div> 这里说的数据类型是value的数据类型,key的类型都是字符串。 5种数据类型: - redis字符串(String) - redis列表(List) - redis集合(Set) - redis哈希表(Hash) - redis有序集合(Zset) 哪里去获取redis常用数据类型操作命令:<a href="http://redis.cn/commands.html" target="_blank">http://redis.cn/commands.html</a> ## 2.1、redis键(key) - keys *:查看当前库所有的key - exists key:判断某个key是否存在 - type key:查看你的key是什么类型 - del key:删除指定的key数据 - unlink key:根据value删除非阻塞删除,仅仅将keys从keyspace元数据中删除,真正的删除会在后续异步中操作。 - expire key 10:为指定的key设置有效期10秒 - ttl key:查看指定的key还有多少秒过期,-1:表示永不过期,-2:表示已过期 - select dbindex:切换数据库【0-15】,默认为0 - dbsize:查看当前数据库key的数量 - flushdb:清空当前库 - flushall:通杀全部库 ## 2.2、redis字符串(String) ### 2.2.1、简介 String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。 String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。 String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M ### 2.2.2、常用命令 #### set:添加键值对 ```shell 127.0.0.1:6379> set key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|XX] [GET] ``` - NX:当数据库中key不存在时,可以将key-value添加到数据库 - XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥 - EX:key的超时秒数 - PX:key的超时毫秒数,与EX互斥 - value中若包含空格、特殊字符,需用双引号包裹 #### get:获取值 ```shell get <key> ``` 示例 ```shell 127.0.0.1:6379> set name ready OK 127.0.0.1:6379> get name "ready" ``` #### apend:追加值 ```shell append <key> <value> ``` > 将给定的value追加到原值的末尾。 示例 ```shell 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:获取值的长度 ```shell strlen <key> ``` 示例 ```shell 127.0.0.1:6379> set name ready OK 127.0.0.1:6379> strlen name (integer) 5 ``` #### setnx:key不存在时,设置key的值 ```shell setnx <key> <value> ``` 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> setnx site "itsoku.com" #site不存在,返回1,表示设置成功 (integer) 1 127.0.0.1:6379> setnx site "itsoku.com" #再次通过setnx设置site,由于已经存在了,所以设置失败,返回0 (integer) 0 ``` #### incr:原子递增1 ```shell incr <key> ``` > 将key中存储的值增1,只能对数字值操作,如果key不存在,则会新建一个,值为1 示例 ```shell 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:原子递减1 ```shell decr <key> ``` > 将key中存储的值减1,只能对数字值操作,如果为空,新增值为-1 示例 ```shell 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:递增或者递减指定的数字 ```shell incrby/decrby <key> <步长> ``` > 将key中存储的数字值递增指定的步长,若key不存在,则相当于在原值为0的值上递增指定的步长。 示例 ```shell 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:同时设置多个key-value ```shell mset <key1> <value1> <key2> <value2> ... ``` 示例 ```shell 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:获取多个key对应的值 ```shell mget <key1> <key2> ... ``` 示例 ```shell 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:当多个key都不存在时,则设置成功 ```shell msetnx <key1> <value1> <key2> <value2> ... ``` > 原子性的,要么都成功,或者都失败。 示例 ```shell 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:获取值的范围,类似java中的substring ```shell getrange key start end ``` > 获取[start,end]返回为的字符串 示例 ```shell 127.0.0.1:6379> set k1 helloworld OK 127.0.0.1:6379> getrange k1 0 4 "hello" ``` #### setrange:覆盖指定位置的值 ```shell setrange <key> <起始位置> <value> ``` 示例 ```shell 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:设置键值&过期时间(秒) ```shell setex <key> <过期时间(秒)> <value> ``` 示例 ```shell 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:以新换旧,设置新值同时返回旧值 ```shell getset <key> <value> ``` ```shell 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) ``` ### 2.2.3、数据结构 String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构上类似于Java的ArrayList,采用分配冗余空间的方式来减少内存的频繁分配。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/001eb7c0-d8a8-4aff-b8a0-15091450cd30.png) 如图所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次会多扩容1M的空间。 要注意的是字符串最大长度为512M。 ## 2.3、redis列表(List) ### 2.3.1、简介 单键多值 redis列表是简单的字符串列表,按照插入顺序排序。 你可以添加一个元素到列表的头部(左边)或者尾部(右边)。 它的底层实际上是使用双向链表实现的,对两端的操作性能很高,通过索引下标操作中间节点性能会较差。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/52af4eb5-89f6-49f9-8b4e-d5a6e02ef1a3.png) ### 2.3.2、常用命令 #### lpush/rpush:从左边或者右边插入一个或多个值 ```shell lpush/rpush <key1> <value1> <key2> <value2> ... ``` 示例 ```shell 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:从列表左边获取指定范围内的值 ```shell lrange <key> <star> <stop> ``` > 返回列表 `key` 中指定区间内的元素,区间以偏移量 `start` 和 `stop` 指定。 > > 下标(index)参数 `start` 和 `stop` 都以 `0` 为底,也就是说,以 `0` 表示列表的第一个元素,以 `1` 表示列表的第二个元素,以此类推。 > > 你也可以使用负数下标,以 `-1` 表示列表的最后一个元素, `-2` 表示列表的倒数第二个元素,以此类推。 > > **返回值:** > > 一个列表,包含指定区间内的元素。 示例 ```shell 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:从左边或者右边弹出多个元素 ```shell lpop/rpop <key> <count> ``` > count:可以省略,默认值为1 > > lpop/rpop 操作之后,弹出来的值会从列表中删除 > > 值在键在,值光键亡。 示例 ```shell 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:从一个列表右边弹出一个元素放到另外一个列表中 ```shell rpoplpush source destination ``` > 从source的右边弹出一个元素放到destination列表的左边 示例 ```shell 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:获取指定索引位置的元素(从左到右) ``` lindex key index ``` > 返回列表 `key` 中,下标为 `index` 的元素。 > > 下标(index)参数 `start` 和 `stop` 都以 `0` 为底,也就是说,以 `0` 表示列表的第一个元素,以 `1` 表示列表的第二个元素,以此类推。 > > 你也可以使用负数下标,以 `-1` 表示列表的最后一个元素, `-2` 表示列表的倒数第二个元素,以此类推。 > > 如果 `key` 不是列表类型,返回一个错误。 > > **返回值:** > > 列表中下标为 `index` 的元素。 > > 如果 `index` 参数的值不在列表的区间范围内(out of range),返回 `nil` 示例 ```shell 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:获得列表长度 ```shell llen key ``` > 返回列表 `key` 的长度。 > > 如果 `key` 不存在,则 `key` 被解释为一个空列表,返回 `0` . > > 如果 `key` 不是列表类型,返回一个错误。 示例 ```shell 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:在某个值的前或者后面插入一个值 ```shell linsert <key> before|after <value> <newvalue> ``` > 将值 `newvalue ` 插入到列表 `key` 当中,位于值 `value`之前或之后。 > > 当 `value` 不存在于列表 `key` 时,不执行任何操作。 > > 当 `key` 不存在时, `key` 被视为空列表,不执行任何操作。 > > 如果 `key` 不是列表类型,返回一个错误。 > > **返回值:** > > 如果命令执行成功,返回插入操作完成之后,列表的长度。 > > 如果没有找到 `value` ,返回 `-1` 。 > > 如果 `key` 不存在或为空列表,返回 `0` 。 示例 ```shell 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:删除指定数量的某个元素 ```shell LREM key count value ``` > 根据参数 `count` 的值,移除列表中与参数 `value` 相等的元素。 > > `count` 的值可以是以下几种: > > - `count > 0` : 从表头开始向表尾搜索,移除与 `value` 相等的元素,数量为 `count` 。 > - `count < 0` : 从表尾开始向表头搜索,移除与 `value` 相等的元素,数量为 `count` 的绝对值。 > - `count = 0` : 移除表中所有与 `value` 相等的值。 > > **返回值:** > > 被移除元素的数量。 > > 因为不存在的 `key` 被视作空表(empty list),所以当 `key` 不存在时,总是返回 `0` 。 示例 ```shell 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:替换指定位置的值 ```shell lset <key> <index> <value> ``` > 将列表 `key` 下标为 `index` 的元素的值设置为 `value` 。 > > 当 `index` 参数超出范围,或对一个空列表( `key` 不存在)进行lset时,返回一个错误。 > > **返回值:** > > 操作成功返回 `ok` ,否则返回错误信息。 示例 ```shell 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 ``` ### 2.3.4、数据结构 List的数据结构为快速链表quickList 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。 它将所有的元素紧挨着一起存储,分配的是一块连续的内存。 当就比较多的时候才会改成quickList。 因为普通的链表需要的附加指针空间太大,会比较浪费空间,比如这个列表里存储的只是int类型的书,结构上还需要2个额外的指针prev和next。 ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/00c9419b-7315-416d-bc6c-ecd1085c9c25.png) redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用,这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。 ## 2.4、redis集合(Set) ### 2.4.1、简介 redis set对外提供的功与list类似,是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择。 redis的set是string类型的**无序集合**,他的底层实际是一个value为null的hash表,收益添加,删除,查找复杂度都是O(1)。 一个算法,如果时间复杂度是O(1),那么随着数据的增加,查找数据的时间不变,也就是不管数据多少,查找时间都是一样的。 ### 2.4.2、常用命令 #### sadd:添加一个或多个元素 ```shell sadd <key> <value1> <value2> ... ``` 示例 ```shell 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:取出所有元素 ```shell smembers <key> ``` 示例 ```shell 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:判断集合中是否有某个值 ```shell 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:返回集合中元素的个数 ```shell 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:删除多个元素 ```shell srem key member [member ...] ``` > 移除集合 `key` 中的一个或多个 `member` 元素,不存在的 `member` 元素会被忽略。 > > 当 `key` 不是集合类型,返回一个错误。 > > **返回值:** > > 被成功移除的元素的数量,不包括被忽略的元素。 示例 ```shell 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:随机弹出多个值 ```sh spop <key> <count> ``` > 随机从key集合中弹出count个元素,count默认值为1 > > **返回值:** > > 被移除的随机元素。 > > 当 `key` 不存在或 `key` 是空集时,返回 `nil` 示例 ```shell 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:随机获取多个元素,不会从集合中删除 ```shell srandmember <key> <count> ``` > 从key指定的集合中随机返回count个元素,count可以不指定,默认值是1。 > > **srandmember 和 spop的区别:** > > 都可以随机获取多个元素,srandmember 不会删除元素,而spop会删除元素。 > > **返回值:** > > 只提供 `key` 参数时,返回一个元素;如果集合为空,返回 `nil` 。 > > 如果提供了 `count` 参数,那么返回一个数组;如果集合为空,返回空数组。 示例 ```shell 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:将某个原创从一个集合移动到另一个集合 ```shell 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` 。 示例 ```shell 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:取多个集合的交集 ```shell sinter key [key ...] ``` 示例 ```shell 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:将多个集合的交集放到一个新的集合中 ```shell sinterstore destination key [key ...] ``` > 这个命令类似于`sinter`命令,但它将结果保存到 `destination` 集合,而不是简单地返回结果集。 > > **返回值:** > > 结果集中的成员数量。 #### sunion:取多个集合的并集,自动去重 ```shell sunion key [key ...] ``` 示例 ```shell 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" ``` #### sunionstore:将多个集合的并集放到一个新的集合中 ```shell sinterstore destination key [key ...] ``` > 这个命令类似于 sunion 命令,但它将结果保存到 `destination` 集合,而不是简单地返回结果集。 > > **返回值:** > > 结果集中的成员数量。 #### sdiff:取多个集合的差集 ```shell SDIFF key [key ...] ``` > 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 > > 不存在的 `key` 被视为空集。 示例 ```shell 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:将多个集合的差集放到一个新的集合中 ```shell sdiffstore destination key [key ...] ``` > 这个命令类似于 sdiff 命令,但它将结果保存到 `destination` 集合,而不是简单地返回结果集。 > > **返回值:** > > 结果集中的成员数量。 ### 3.4.3、数据结构 set数据结构是字典,字典是用hash表实现的。 Java中的HashSet的内部实现使用HashMap,只不过所有的value都指向同一个对象。 Redis的set结构也是一样的,它的内部也使用hash结构,所有的value都指向同一个内部值。 ## 2.5、redis哈希(Hash) ### 2.5.1、简介 Redis hash是一个键值对集合。 Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 类似于java里面的Map<String,Object> ### 2.5.2、常用命令 #### hset:设置多个field的值 ```shell hset key field value [field value ...] ``` > 将哈希表 `key` 中的域 `field` 的值设为 `value` 。 > > 如果 `key` 不存在,一个新的哈希表被创建并进行 hset 操作。 > > 如果域 `field` 已经存在于哈希表中,旧值将被覆盖。 > > **返回值:** > > 如果 `field` 是哈希表中的一个新建域,并且值设置成功,返回 `1` 。 > > 如果哈希表中域 `field` 已经存在且旧值已被新值覆盖,返回 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 ``` #### hget:获取指定filed的值 ```shell hget key field ``` 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hget user name #获取user中的name "ready" ``` #### hgetall:返回hash表所有的域和值 ```shell hgetall key ``` 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hgetall user #获取user所有信息 1) "name" 2) "ready" 3) "age" 4) "30" ``` #### hmset:和hset类似(已弃用) ```shell hmset key field value [field value ...] ``` #### hexists:判断给定的field是否存在,1:存在,0:不存在 ```shell hexists key field ``` > 查看哈希表 `key` 中,给定域 `field` 是否存在。 > > **返回值:** > > 如果哈希表含有给定域,返回 `1` 。 > > 如果哈希表不含有给定域,或 `key` 不存在,返回 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hexists user name #user中存在name域 (integer) 1 127.0.0.1:6379> hexists user address #user中不存在address域,返回0 (integer) 0 127.0.0.1:6379> hexists user1 address #user1这个key不存在,返回0 (integer) 0 ``` #### hkeys:列出所有的filed ```shell hkeys key ``` 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hkeys user #获取user中的所有filed 1) "name" 2) "age" ``` #### hvals:列出所有的value ```shell hvals key ``` 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hvals user #获取user中的所有filed的值列表 1) "ready" 2) "30" ``` #### hlen:返回filed的数量 ```shell HLEN key ``` > 返回哈希表 `key` 中域的数量。 > > **返回值:** > > 哈希表中域的数量。 > > 当 `key` 不存在时,返回 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #哈希表user中设置2个域:name和age,name的值为ready,age的值为30 (integer) 2 127.0.0.1:6379> hlen user (integer) 2 ``` #### hincrby:filed的值加上指定的增量 ```shell hincrby key field increment ``` > 为哈希表 key 中的域 field 的值加上增量 increment 。 > > 增量也可以为负数,相当于对给定域进行减法操作。 > > 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 > > 如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。 > > 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。 > > **返回值:** > > 执行 hincrby 命令之后,哈希表 `key` 中域 `field` 的值。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset siteInfo site itsoku.com pv 1000 #hash表siteInfo中有2个域:{site:"itsoku.com",pv:1000} (integer) 2 127.0.0.1:6379> hget siteInfo pv #获取siteInfo中pv的值 "1000" 127.0.0.1:6379> hincrby siteInfo pv 10 #siteInfo中的pv值增加10 (integer) 1010 127.0.0.1:6379> hget siteInfo pv #获取siteInfo中的pv "1010" 127.0.0.1:6379> hincrby siteInfo uv 500 #siteInfo中的uv值增加500,uv这个域不存在,则会先添加,然后再执行hincrby (integer) 500 ``` #### hsetnx:当filed不存在的时候,设置filed的值 ```shell hsetnx key field value ``` > 将哈希表 `key` 中的域 `field` 的值设置为 `value` ,当且仅当域 `field` 不存在。 > > 若域 `field` 已经存在,该操作无效。 > > 如果 `key` 不存在,一个新哈希表被创建并执行 hsetnx 命令。 > > **返回值:** > > 设置成功,返回 `1` 。 > > 如果给定域已经存在且没有操作被执行,返回 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> hset user name ready age 30 #创建user,包含2个域:name、age (integer) 2 127.0.0.1:6379> hsetnx user name tom #name已存在,设置失败,返回0 (integer) 0 127.0.0.1:6379> hget user name #name依旧是ready "ready" 127.0.0.1:6379> hsetnx user address shanghai #address不存在,设置成功 (integer) 1 127.0.0.1:6379> hget user address #输出address的值 "shanghai" ``` ### 2.5.3、数据结构 Hash类型对应的数据结构是2中:ziplist(压缩列表),hashtable(哈希表)。 当field-value长度较短个数较少时,使用ziplist,否则使用hashtable。 ## 2.6、redis有序集合zset(sorted set) ### 2.6.1、简介 redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。 集合的成员是唯一的,但是评分是可以重复的。 因为元素是有序的,所以你可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。 访问有序集合中的中间元素也是非常快的,因为你能够使用有序集合作为一个没有重复成员你的智能列表。 ### 2.6.2、常用命令 #### zadd:添加元素 ```shell zadd <key> <score1> <member1> <score2> <member2> ... ``` > 将一个或多个 `member` 元素及其 `score` 值加入到有序集 `key` 当中。 > > 如果某个 `member` 已经是有序集的成员,那么更新这个 `member` 的 `score` 值,并通过重新插入这个 `member` 元素,来保证该 `member` 在正确的位置上。 > > `score` 值可以是整数值或双精度浮点数。 > > 如果 `key` 不存在,则创建一个空的有序集并执行 zadd 操作。 > > 当 `key` 存在但不是有序集类型时,返回一个错误。 > > **返回值:** > > 被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topn 100 java 80 c 90 c++ 50 php 70 js #创建名称为topn的zset,添加了5个元素 (integer) 5 ``` #### zrange:score升序,获取指定索引范围的元素 ```shell zrange key start top [withscores] ``` > - 返回存储在有序集合`key`中的指定范围的元素。 返回的元素可以认为是按score从最低到最高排列,如果得分相同,将按字典排序。 > > - 下标参数 `start` 和 `stop` 都以 `0` 为底,也就是说,以 `0` 表示有序集第一个成员,以 `1` 表示有序集第二个成员,以此类推。 > > 你也可以使用负数下标,以 `-1` 表示最后一个成员, `-2` 表示倒数第二个成员,以此类推。 > > - zrange key 0 -1:可以获取所有元素 > > - withscores:让成员和它的 `score` 值一并返回,返回列表以 `value1,score1, ..., valueN,scoreN` 的格式表示 > > **可用版本:** > > \>= 1.2.0 > > **时间复杂度:** > > O(log(N)+M), `N` 为有序集的基数,而 `M` 为结果集的基数。 > > **返回值:** > > 指定区间内,带有 `score` 值(可选)的有序集成员的列表。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topn 100 java 80 c 90 c++ 50 php 70 js #创建名称为topn的zset,添加了5个元素 (integer) 5 127.0.0.1:6379> zrange topn 0 -1 #按score升序,返回topn中所有元素的值 1) "php" 2) "js" 3) "c" 4) "c++" 5) "java" 127.0.0.1:6379> zrange topn 0 -1 withscores #按score升序,返回所有元素的值以及score 1) "php" 2) "50" 3) "js" 4) "70" 5) "c" 6) "80" 7) "c++" 8) "90" 9) "java" 10) "100" 127.0.0.1:6379> zrange topn 2 4 #返回索引范围[2,4]内的3个元素 1) "c" 2) "c++" 3) "java" ``` #### zrevrange:score降序,获取指定索引范围的元素 ```shell zrevrange key start stop [WITHSCORES] ``` > - 返回存储在有序集合`key`中的指定范围的元素。 返回的元素可以认为是按score最高到最低排列, 如果得分相同,将按字典排序。 > > - 下标参数 `start` 和 `stop` 都以 `0` 为底,也就是说,以 `0` 表示有序集第一个成员,以 `1` 表示有序集第二个成员,以此类推。 > > 你也可以使用负数下标,以 `-1` 表示最后一个成员, `-2` 表示倒数第二个成员,以此类推。 > > - withscores:让成员和它的 `score` 值一并返回,返回列表以 `value1,score1, ..., valueN,scoreN` 的格式表示 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topn 100 java 80 c 90 c++ 50 php 70 js #创建名称为topn的zset,添加了5个元素 (integer) 5 127.0.0.1:6379> zrevrange topn 0 -1 #按照score降序获取所有元素 1) "java" 2) "c++" 3) "c" 4) "js" 5) "php" 127.0.0.1:6379> zrevrange topn 0 2 #按照score降序获取前3名 1) "java" 2) "c++" 3) "c" ``` #### zrangebyscore:按照score升序,返回指定score范围内的数据 ``` zrangebyscore key min max [WITHSCORES] [LIMIT offset count] ``` > 返回有序集 `key` 中,所有 `score` 值介于 `min` 和 `max` 之间(包括等于 `min` 或 `max` )的成员。有序集成员按 `score` 值递增(从小到大)次序排列。 > > 具有相同 `score` 值的成员按字典序来排列(该属性是有序集提供的,不需要额外的计算)。 > > 可选的 `LIMIT` 参数指定返回结果的数量及区间(就像SQL中的 `SELECT LIMIT offset, count` ),注意当 `offset` 很大时,定位 `offset` 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。 > > 可选的 `WITHSCORES` 参数决定结果集是单单返回有序集的成员,还是将有序集成员及其 `score` 值一起返回。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topn 100 java 80 c 90 c++ 50 php 70 js #创建名称为topn的zset,添加了5个元素 (integer) 5 127.0.0.1:6379> zrangebyscore topn 70 90 #score升序,获取score位于[70,90]区间中的元素值 1) "js" 2) "c" 3) "c++" 127.0.0.1:6379> zrangebyscore topn 70 90 withscores #score升序,获取score位于[70,90]区间中的元素值及score 1) "js" 2) "70" 3) "c" 4) "80" 5) "c++" 6) "90" 127.0.0.1:6379> zrangebyscore topn 70 90 withscores limit 1 2 #相当于:select value,score from topn集合 where score>=70 and score<=90 order by score asc limit 1,2 1) "c" 2) "80" 3) "c++" 4) "90" ``` #### zrevrangebyscore:按照score降序,返回指定score范围内的数据 ```shell zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count] ``` > 返回有序集 `key` 中, `score` 值介于 `max` 和 `min` 之间(默认包括等于 `max` 或 `min` )的所有的成员。有序集成员按 `score` 值递减(从大到小)的次序排列。 > > 具有相同 `score` 值的成员按字典序的逆序排列。 > > 除了成员按 `score` 值递减的次序排列这一点外, zrevrangebyscore 命令的其他方面和 zrangebyscore 命令一样。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topn 100 java 80 c 90 c++ 50 php 70 js #创建名称为topn的zset,添加了5个元素 (integer) 5 127.0.0.1:6379> zrevrangebyscore topn 100 90 #score降序,获取score位于[70,90]区间中的元素值 1) "java" 2) "c++" 127.0.0.1:6379> zrevrangebyscore topn 100 90 withscores #score降序,获取score位于[70,90]区间中的元素值及score 1) "java" 2) "100" 3) "c++" 4) "90" ``` #### zincrby:为指定元素的score加上指定的增量 ```shell 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` 值可以是整数值或双精度浮点数。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ #集合topx中添加3个元素:java、c、c++,对应的score分别是:90、70、80 (integer) 3 127.0.0.1:6379> zrevrange topx 0 -1 withscores #输出集合topx中的元素,包含score 1) "java" 2) "90" 3) "c++" 4) "80" 5) "c" 6) "70" 127.0.0.1:6379> zincrby topx 5 java #对topx中的元素java的score加5,变成95了 "95" 127.0.0.1:6379> zrevrange topx 0 -1 withscores # 输出集合元素,注意java的score是95了 1) "java" 2) "95" 3) "c++" 4) "80" 5) "c" 6) "70" ``` #### zrem:删除集合中多个元素 ```shell zrem key member [member ...] ``` > 移除有序集 `key` 中的一个或多个成员,不存在的成员将被忽略。 > > 当 `key` 存在但不是有序集类型时,返回一个错误。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ #集合topx中添加3个元素:java、c、c++,对应的score分别是:90、70、80 (integer) 3 127.0.0.1:6379> zrange topx 0 -1 #输出集合topx中所有元素 1) "c" 2) "c++" 3) "java" 127.0.0.1:6379> zrem topx c c++ #删除集合topx中的2个元素:c、c++ (integer) 2 127.0.0.1:6379> zrange topx 0 -1 #输出集合topx中所有元素 1) "java" ``` #### zremrangebyrank:根据索引范围删除元素 ```shell zremrangebyrank key start stop ``` > 移除有序集 `key` 中,指定排名(rank)区间内的所有成员。 > > 区间分别以下标参数 `start` 和 `stop` 指出,包含 `start` 和 `stop` 在内。 > > 下标参数 `start` 和 `stop` 都以 `0` 为底,也就是说,以 `0` 表示有序集第一个成员,以 `1` 表示有序集第二个成员,以此类推。 > > 你也可以使用负数下标,以 `-1` 表示最后一个成员, `-2` 表示倒数第二个成员,以此类推。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ #集合topx中添加3个元素:java、c、c++,对应的score分别是:90、70、80 (integer) 3 127.0.0.1:6379> zrange topx 0 -1 #输出集合topx中所有元素 1) "c" 2) "c++" 3) "java" 127.0.0.1:6379> zremrangebyrank topx 0 1 #删除索引范围[0,1]的数据 (integer) 2 127.0.0.1:6379> zrange topx 0 -1 #输出鞂topx中所有元素 1) "java" ``` #### zremrangebyscore:根据score的范围删除元素 ``` zremrangebyscore key min max ``` > 移除有序集 `key` 中,所有 `score` 值介于 `min` 和 `max` 之间(包括等于 `min` 或 `max` )的成员 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ 50 php #topx集合中添加4个元素 (integer) 4 127.0.0.1:6379> zrange topx 0 -1 withscores #输出topx中所有元素值、score 1) "php" 2) "50" 3) "c" 4) "70" 5) "c++" 6) "80" 7) "java" 8) "90" 127.0.0.1:6379> zremrangebyscore topx 70 80 #删除score位于[70,80]区间的元素 (integer) 2 127.0.0.1:6379> zrange topx 0 -1 withscores #输出剩下的元素 1) "php" 2) "50" 3) "java" 4) "90" ``` #### zcount:统计指定score范围内元素的个数 ```shell zcount key min max ``` > 返回有序集 `key` 中, `score` 值在 `min` 和 `max` 之间(默认包括 `score` 值等于 `min` 或 `max` )的成员的数量 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ 50 php #topx集合中添加4个元素 (integer) 4 127.0.0.1:6379> zcount topx 80 100 #统计score位于[80,100]区间中的元素个数 (integer) 2 ``` #### zrank:按照score升序,返回某个元素在集合中的排名 ```shell zrank key member ``` > 返回有序集 `key` 中成员 `member` 的排名。其中有序集成员按 `score` 值递增(从小到大)顺序排列。 > > 排名以 `0` 为底,也就是说, `score` 值最小的成员排名为 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ 50 php #topx集合中添加4个元素 (integer) 4 127.0.0.1:6379> zrank topx c #获取元素c的排名,返回1表示排名第2 (integer) 1 127.0.0.1:6379> zrange topx 0 -1 #输出集合中所有元素,看一下c的位置确实是2 1) "php" 2) "c" 3) "c++" 4) "java" ``` #### zrevrank:按照score降序,返回某个元素在集合中的排名 > 返回有序集 `key` 中成员 `member` 的排名。其中有序集成员按 `score` 值递减(从大到小)排序。 > > 排名以 `0` 为底,也就是说, `score` 值最大的成员排名为 `0` 。 示例 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ 50 php #topx集合中添加4个元素 (integer) 4 127.0.0.1:6379> zrange topx 0 -1 1) "php" 2) "c" 3) "c++" 4) "java" 127.0.0.1:6379> zrevrank topx java #score降序,得到java的排名,排在第1位 (integer) 0 ``` #### zscore:返回集合中指定元素的score ```shell zscore key member ``` > 返回有序集 `key` 中,成员 `member` 的 `score` 值。 > > 如果 `member` 元素不是有序集 `key` 的成员,或 `key` 不存在,返回 `nil` 。 ```shell 127.0.0.1:6379> flushdb #清空db,方便测试 OK 127.0.0.1:6379> zadd topx 90 java 70 c 80 c++ 50 php #topx集合中添加4个元素 (integer) 4 127.0.0.1:6379> zrange topx 0 -1 #输出topx集合所有元素 1) "php" 2) "c" 3) "c++" 4) "java" 127.0.0.1:6379> zscore topx java #获取集合topx中java的score "90" ``` ### 2.6.3、数据结构 SortedSet(zset)是redis提供的一个非常特别的数据结构,内部使用到了2种数据结构。 **1、hash表** 类似于java中的Map<String,score>,key为集合中的元素,value为元素对应的score,可以用来快速定位元素定义的score,时间复杂度为O(1) **2、跳表** 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入、删除、查找的复杂度均为O(logN)。 类似java中的ConcurrentSkipListSet,根据score的值排序后生成的一个跳表,可以快速按照位置的顺序或者score的顺序查询元素。 这里我们来看一下跳表的原理: 首先从考虑一个有序表开始: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/cf5daf4e-d19b-4d4b-8f81-60eab04e4065.jpg) 从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数为 2 + 4 + 6 = 12 次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/f3374da3-fd0e-47ed-b95e-fee2a8cbf7fb.jpg) 这里我们把 < 14, 34, 50, 72 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/249/7b11af99-8bcd-40f9-a778-3fb77f7fb6b9.jpg) 这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。 <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container