哪五大常用类型?
- 字符串(String)
- 列表(List)
- 集合(Set)
- 哈希表(Hash)
- 有序集合(SortedSet)
五大常用类型介绍
1. String(字符串)
- String 是 Redis最基本的类型,它是二进制安全的。
- 二进制安全:意味着Redis可以包含任何数据。例如:JPG图片或者其他序列化的对象。
- 一个字符串值最多可以是512M。
- 字符串的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
- 如图所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间;如果超过1M,扩容时一次只会多扩1M的空间。
2. List(列表)
- 列表是单键多值的结构。
- Redis列表是最简单的字符串列表,按照插入顺序排列。
- 我们可以从列表的头部(左边)或者尾部(右边)添加元素到列表中。
- 列表的底层实际是个双向链表,对两端的操作性能高,通过索引下标操作中间的节点性能会较差。
列表的数据结构为快速链表(quicklist)。
- 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist(压缩列表)。
- ziplist将所有的元素紧挨着一起存储,分配的是一块连续的内存。
- 当列表存储的数据量比较多的时候才会形成quicklist结构。Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
- 普通的链表需要的附加指针空间太大,会比较浪费空间。例如:这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。
3. Set(集合)
- Redis Set对外提供的功能与List类似,是一个列表的功能,特殊之处在于Set是可以==自动排重(排除重复数据)==的。当我们需要存储一个列表数据,又不希望出现重复数据时,它是一个很好的选择。
- Set提供了判断某个成员是否在一个Set集合内得重要接口,这也是List所不能提供的。
- Set是String类型的无序集合。它底层其实是一个value为null的hash表,所有添加、删除、查找的复杂度都是O(1)。
- 一个算法,随着数据的增加,执行事件的长短,如果是O(1),数据增加,查找数据的事时间不变。
- Set数据结构是dict字典,字典是用哈希表实现的。
- Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。
- Redis的Set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
4. Hash(哈希表)
- Hash表是一个键值对集合。它是一个String类型的field 和 value 的映射表,hash特别适合用于存储对象。类似于Java里面的Map<String, Object>。
用户ID为查找的key,存储的value用户对象包括姓名、年龄、生日等信息,如果用普通的key/value结构来存储。以下有3种存储方式
方式一:
每次修改用户的某个属性需要,先反序列化改好后再序列化回去。开销较大。
方式二:
用户ID数据冗余。
方式三:
通过==key(用户ID)+ field(属性标签)==就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。
Hash类型对应的数据结构有两种:ziplist(压缩列表)、hashtable(哈希表)。
当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
5. SortedSet(有序集合)
- Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
- 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了。
- 因为元素是有序的,所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
- 访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
SorteSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<string, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
- hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
- 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
5.1 跳跃表
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。
但是它们有如下的缺点:
-
数组不便元素的插入、删除。
-
平衡树或红黑树虽然效率高但结构复杂。
-
链表查询需要遍历所有效率低。
Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
对比有序链表和跳跃表,从链表中查询出51
- 有序链表
要查找值为51的元素,需要从第一个依次查找,比较才能找到。共需要6次比较。
- 跳跃表
从第2层开始,1节点比51节点小,向后比较。
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层。
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
在第0层,51节点为要查找的节点,节点被找到,共查找4次。
从此可以看出跳跃表比有序链表效率高。
常用操作命令
1. String(字符串)
1.1 设置值
命令:SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX second
:设置键的过期时间为second
秒。PX millisecond
:设置键的过期时间为millisecond
毫秒。NX
:只在键不存在时,才对键进行设置操作。XX
:只在键已经存在时,才对键进行设置操作。
1.2 查询值
命令:get key
注:若获取到字符串,则返回字符串值,否则返回nil(特殊值)。
1.3 追加指定字符串到原字符串末尾
命令:append key value
注:若原字符串不存在,则为新建设置值。
1.4 获取字符串值长度
命令:strlen key
1.5 设置值(当设置的key原来不存在时)
命令:setnx key value
注:将key的值设为value,当且仅当key不存在。若给定的key已经存在,则该命令不做任何动作。
1.6 自增(加 1)
命令:incr key
注:如果key不存在,那么key的值会先被初始化为0,然后再执行INCR操作。本操作的值限制在64位有符号数字表示之内。
1.7 自减(减 1)
命令:decr key
注:限制条件等同于自增。
1.8 指定步长自增或自减
命令:incrby / decrby key 步长
1.9 同时设置多个值
命令:mset key1 value1 key2 value2 …
注:有一个值失败了,则都会失败。
1.10 同时获取多个值
命令:mget key1 key2 …
1.11 key不存在时,同时设置多个值
命令:msetnx key1 value1 key2 value2 …
若其中一个值没设置成功,其他值都设置不成功。
1.12 获取指定起止位置的字符串(截取字符串)
命令:getrange key 起始位置 结束位置
注:下标从0开始,截取的字符串包含起始位置和结束位置。
1.13 覆盖指定偏移索引后的值
命令:setrange key 偏移值 value
注:从偏移位置开始(包含偏移位置)设置新的值。
1.14 设置值(给定过期时间)
命令:setex key 过期时间(秒) value
命令:psetex key 过期时间(毫秒)value
注:不推荐使用。后期可能会被官方移除。
原子操作描述
所谓原子操作是指不会被线程调度机制打断的操作。
这种操作一旦开始,就一直运行到结束,中间不会有任何context switch(切换到另外一个线程)。
- 在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间。
- 在多线程中,不能被其他进程(线程)打断的操作就叫原子操作。
Redis单命令的原子性主要得益于redis的单线程。所以自增自减操作是原子性的。
2. List(列表)
2.1 从列表头/尾插入一个或多个值
命令:lpush/rpush key value1 value2 value3…
- lpush表示从列表左侧(头)插入值
- rpush表示从列表右侧(尾)插入值
2.2 从列表头/尾吐出一个值
命令:lpop/rpop key
- lpop表示从列表左侧(头)吐出一个值
- rpop表示从列表右侧(尾)吐出一个值
注:值存在,键存在。值都没了,键也没了。
2.3 按照索引下标范围获得元素(从左到右)
命令:lrange key start stop
注:索引下标从0开始。若索引从 0 到 -1表示取出所有元素。
注:此时不是取出元素,列表大小不会变化。
2.4 按照索引下标值获得元素(从左到右)
命令:lindex key index
2.5 获取列表长度
命令:llen key
2.6 在指定value值的前面/后面插入newvalue值
命令:linsert key BEFORE/AFTER value newvalue
2.7 从左边删除n个value(从左到右)
命令:lrem key n value
注:若指定的删除元素值不存在,则不会影响任何数据。
2.8 将列表下标为index的值替换成value
命令:lset key index value
3. Set(集合)
3.1 将一个或多个member元素加入到集合key中,已经存在的member元素将被忽略
命令:sadd key value1 value2 …
3.2 取出集合的所有值
命令:smembers key
3.3 判断集合是否含有value值
命令:sismember key value
注:若有值则返回1,无值则返回0。
3.4 获取集合的元素个数
命令:scard key
3.5 删除集合中的元素
命令:srem key value1 value2 …
3.6 随机从集合中吐出一个值
命令:spop key
注:吐出的值会从集合中删除。
3.7 随机从集合中吐出n个值(不会影响原集合)
命令:srandmember key n
3.8 移动集合元素到另外一个集合
命令:smove source destination value
3.9 获取两个集合的交集元素
命令:sinter key1 keky2
3.10 获取两个集合的并集元素
命令:sunion key1 key2
3.11 获取两个集合的差集元素
命令:sdiff key1 key2
注:key1中的,不包含key2中的。
4. Hash(哈希表)
4.1 给集合中的field键赋值value
命令:hset key field value
4.2 从集合中的field取值value
命令:hget key field
4.3 批量设置hash的值
命令:hmset key field1 value1 field2 value2…
4.4 查询哈希表key中指定域field是否存在
命令:hexists key field
4.5 获取集合的所有field
命令:hkeys key
4.6 获取集合的所有value
命令:hvals key
4.7 为哈希表key中的域field的值加上增量increment
命令:hincrby key field increment
注:增量值可以为负值,表示减去多少。
4.8 将哈希表key中的域field的值设置为value,当且仅当域field不存在
命令:hsetnx key field value
注:设置成功返回 1,失败返回 0。
5. SortedSet(有序集合)
5.1 将一个或多个member元素及其score值加入到有序集key当中
命令:zadd key score1 value1 score2 value2 …
5.2 获取有序集key中,下标在start ~ stop之间的元素
命令:zrange key start stop [WITHSCORES]
- 若范围值为 0 ~ -1 表示返回全部值。
- 若最后加上 WITHSCORES,表示连同score分值一起返回。
5.3 返回有序集key中,所有score值介于min 和 max 之间(包括等于 min 或 max)的成员
命令:zrangebyscore key min max [withscores] [limit offset count]
- withscores:表示同时返回分值。
- LIMIT 偏移值 个数:偏移值表示返回的结果偏移的个数;个数表示限制返回的结果条数。
- 有序集成员按score值递增(从小到大)次序排列。
5.4 同上(从大到小排序)
命令:zrevrangebyscore key max min withscores limit offset count
5.5 为元素的score加上增量
zincrby key increment value
注:increment为增值。
5.6 删除该集合下指定值的元素
命令:zrem key value
5.7 统计该集合下,分数区间内的元素个数
命令:zcount key min max
5.8 返回该值在集合中的排名,从0开始
命令:zrank key value
注:排名从0开始,从小到大,依次递增。
文章访问量的排行榜(Zset)