本文主要是讲解了 redis 的基础命令
Redis 基本命令
1. Redis 字符串
Redis 对于字符串(string)的操作
操作 | 命令 | 时间复杂度 |
---|---|---|
设置值 | set key value | O(1) |
获取值 | get key | O(1) |
删除值 | del key [key …] | O(K), K为键的个数 |
批量设置key value | mset key value [key value ….] | O(K), K为键的个数 |
批量获取值 | mget key [key …] | O(K), K为键的个数 |
自增值 | incr key | O(1) |
自减值 | decr key | O(1) |
自增指定数字 | incrby key increment | O(1) |
自减指定数字 | decrby key decrement | O(1) |
自增指定浮点数 | incrbyfloat key increment | O(1) |
追加字符串 | append key alue | O(1) |
获取字符串长度 | strlen key | O(1) |
获取指定起始字符之后的字符串 | setrange key offset value | O(1) |
获取指定位置字符串 | getrange key start end | O(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 | struct RedisObject { |
redis对象头信息如上,会占用16字节的大小,固定不变
SDS 占用字节情况:
3.2版本以前:
1 | struct sdshdr { |
使用 len 和 free 记录了这个 SDS 的长度和空闲空间,且 content 必须以\0结尾,固定占用字节数为 9 字节,因此 embstr 的存储空间为 64 - 9 - 16 = 39 字节。
3.2版本以后:
1 | struct sdshdr8 { |
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 value | O(1) |
获取哈希值 | hget key field | O(1) |
批量删除哈希值 | del key field [field …] | O(K), K为field的个数 |
获取哈希个数 | hlen key | O(1) |
获取所有哈希键值对 | hgetall key | O(K), K为field的个数 |
批量获取哈希值 | hmget field [field …] | O(K), K为field的个数 |
批量设置哈希键值对 | hmset field value [field value …] | O(K), K为field的个数 |
判断哈希是否存在 | hexists key field | O(1) |
获取所有哈希键 | hkeys key | O(K), K为field的个数 |
获取所有哈希值 | hvals key | O(K), K为field的个数 |
字段不存在时设置哈希键值对 | hsetnx key field value | O(1) |
为哈希指定键的值加上指定数字 | hincrby key field value | O(1) |
为哈希指定键的值加上指定浮点数 | hincrbyfloat key field value | O(1) |
获取指定哈希键的值长度 | hstrlen key field | O(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 value | O(n), n为偏移量 |
根据指定范围获取元素 | lrange key start end | O(s+n),s是start的偏移量,n是start到end的范围 |
获取指定下标元素 | lindex key index | O(n), n为索引的偏移量 |
获取列表长度 | llen key | O(1) |
弹出左边列表第一个元素 | lpop key | O(1) |
弹出右边列表第一个元素 | rpop key | O(1) |
删除指定元素(count为0全部删除) | lrem count value | O(n), n为列表长度 |
根据范围删除指定元素 | ltrim key start end | O(n),n为要裁剪的元素总数 |
修改指定下标元素 | lset key index value | O(n),n是索引的偏移量 |
阻塞操作 | blpop brpop | O(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 key | O(1) |
判断元素是否在集合中 | sismember key element | O(1) |
随机从集合返回指定元素个数 | srandmember key [count] | O(n), n为count |
从集合中随机弹出元素 | spop key | O(1) |
获取所有元素 | smembers key | O(n), n为元素总数 |
求多个集合的交集 | sinter key [key …] 或者 sinterstore | O(m*k), k是多个集合中元素最少的个数,m是键个数 |
求多个集合的并集 | suinon key [key …] 或者 suinonstore | O(k), k是多个集合元素个数和 |
求多个集合的差集 | sdiff key [key …] 或者 sdiffstore | O(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 key | O(1) |
计算某个成员分数 | zscore key member | O(1) |
计算成员的排名(由低到高) | zrank key member | O(log(n)), n为当前有序集合的成员个数 |
计算成员的排名(由高到低) | zrevrank key member | O(log(n)), n为当前有序集合的成员个数 |
删除成员 | zrem key member [member …] | O(k*log(n)), k为添加成员的个数,n为当前有序集合成员个数 |
增加成员的分数 | zincrby key increment member | O(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为当前有序集合的成员个数 |
返回指定分数范围成员个数 | zcount | O(log(n)), n为当前有序集合的成员个数 |
删除指定排名内的升序元素 | zremrangebyrank key start end | O(log(n) + k), k为要删除的成员个数,n为当前有序集合的成员个数 |
删除指定分数范围的成员 | zremrangebyscore key min max | O(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。