Redis 能存多少个 Key

数据结构

Redis 的键值对存在哪里? 哈希表

dictEntry 存储的是实际键值对,定义如下

1
2
3
4
5
6
7
8
9
10
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;

dictht是哈希表,它的定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct dictht {

//dictEntry数组链表
dictEntry **table;

//数组的长度
unsigned long size;

//数组掩码,等于size-1
unsigned long sizemask;

//键值个数
unsigned long used;
} dictht;

Redis 的 kv 里面不是直接存字符串 key 是 sds,value 是 redisObject

冲突处理

因为每个数组下标对应一个链表,如果一个新的 key 算出的数组下标已经包含了其他的 dictEntry,只需要把新的 key 对应的 dictEntry 挂在链表的第一个位置即可(因为是新插入的,可能马上会用到)

扩缩容

单纯依赖链表做冲突处理,链表会越来越长,读写效率差,所以需要对哈希表扩容

对于 Redis 来说,一个哈希结构内置了两个哈希表,一个用于使用,另一个平时闲置,待时机成熟,扩容一倍,将数据迁移到另一个哈希表中

1
2
3
4
5
6
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */unsigned long iterators; /* number of iterators currently running */
} dict;

其中 rehashidx 表示当前 dict 是否正在做 rehash,如果为 -1 表示没有 rehash,当 used > 2^n 时候,需要扩容(rehashidx 表示ht[0] 在 rehash 的时候所在的索引值)

为了保证 Redis 单线程的高效性,整个 rehash 是渐进式的,全部迁移完成之后 rehashidx 置为 -1 对于key的路由来说,它依然先从dict[0]去找,如果找到了,就顺便把它迁移到dict[1]。如果没找到就要从dict[1]去找

能存多少 key?

先说结论:最好千万级别

size 定义了数组的长度,由于它是 unsigned long (4 个字节)所以理论上可以存 2 ^ 32 个元素

官方回答说最好放 2.5 亿个键值对

原因:

  1. rehash 问题,导致内存异常增长,由于要为 ht[1] 分配空间,大小取决于 ht[0] 当前包含的数量
  2. 死键问题,Redis 将所有键值对保存在 dict 中,还有一个 dict *expires 用来保存过期键,如果键值数量过多,并设置了过期时间,会导致内存占用
1
2
3
4
5
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */
dict *expires; /* Timeout of keys with a timeout set */
......
} redisDb;
  1. Redis有最大内存淘汰机制(maxmemory-policy),如果键值个数过多,那么可能逐出的就会更多,也就意味段时间内有大量的删除操作,甚至会造成Redis段时间不可用

Redis 能存多少个 Key
http://showyoubug.cn/2024/09/07/Redis 能存多少个 Key/
作者
Dong Su
发布于
2024年9月7日
许可协议