redis
基本数据结构
redis
的返回值
- 在设置一个
key-value
对的时候通常会返回ok
告诉我们操作成功了,1
代表成功,0
代表失败,通常会根据返回值的不同处理不同的业务逻辑 - 用
redis.cn
来查看命令
全局操作
flushdb
清空内存数据库keys *
展示所有存储结构名del db
删除某个内存结构
string
一个string
键最大能存储512MB
> SET runoob "菜鸟教程" > GET runoob INCR key # 原子执行加一 incr key increment # 原子执行加increment decr key decrby key decrement setnx key value # 只有key不存在的时候
删除操作
DEL runoob
二进制操作
setbit key offset value # 设置offset处值为value getbit key offset bitcount key [start end] # 签到功能后台实现 setbit sign:10001:202106 1 1 # 某月某天签到情况 bitcount sign:1001:202106 # 查看当月其签到次数 getbit sign:1001:202106 2 # 查看当月第二天天签到次数
分布式锁
setnx lock 1 # 获取锁 del lock # 释放锁 # 其他操作
Hash
每个hash
可以存储2 32 − 1 2^{32} - 1232−1个键值对
hmset
HMSET runoob field1 Hello field2 World
hget runoob field1
hmget runoob field1 field2
hgetall runoob
hincrby key field number
- 只能对数值字段
+
hdel key field
- 删除某
key
上的键值对
List
简单的字符串列表,底层由循环双向链表实现
列表最多可存储 2 32 − 1 2^{32} - 1232−1 元素 (4294967295
, 每个列表可存储40
多亿)。
lpush runoob redis rpush runoob mongodb lpop runoob rpop runoob lrange runoob 0 1 # [0, 1] 左闭右闭 # 删除前i次出现的值为value的元素 lrem runoob count value - lrem runoob 2 king # 删除前2次出现的king 如果有三个king则删除前2个 # 修剪功能 将list修剪为只有start,end中的元素 ltrim runoob 0 0 # 只保留第一个元素
设置阻塞超时队列
不存在list队列 | |
brpop list 0 永久阻塞等待回应 |
|
阻塞 | lpush list 1 |
解开阻塞得到数据1返回格式为 1) "list" 2) "mark" ( 23.11s )阻塞23 秒 |
|
不存在list 因为被立即pop 了 |
Set
Redis
的 Set
是 string
类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
sadd runoob redis smembers runoob scard runoob # 统计元素个数 sismember runoob vico # 查看vico在不在runoob中 srandmember key [count]# 随机取出一个元素(或者多个)出来 spop members # 移除并返回一个随机元素 sdiff key [key...] # 返回指定所有集合的成员的差集 sinter key [key...] # 返回交集 sunion key [key...] # 返回并集
集合内元素的唯一性,第二次插入的元素将被忽略
zset
Redis zset
和set
一样也是string
类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double
类型的分数。
redis
正是通过分数来为集合中的成员进行从小到大的排序。
zset
的成员是唯一的,但分数(score
)却可以重复。
zadd key score member
添加元素到集合,元素在集合中存在则更新对应score
zadd runoob 0 redis zadd runoob 0 mongodb zadd runoob 0 rabbitmq zrange runoob 0 -1 [withscores] zcard runoob # 返回key的个数 zincrby runoob 11 member # 给member的分数加11
zrangebyscore runoob 0 1000
根据score
范围查找
redis
的存储结构
string
类型
动态字符串(sds
)sds.h
查看源码typedef char* sds;
,sds
就是char*
类型
针对不同的字符串长度用不同的数据结构(为了节约内存)
struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* 分配的长度 为了进行惰性删除 */ unsigned char flags; /* 标识字符串类型 */ char buf[]; };
- 上述是字符串长度小于2 8 2^828时的存储结构,其他还有
sdshdr16
、sdshdr32
、sdshdr64
- 柔性数组:一次
malloc
,一次free
就可以,内存连续
分配时的代码
s = malloc(sizeof(struct sdshdr8) + 64); return s + sizeof(struct sdshdr8); // sds的起始地址
- 释放
free(sds - sizeof(struct sdshdr8));
存储结构
字符串长度小于等于
20
且能转成整数,则使用int
存储;字符串长度小于等于
44
,则使用embstr
存储;字符串长度大于
44
,则使用raw
存储;
list
类型
双向链表结构
查看源码:
一个quicklist
,尾部链表的头节点和尾部节点
quicklistNode
是一个双向链表
typedef struct quicklistNode { struct quicklistNode *prev; struct quicklistNode *next; unsigned char *entry; // 存储的值 size_t sz; /* entry size in bytes */ unsigned int count : 16; /* count of items in listpack */ unsigned int encoding : 2; /* RAW==1 or LZF==2 */ unsigned int container : 2; /* PLAIN==1 or PACKED==2 */ unsigned int recompress : 1; /* was this node previous compressed? */ unsigned int attempted_compress : 1; /* node can't compress; too small */ unsigned int extra : 10; /* more bits to steal for future usage */ } quicklistNode;
数据压缩
- 元素长度小于
48
,不压缩 - 元素压缩前后长度差不超过
8
,不压缩
Hash
第二层hash
的value
值只能是string
类型
查询插入删除都是O(1)
set
底层实现
- 如果存储的是整数,则底层是整数数组(有序),便于交并差集
- 如果存储的是字符串,则底层是
hash
存储结构
- 元素素都为整数且节点数量小于等于
512
(set-max-intset-entries
),则使用整数数组存储; - 元素当中有一个不是整数或者节点数量大于
512
,则使用字典 存储;
时间复杂度
如果存储的是数字则smembers
的时间复杂度为O(nlogn)
如果存储的是字符串则时间复杂读为o(1)
zset
存储结构:
- 节点数量大于
128
或者有一个字符串长度大于64
使用跳表(skiplist
) - 节点数量小于等于
128
且所有字符串长度小于等于64
则使用ziplist
存储
redis
抽象层次
redis
没有创建数据结构的命令
- 设置的同时创建
- 添加的同时创建
redis
有删除kv
的命令,但是v
中没有元素时会自动删除kv
- 阻塞连接概念
brpop # 队列没有数据pop的话就阻塞 或者设置超时时间
- 通过命令的组合实现其他数据结构
- 栈
lpush + lpop rpush + rpop
- 队列
lpush + pop rpush + lpop
- 阻塞队列 多个队列则先来先服务
lpush + brpop rpush + brpop
- 通过组合数据结构实现功能
hash + list # list装购物车结构 hash + set # set装在线玩家id hash + zset # zset装排行榜
key的数量
- 无线增长 并且性能不会随着数量增加而减少,因为是hash
- 代价是扩容缩容渐进式
hash