Redis 基础命令

本文主要是讲解了 redis 的基础命令

Redis 基本命令

1. Redis 字符串

Redis 对于字符串(string)的操作

操作命令时间复杂度
设置值set key valueO(1)
获取值get keyO(1)
删除值del key [key …]O(K), K为键的个数
批量设置key valuemset key value [key value ….]O(K), K为键的个数
批量获取值mget key [key …]O(K), K为键的个数
自增值incr keyO(1)
自减值decr keyO(1)
自增指定数字incrby key incrementO(1)
自减指定数字decrby key decrementO(1)
自增指定浮点数incrbyfloat key incrementO(1)
追加字符串append key alueO(1)
获取字符串长度strlen keyO(1)
获取指定起始字符之后的字符串setrange key offset valueO(1)
获取指定位置字符串getrange key start endO(n), n是字符串的长度,由于获取字符串非常快,所以如果字符串不是很长,可以视同为O(1)

Redis 字符串内部编码格式

字符串类型的内部编码有3种:

  • int:8个字节的长整型。
  • embstr:小于等于 39 (3.0版本以前,3.2版本以后为44) 个字节的字符串。
  • raw:大于39 (3.0版本以前,3.2版本以后为44) 个字节的字符串。

针对于3.2版本embstr存储字节改动的说明

embstr 分配的是一块连续的内存,长度为 64 字节,如果超出了 64 个字节就认为是一个大字符串,就会用到 raw 编码。embstr 由 RedisObject 和
sdshdr 两个对象组成。

其中 RedisObject 占用字节情况:

1
2
3
4
5
6
7
struct RedisObject {
int4 type; // 4bits
int4 encoding; // 4bits
int24 lru; // 24bits
int32 refcount; // 4bytes = 32bits
void *ptr; // 8bytes,64-bit system
}

redis对象头信息如上,会占用16字节的大小,固定不变

SDS 占用字节情况:

3.2版本以前:

1
2
3
4
5
struct sdshdr {
unsigned int len; // 4byte
unsigned int free; // 4byte
byte[] content; // 内联数组
}

使用 len 和 free 记录了这个 SDS 的长度和空闲空间,且 content 必须以\0结尾,固定占用字节数为 9 字节,因此 embstr 的存储空间为 64 - 9 - 16 = 39 字节。

3.2版本以后:

1
2
3
4
5
6
struct sdshdr8 {
uint8_t len; // 1byte
uint8_t alloc; // 1byte
uint8_t flags; // 1byte
byte[] content; // 内联数组
}

3.2 版本则将原来的 sdshdr 改成了 sdshdr8, sdshdr16,sdshdr32,sdshdr64(具体结构由字符串长度不同而改变),里面的unsigned int 变成了 uint8_t, uint16_t。(还加了一个char flags)这样更加优化小sds的内存使用。

而 embstr 本身就是针对于短字符串的使用,自然会使用最小的 sdshdr8,而 sdshdr8 与之前的 sdshdr 相比正好减少了 5 个字节( sdsdr8 = uint8_t * 2 + char = 1 * 2 + 1 = 3, sdshdr = unsigned int * 2 = 4 * 2 = 8),所以其能容纳的字符串长度增加了5个字节变成了44。

2. Redis 哈希

Redis 对哈希(hash)的操作

操作命令时间复杂度
设置哈希键值对hset key field valueO(1)
获取哈希值hget key fieldO(1)
批量删除哈希值del key field [field …]O(K), K为field的个数
获取哈希个数hlen keyO(1)
获取所有哈希键值对hgetall keyO(K), K为field的个数
批量获取哈希值hmget field [field …]O(K), K为field的个数
批量设置哈希键值对hmset field value [field value …]O(K), K为field的个数
判断哈希是否存在hexists key fieldO(1)
获取所有哈希键hkeys keyO(K), K为field的个数
获取所有哈希值hvals keyO(K), K为field的个数
字段不存在时设置哈希键值对hsetnx key field valueO(1)
为哈希指定键的值加上指定数字hincrby key field valueO(1)
为哈希指定键的值加上指定浮点数hincrbyfloat key field valueO(1)
获取指定哈希键的值长度hstrlen key fieldO(1)

Redis 哈希内部编码

哈希类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries 配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64 字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的 结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使 用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而 hashtable的读写时间复杂度为O(1)。

3. Redis 列表

Redis 对列表(list)的操作

操作命令时间复杂度
从右边插入元素rpush key value [value …]O(K), K为元素的个数
从左边插入元素lpush key value [value …]O(K), K为元素的个数
从列表左/右多少个偏移位插入元素linsert key before/after pivot valueO(n), n为偏移量
根据指定范围获取元素lrange key start endO(s+n),s是start的偏移量,n是start到end的范围
获取指定下标元素lindex key indexO(n), n为索引的偏移量
获取列表长度llen keyO(1)
弹出左边列表第一个元素lpop keyO(1)
弹出右边列表第一个元素rpop keyO(1)
删除指定元素(count为0全部删除)lrem count valueO(n), n为列表长度
根据范围删除指定元素ltrim key start endO(n),n为要裁剪的元素总数
修改指定下标元素lset key index valueO(n),n是索引的偏移量
阻塞操作blpop brpopO(1)

列表使用场景

列表组合可实现多种数据结构:

  • lpush + lpop = Stack(栈)
  • lpush + rpop = Queue(队列)
  • lpsh + ltrim = Capped Collection(有限集合)
  • lpush + brpop = Message Queue(消息队列)

4. Redis 集合

Redis 对集合(set)的操作

操作命令时间复杂度
添加元素sadd key element [element …]O(K), K为元素的个数
删除元素srem key element [element …]O(K), K为元素的个数
计算元素个数scard keyO(1)
判断元素是否在集合中sismember key elementO(1)
随机从集合返回指定元素个数srandmember key [count]O(n), n为count
从集合中随机弹出元素spop keyO(1)
获取所有元素smembers keyO(n), n为元素总数
求多个集合的交集sinter key [key …] 或者 sinterstoreO(m*k), k是多个集合中元素最少的个数,m是键个数
求多个集合的并集suinon key [key …] 或者 suinonstoreO(k), k是多个集合元素个数和
求多个集合的差集sdiff key [key …] 或者 sdiffstoreO(k), k是多个集合元素个数和

Redis 集合内部编码

集合类型的内部编码有两种:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max- intset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实 现,从而减少内存的使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使 用hashtable作为集合的内部实现。

集合的使用场景

  • sadd = Tagging(标签)
  • spop/srandmember = Random item(生成随机数,比如抽奖)
  • sadd + sinter = Social Graph(社交需求)

5. Redis 有序集合

Redis 对有序集合(sortset)的操作

操作命令时间复杂度
添加成员zadd key score member [score member …]O(k*log(n)), k为添加成员的个数,n为当前有序集合成员个数
计算成员个数zcard keyO(1)
计算某个成员分数zscore key memberO(1)
计算成员的排名(由低到高)zrank key memberO(log(n)), n为当前有序集合的成员个数
计算成员的排名(由高到低)zrevrank key memberO(log(n)), n为当前有序集合的成员个数
删除成员zrem key member [member …]O(k*log(n)), k为添加成员的个数,n为当前有序集合成员个数
增加成员的分数zincrby key increment memberO(log(n)), n为当前有序集合的成员个数
返回指定排名范围的成员(由低到高返回)zrange key start end [withscores]O(log(n) + k), k为要获取的成员个数,n为当前有序集合的成员个数
返回指定排名范围的成员(由高到低返回)zrevrang key start end [withscores]O(log(n) + k), k为要获取的成员个数,n为当前有序集合的成员个数
返回指定分数范围的成员(由低到高返回)zrangebyscore key min max [withscores] [limit offset count]O(log(n) + k), k为要获取的成员个数,n为当前有序集合的成员个数
返回指定分数范围的成员(由高到低返回)zrevrangebyscore key min max [withscores] [limit offset count]O(log(n) + k), k为要获取的成员个数,n为当前有序集合的成员个数
返回指定分数范围成员个数zcountO(log(n)), n为当前有序集合的成员个数
删除指定排名内的升序元素zremrangebyrank key start endO(log(n) + k), k为要删除的成员个数,n为当前有序集合的成员个数
删除指定分数范围的成员zremrangebyscore key min maxO(log(n) + k), k为要删除的成员个数,n为当前有序集合的成员个数
交集zinterstore destination numkeys key [key …]O(n*k) + O(m*log(m)), n是成员数最小的有序集合成员个数,k是有序集合的个数,m是结果集中成员个数
并集zunionstore destination numkeys key [key …]O(n*k) + O(m*log(m)), n是成员数最小的有序集合成员个数,k是有序集合的个数,m是结果集中成员个数

Redis 有序集合内部编码

有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist- entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配 置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist 可以有效减少内存的使用。
  • skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作 为内部实现,因为此时ziplist的读写效率会下降。

列表、集合、有序集合的异同点

数据结构是否允许重复元素是否有序有序实现方式应用场景
列表索引下标时间轴、消息队列等
集合标签、社交等
有序集合分值排行榜系统、社交等

使用 Redis 需要注意的问题

  • 由于Redis的单线程架构,所以需要每个命令能被快速执行完,否则 会存在阻塞Redis的可能,理解Redis单线程命令处理机制是开发和运维Redis 的核心之一。
  • 批量操作(例如mget、mset、hmset等)能够有效提高命令执行的效 率,但要注意每次批量操作的个数和字节数。
  • 了解每个命令的时间复杂度在开发中至关重要,例如在使用keys、 hgetall、smembers、zrange等时间复杂度较高的命令时,需要考虑数据规模 对于Redis的影响。
  • scan命令可以解决keys命令可能带来的阻塞问题,同时Redis还提供了hscan、sscan、zscan渐进式地遍历hash、set、zset。