了解 Redis 的都知道,Redis 服务器状态有很多可配置的默认值。
例如:数据库数量,最大可用内存,AOF 持久化相关配置和 RDB 持久化相关配置等等。我相信,关于 AOF 持久化和 RDB 持久化的配置大家都很熟悉,但是关于数据库数量和最大可用内存,是不是恰恰很容易被大家忽略?
当 Redis 实例部署在正式环境时,我们可能会根据系统业务或者服务器配置来对 redis.conf 配置文件里的一些选项进行修改。可能此时我们的潜意识都会觉得,大部分东西都是越多越好:数据库数量越多,那么我们就可以一个业务对应一个数据库,再繁杂的业务也不怕不够用;最大可用内存越大,那么我们就可以往 Redis 里存放越多的数据。
那么,数据库数量是不是真的可以无限大,Redis 没做限制么?是不是真的越多越好用?
最大可用内存是不是设置成越大越好?Redis 会不会对此也有限制呢?
下面我们来分析看看。
在 redis.h 里我们可以看到最大可用内存 REDIS_DEFAULT_MAXMEMORY
的默认值是0,即最大可用内存默认没有设置最大值。
如果 maxmemory == 0 ,那么不管用户存放多少数据到 Redis 中,Redis 也不会对可用内存进行检查,直到 Redis 实例因内存不足而崩溃也无作为。
但是 Redis 其实不是没有做任何限制,对于 32 位实例,Redis 就做了限制。如果你在 32 位的服务器上部署 Redis 实例,它的最大可用内存将限制在 3 GB。
为什么是 3 GB?
如果你在32位的服务器上部署 Redis 实例,它的最大可用内存将限制在 3 GB。
因为 32 位的机器最大只支持 4GB 的内存,而系统本身就需要一定的内存资源来支持运行,所以 32 位机器限制最大 3 GB 的可用内存是非常合理的,这样可以避免因为内存不足而导致 Redis 实例崩溃。
我们可以在 redis.c 里看到 32 位限制 3 GB 的源码:
/* 32 bit instances are limited to 4GB of address space, so if there is
* no explicit limit in the user provided configuration we set a limit
* at 3 GB using maxmemory with 'noeviction' policy'. This avoids
* useless crashes of the Redis instance for out of memory. */
// 对于 32 位实例来说,默认将最大可用内存限制在 3 GB
if (server.arch_bits == 32 && server.maxmemory == 0) {
redisLog(REDIS_WARNING,"Warning: 32 bit instance detected but no memory limit set. Setting 3 GB maxmemory limit with 'noeviction' policy now.");
server.maxmemory = 3072LL*(1024*1024); /* 3 GB */
server.maxmemory_policy = REDIS_MAXMEMORY_NO_EVICTION;
}
2、Redis 配置文件的最大可用内存选项:maxmemory
当然了,用户可以通过 redis.conf 配置文件的 maxmemory <bytes>
选项来设置最大可用内存。
但是,如果用户在配置文件开启了 maxmemory 选项,那么 Redis 会限制这个值不能小于 1M。
/* Warning the user about suspicious maxmemory setting. */
// 检查不正常的 maxmemory 配置
if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
redisLog(REDIS_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
}
此时,我们都知道:
当然是越大越好了,但是前提我们要考虑一下服务器会用来做什么。
假如我们的机器只用来部署一个 Redis 实例,那么大概留个 1 GB 的内存资源来支撑系统本身的运行即可。
但是如果我们部署多个 Redis 实例,或者还要部署其他系统,那么就要好好计算一翻了;如果设置的最大可用内存过大了,就会导致 Redis 实例因为内存不足而崩溃了。
在 redis.h 里我们可以看到 Redis 默认的数据库数量 REDIS_DEFAULT_DBNUM
为16。
用户可以通过 redis.conf 配置文件的 databases
选项来设置数据库数量。
1)在初始化服务器状态(redisServer)时,直接读取默认值 REDIS_DEFAULT_DBNUM。
2)在读取配置文件时,读取 databases 配置项,并做下一步判断。
rewriteConfigNumericalOption(state,"databases",server.dbnum,REDIS_DEFAULT_DBNUM);
server.dbnum = atoi(argv[1]);
if (server.dbnum < 1) {
err = "Invalid number of databases"; goto loaderr;
}
到此我们都知道:
Redis 数据库的数量无非就是想对应不同业务的数量,一个业务对应一个数据库,清晰明了;但是如果数据库数量太多,却可能会导致一些用户不易发现的问题!
例如删除过期键的 activeExpireCycle
函数中,就会对数据库数量有限制了:
一般情况下,函数只处理 REDIS_DBCORN_DBS_CALL
个数据库(即16个数据库)的过期键,除非上一次处理过期键遇到了时间限制,才会对所有数据库进行扫描;这其实就和 Redis 默认就是 16个数据库是一一对应的。
然后程序接着从数据库 0 - 15,一一遍历处理过期键:
/* We usually should test REDIS_DBCRON_DBS_PER_CALL per iteration, with
* two exceptions:
*
* 一般情况下,函数只处理 REDIS_DBCRON_DBS_PER_CALL 个数据库
* 除非:
*
* 1) Don't test more DBs than we have.
* 当前数据库的数量小于 REDIS_DBCRON_DBS_PER_CALL
* 2) If last time we hit the time limit, we want to scan all DBs
* in this iteration, as there is work to do in some DB and we don't want
* expired keys to use memory for too much time.
* 如果上次处理遇到了时间上限,那么这次需要对所有数据库进行扫描,
* 这可以避免过多的过期键占用空间
*/
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
//.....
// 遍历数据库
for (j = 0; j < dbs_per_call; j++) {
int expired;
// 指向要处理的数据库
redisDb *db = server.db+(current_db % server.dbnum);
// ....
那么存在一种情况:
如果用户设置了 databases = 20,而正常情况下,只有数据库 0 -15 这 16 个数据库的过期键得到定期删除策略的删除,而数据库 16 -19 这几个数据库不能通过定期删除策略删除掉过期键;只能等待惰性删除策略:即当数据库键被访问时才判断此键是否过期,过期了才删除此键。但是如果这些库的过期键很长一段时间都不被访问,那么会导致浪费不少宝贵的内存空间。
最后关于数据库数量的建议:
当然了,既然 Redis 将默认的数据库数量设置为 16,那么很多地方应该都会用到此来对数据库数量做一些限制,所以,我们正常情况下,尽量不要修改 Redis 的数据库数量;而且,我自己感觉 16 个其实是挺多的了,我们连一半都用不到,尴尬尴尬~
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8