Redis基础数据结构及操作命令解析

Redis基础数据结构及操作命令解析


Redis有五种基础数据结构,分别为string、list、hash、set、zset。

字符串类型(string)

字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据、JSON化的对象甚至是一张图片。一个字符串类型键允许存储的数据的最大容量是512 MB,String数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。

基础结构

Redis的字符串是动态字符串,内部结构的实现类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。string在redis内部结构是一个带长度信息的字节数组,叫做"Simple Dynamic String"。

struct SDS<T>{
    T capacity; // 数组容量
    T len;  // 数组长度
    byte flags; // 特殊标志位,不用理睬它
    byte[] content; // 数组内容
}

如上代码所示,capacity表示所分配数组的长度;len表示字符串的实际长度;content表示字符串的内容。
  上面SDS结构使用了泛型T,之所以使用泛型,是因为redis为了对内存做极致的优化,不同长度的字符串使用不同的结构体来表示,当字符串比较短的时候,len和capacity就使用byte和short来表示。
  字符串的扩容,字符串长度小于1MB之前,扩容空间采用加倍策略;字符串长度超过1MB之后,为了避免加倍扩容带来的冗余空间的浪费,就每次扩容1MB。

embstr VS raw

字符串有两种存储方式,在长度特别短时,使用embstr形式存储,长度超过44字节时,使用raw形式存储。
  embstr是将RedisObject对象头结构和SDS对象连续存在一起,一次内存分配即可。
  raw是将RedisObject对象头结构和SDS对象分开存储,内存地址不连续。

struct RedisObject{
    int4 type;
    int4 encoding;
    int24 lru;
    int32 refcount;
    void *ptr;
}

如上代码所示,RedisObject对象头占16字节的内存空间,是每种数据结构共有的结构,且ptr指针指向具体对象内容的内存地址,这里就是SDS的内存地址。

为什么长度超过44字节就会使用raw形式存储呢?

内存分配器每次分配内存大小分别是2/4/8/16/32/64等字节,一个完整的embstr对象至少需要32个字节的空间(RedisObject对象头16字节+SDS头3字节),字符串总体超过64字节Redis会认为是一个大字符串,选择raw形式存储。
  RedisObject对象头16字节;SDS头3字节;字符串是以null结尾占1字节。总共20字节,所以留给存储数据的空间就只有44字节。

常用命令

get、set、incr、decr、mget

1. SET(赋值)

SET key value,对指定key赋值value。

SET user:1 zhangsan

2. GET(取值)

GET key,取得指定key的value值。

GET user:1

3. INCR(递增数字)

INCR key,将key指定为递增数字,每次调用都会自增1。INCR是保证原子性的,即多个线程同时操作不会发生线程不安全的情况。
  注:如果key不为整数时,会报错。

INCR num
redis> 1

4. INCRBY(增加指定的整数)

INCRBY key value,INCRBY与INCR命令类型,只是INCRBY可以对某个key增加指定的整数,而INCR每次都固定增加1。

INCRBY num 3
redis> 4

5. DECR(递减数字)

DECR key,每次调用都会自减1。

DECR num
redis> 3

6. DECRBY(减少指定的整数)

DECRBY key value,每次调用对某个key减少指定的整数。

DECRBY num 2
redis> 1

7. INCRBYFLOAT(增加指定浮点数)

INCRBYFLOAT key value,每次调用对某个key增加指定浮点数。

INCRBYFLOAT folat_num 5E+4
redis> 50006.30000000000000071

8. APPEND(尾部追加值)

APPEND key value,每次调用对某个key增加指定浮点数。如果key不存在,则相当于SET key value,返回追加值的总长度。

APPEND user:1 feng
GET user:1
redis> "zhangsanfeng"

9. STRLEN(获取字符串的长度)

STRLEN key,返回key对应value长度,如果不存在就返回0。

STRLEN user:1
redis> (integer) 12

10. MGET(取得多个键值)

MGET ...key,返回指定的多个key的value值。

MGET key1 key2 key3

11. MSET(设置多个键值)

MSET key value [key value],对多个key设置value。

MSET key1 k1 key2 k2 key3 k3

列表类型(list)

Redis的列表相当于Java中的LinkedList(双向链表),亦意味着插入和删除只需O(1),读取需要O(n)。可以巧妙运用列表提供的操作命令来完成队列和栈的数据结构操作。
  列表类型(list)可以用来存储一个有序的字符串列表,常用操作是向列表两端添加元素,或者取得某个片段。列表类型内部是使用双向链表实现的,即向列表两端添加元素的时间复杂度都为O(1),获取越接近两端的元素越快。

常用命令

1. LPUSH(列表左边添加元素)

LPUSH key value [...value],从列表左边添加指定元素,可以添加多个,返回添加元素后列表的长度。

LPUSH numbers 1
redis> 1

2. RPUSH(列表右边添加元素)

RPUSH key value [...value],从列表右边添加指定元素,可以添加多个,同样返回添加元素之后,当前列表的长度。

RPUSH numbers 2
redis> 2

3. LPOP(从列表左边弹出一个元素)

LPOP key,从列表左边弹出一个元素,该操作分为两步,第一步将元素从列表中移除,第二部将元素返回。

LPOP numbers
redis> 1

4. RPOP(从列表右边弹出一个元素)

RPOP key,从列表右边弹出一个元素,该操作分为两步,第一步将元素从列表中移除,第二部将元素返回。

RPOP numbers
redis> 2

5. LLEN(获取列表中元素个数)

LLEN key,获取列表中元素个数,返回个数。

LLEN numbers
redis> 2

6. LRANGE(获取列表片段)

LRANGE key start stop,获取列表片段,索引从0开始,LRANGE与POP命令不同,LRANGE只取出片段不会将其从列表中删除。

LRANGE numbers 0 3
redis>

7. LREM(删除列表中指定值)

LREM key count value,删除列表中指定值。

  1. 当count > 0时LREM命令会从列表左边开始删除前count个值为value的元素。

  2. 当count < 0时LREM命令会从列表右边开始删除前count个值为value的元素。

  3. 当count = 0时LREM命令会删除所有值为value的元素。

    LREM numbers 0 2

6. LINDEX(获取指定索引位置的元素)

LINDEX key index,获取指定索引位置的元素,索引从0开始,获取索引为0位置的元素。

LINDEX numbers 0

7. LSET(设置指定索引位置的元素)

LSET key index value,设置指定索引位置的元素,索引从0开始,设置索引为0位置的元素为7。

LSET numbers 0 7

8. LTRIM(删除指定范围外的所有元素)

LTRIM key start end,删除指定范围外的所有元素,删除除0-3位置之外的所有元素。

LTRIM numbers 0 3

9. LINSERT(向列表中插入元素)

LINSERT key AFTER|BEFORE pivot value,向列表中插入元素,在选择在指定元素前面插入还是后面插入。

LINSERT numbers AFTER 3 2

在元素3后插入2。

LINSERT numbers BEFORE 3 2

在元素3前插入2。

集合类型(set)

Redis的集合相当于Java中的HashSet,内部的键值对是无序且唯一的。内部相当所有元素的value都为null的一个特殊的字典。set可用于需要去重的地方。
  集合类型和列表类型及其相似。但集合类型主要用于求得交集、并集和差集。

常用命令

1. SADD(添加元素)

SADD key member [...member],添加元素,可以添加多个,当所添加元素已经存在,不再添加。

SADD letters a b c d
2. SREM(删除元素)

SREM key member [...member],删除元素,可以删除多个,返回成功删除个数。

SREM letters d

3. SISMEMBER(判断元素是否在集合中)

SISMEMBER key member,判断元素是否在集合中,有返回1,否则返回0。

SISMEMBER letters a

4. SDIFF(差集运算)

SDIFF key [...key],差集运算。

SADD A 1 2 3 4 5
SADD B 2 4 6 8 9
SDIFF A B
1) "1"
2) "3"
3) "5"

SDIFF A B = A - B,返回即集合A排除掉A与B交集之后的元素。

5. SINTER(交集运算)

SINTER key [...key],交集运算,返回即集合A与B共有元素。

SADD A 1 2 3 4 5
SADD B 2 4 6 8 9
SINTER A B
1) "2"
2) "4"

6. SUNION(并集运算)

SUNION key [...key],交集运算,返回A + B,所有元素。

SADD A 1 2 3 4 5
SADD B 2 4 6 8 9
SUNION A B
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "8"
8) "9"

7. SCARD(获得集合中元素个数)

SCARD key,获得集合中元素个数。

SCARD A

8. SPOP(从集合中弹出一个元素)

SPOP key,从集合中随机弹出一个元素,因为集合是无序的。

SPOP A

散列类型(hash)

Redis的字典相当于Java语言里面的HashMap,它是无序字典,内部存储了很多键值对。其内部实现结构也和Java的HashMap一样,"数组+链表"形式,如遇hash冲突则用链表串起来。
  大字典的扩容比较耗时,首先需要重新申请新的数组,然后再将旧字典中的所有元素搬移到新数组中。这是一个O(n)级别的操作,单线程的Redis显然承担不起,所以Redis采用渐进式rehash的方式进行扩容。小步搬移,等待所有都搬移完毕之后再释放旧字典的内存。
  一般情况下,当hash元素个数等于数组长度时就会扩容两倍。但是如果当前redis正在bgsave(将数据保存到磁盘),为了减少内存页的过多分离,redis会选择尽量不去扩容,但是如果元素个数已经是数组长度的5倍,那么此时会强制扩容。
  如果hash表中元素个数低于数组长度的10%,redis就会进行缩容,且不考虑bgsave。

hash攻击

如果hash函数存在偏向性,这种偏向性就会被利用来攻击服务器。因为偏向性强的hash函数会将过多的元素分配到同一个链表中,从而就会使查找从O(1)退化为O(n)。

常用命令

1. HSET(赋值)

HSET key field value,对key中的field字段设置value。

HSET car price 500
HSET car name changan

以上是分别对car下price字段和name进行赋值,price+name类似于一个对象或一个hashmap。

2. HGET(取值)

HGET key field,取得指定key下指定的field字段。

HGET car price
redis> 500

3. HMSET(批量赋值)

HMSET key field value [field value ...],对指定key下字段批量赋值。

HMSET car price 600 name changan

对car下price字段和name同时进行赋值。

4. HMGET(取值)

HMGET key field [field ...],取得指定key下指定的field字段对应值。

HMGET car price name
redis> "600"
redis> "changan"

5. HGETALL(取的所有值)

HGETALL key,取得指定key下所有字段对应值,返回格式为field-value形式。

HGETALL car
redis> "price"
redis> "600"
redis> "name"
redis> "changan"

6. HEXISTS(判断字段是否存在)

HEXISTS key field,判断指定key下的field字段是否存在,存在返回1,否则返回0。

HEXISTS car price
redis> 1

7. HSETNX(当字段不存在时赋值)

HSETNX key field value,对某个key下field进行赋值,但如果该field存在则不做任何操作,不存在才赋值。

HSETNX car price 500
redis> 0
HSETNX car brand changan
redis> 1

8. HINCRBY(增加指定数字)

HINCRBY key field increment,对指定key的field增加指定数字,类似于字符串类型的INCRBY。

HINCRBY car price 400
redis> 1000
HGET car price
redis> "1000"

9. HDEL(删除字段)

HDEL key field [field ...],删除key下field字段,这里可以为多个。

HDEL car brand
redis> 1

10. HKEYS(获取所有field字段)

HKEYS key,获取当前key下所有field字段。

HKEYS car
redis> "price"
redis> "name"

11. HVALS(获取所有field字段对应值)

HVALS key,获取当前key下所有field字段对应值。

HVALS car
redis> "1000"
redis> "changan"

12. HLEN(获取field字段数量)

HLEN key,获取field字段数量。

HLEN car
redis> 2

有序集合类型(zset)

Redis的有序列表首先保证内部value的唯一性,且还可以给每一个value设置一个score代表其排序的权重,zset内部是用跳跃列表来实现的。
  因为zset提供有score进行排序,所有在读取数据时可以指定某一区间的score进行读取数据,从而可以实现一个有序、不重复且可灵活读取的列表。
  有序集合类型和集合类型,一个是无序,一个有序的。有序集合类型与列表类型:

  1. 列表类型是通过链表实现,即两端元素获取速度快,更多用于"日志"或"新鲜事"的应用场景。
  2. 有序集合类型是通过散列表和跳跃表实现,即读取中间数据相比列表类型要快。
  3. 列表类型不能简答的调整某个元素的位置,但有序集合类型可以(通过更改元素的分数)。
  4. 有序集合类型比列表类型更耗费内存。

常用命令

1. ZADD(添加元素)

ZADD key [NX|XX] [CH] [INCR] score member [score member ...],添加元素,可以添加多个,这里的score可以是应用场景中任何用于排序的元素。

ZADD scoreboard 90 Tom 60 Ann 87 Jason

2. ZSCORE(获取分数)

ZSCORE key member,获取指定key分数。

ZSCORE scoreboard Tom

3. ZRANGE(获取某个范围排名的元素:从小到大)

ZRANGE key start stop [WITHSCORES],获取排名在某个范围的元素,WITHSCORES-加上之后会将分数一同返回。

ZRANGE scoreboard 0 2
1) "Ann"
2) "Jason"
3) "Tom"

4. ZREVRANGE(获取某个范围排名的元素:从大到小)

ZREVRANGE key start stop [WITHSCORES],获取排名在某个范围的元素,WITHSCORES-加上之后会将分数一同返回。

ZREVRANGE scoreboard 0 2
1) "Tom"
2) "Jason"
3) "Ann"

5. ZRANGEBYSCORE(获取指定分数范围的元素)

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count],获取指定分数范围的元素,WITHSCORES-加上之后会将分数一同返回。

ZRANGEBYSCORE scoreboard 70 100
1) "Jason"
2) "Tom"

6. ZINCRBY(增加指定分数)

ZINCRBY key score member,对指定key下的元素增加指定分数,如为-10------则表示为减少10分。

ZINCRBY scoreboard 20 Ann

7. ZCARD(获取元素个数)

ZCARD key,获取集合中元素的个数并返回。

ZCARD scoreboard
(integer) 3

8. ZCOUNT(统计指定范围的元素个数)

ZCARD key min max,统计指定范围的元素个数。

ZCOUNT scoreboard 60 100
(integer) 3

9. ZREM(删除元素)

ZREM key member [...member],删除元素。

ZREM scoreboard Tom

10. ZRANK(获取元素中排名:从小到大)

ZREM key member,获取元素在有序集合中排名。

ZRANK scoreboard Tom
(integer) 2

11. ZREVRANK(获取元素中排名:从大到小)

ZREVRANK key member,获取元素在有序集合中排名。

ZREVRANK scoreboard Tom
(integer) 0

一键三连,让我的信心像气球一样膨胀!

相关推荐
Chrikk20 分钟前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*23 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue23 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man25 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
ChoSeitaku2 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程2 小时前
双向链表专题
数据结构
香菜大丸2 小时前
链表的归并排序
数据结构·算法·链表
jrrz08282 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
@小博的博客2 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习