# Redis 内存
Redis
的所有的数据都是存在了内存中的。换句话说,Redis
是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。
Redis
通过一个叫做过期字典(可以看作是hash
表)来保存数据过期的时间。过期字典的键指向Redis
数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了key
所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
过期字典是存储在redisDb
这个结构里的:
typedef struct redisDb {
...
dict *dict; //数据库键空间,保存着数据库中所有键值对
dict *expires // 过期字典,保存着键的过期时间
...
} redisDb;
2
3
4
5
6
7
# Redis 内存淘汰机制
Redis 提供 6 种数据淘汰策略:
- volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
4.0 版本后增加以下两种:
- volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
- allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?
- 保留热点数据:对于保留 Redis 热点数据来说,我们可以使用 Redis 的内存淘汰策略来实现,可以使用allkeys-lru淘汰策略,该淘汰策略是从 Redis 的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。
- 保证 Redis 只存20w的数据:1个中文占2个字节,假如1条数据有100个中文,则1条数据占200字节,20w数据 乘以 200字节 等于 4000 字节(大概等于38M);所以要保证能存20w数据,Redis 需要38M的内存。
# 过期删除机制
常用的过期数据的删除策略就两个:
- 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
- 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
Redis
采用的是 定期删除+惰性/懒汉式删除,两者配合使用。
redis 主从模式下,惰性删除也只在
master
上生效,slave
上是不生效的。slave
上过期的key
会依赖master
发过来的DEL
命令来删除
# 数据持久化
# RDB(Redis DataBase)
RDB
方式也叫快照方式,这种方式会在一定的触发时机下,将当前redis
的内存快照保存到磁盘上的dump.rdb
文件中。这个过程中,主要执行一个命令bgsave
。
非常适用于备份,全量复制等场景,但是无法做到实时持久化/秒级持久化。
快照持久化是 Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
2
3
4
5
- save 命令是一个同步操作,当客户端向服务器发送 save 命令请求进行持久化时,服务器会阻塞 save 命令之后的其他客户端的请求,直到数据同步完成。
- bgsave 命令是一个异步操作,当客户端发出 bgsave 命令时,Redis 服务器主进程会 fork 一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求,子进程会在数据保存到 rdb 文件后退出。
# AOF(Append Only File)
在Redis
的配置文件中存在三种不同的AOF
持久化方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显式地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
2
3
AOF
的appendfsync
触发机制是上面配置的三个参数决定的:no
、always
、everysec
。
可以根据对性能和持久化的实时性要求,具体配置。如果不知道哪种合适,就使用默认的everysec
,这样即使出现系统崩溃,最多只会丢失1s
之内产生的数据。
AOF
文件远大于RDB
文件,数据恢复速度比rdb
慢。
# 写时复制 COW (Copy On Write)机制
- Redis使用操作系统的多进程写时复制(Copy On Write)机制来实现快照的持久化,在持久化过程中调用glibc(Linux下的C函数库)的函数fork()产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端的读写请求。
- 如果主线程收到的客户端的读写请求,需要修改某块数据,那么这块数据就会被复制一份到内存,生成该数据的副本,主进程在该副本上进行修改操作。所以即使对某个数据进行了修改,Redis持久化到RDB中的数据也是未修改的数据,这也是把RDB文件称为"快照"文件的原因,子进程所看到的数据在它被创建的一瞬间就固定下来了,父进程修改的某个数据只是该数据的复制品。
# AOF 文件过大怎么办?
执行BGREWRITEAOF
命令对redis
的AOF
进行重写(rewrite)机制
- 随着
AOF
文件越来越大,里面会有大部分是重复命令或者可以合并的命令 - 减少
AOF
日志大小,减少内存占用,加快数据库恢复时间
参考文档