# Redis 内存

Redis的所有的数据都是存在了内存中的。换句话说,Redis是一种内存数据库,将数据保存在内存中,读写效率要比传统的将数据保存在磁盘上的数据库要快很多。

Redis通过一个叫做过期字典(可以看作是hash表)来保存数据过期的时间。过期字典的键指向Redis数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了key所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。

redis过期时间

过期字典是存储在redisDb这个结构里的:

typedef struct redisDb {
...

    dict *dict;     //数据库键空间,保存着数据库中所有键值对
    dict *expires   // 过期字典,保存着键的过期时间
    ...
} redisDb;
1
2
3
4
5
6
7

# Redis 内存淘汰机制

Redis 提供 6 种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

4.0 版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
  2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

  1. 保留热点数据:对于保留 Redis 热点数据来说,我们可以使用 Redis 的内存淘汰策略来实现,可以使用allkeys-lru淘汰策略,该淘汰策略是从 Redis 的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。
  2. 保证 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命令创建快照。
1
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        #让操作系统决定何时进行同步
1
2
3

AOFappendfsync触发机制是上面配置的三个参数决定的:noalwayseverysec。 可以根据对性能和持久化的实时性要求,具体配置。如果不知道哪种合适,就使用默认的everysec,这样即使出现系统崩溃,最多只会丢失1s之内产生的数据。

AOF文件远大于RDB文件,数据恢复速度比rdb慢。

# 写时复制 COW (Copy On Write)机制

  1. Redis使用操作系统的多进程写时复制(Copy On Write)机制来实现快照的持久化,在持久化过程中调用glibc(Linux下的C函数库)的函数fork()产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端的读写请求。
  2. 如果主线程收到的客户端的读写请求,需要修改某块数据,那么这块数据就会被复制一份到内存,生成该数据的副本,主进程在该副本上进行修改操作。所以即使对某个数据进行了修改,Redis持久化到RDB中的数据也是未修改的数据,这也是把RDB文件称为"快照"文件的原因,子进程所看到的数据在它被创建的一瞬间就固定下来了,父进程修改的某个数据只是该数据的复制品。

# AOF 文件过大怎么办?

执行BGREWRITEAOF命令对redisAOF进行重写(rewrite)机制

  1. 随着AOF文件越来越大,里面会有大部分是重复命令或者可以合并的命令
  2. 减少AOF日志大小,减少内存占用,加快数据库恢复时间

参考文档

Last Updated: 2 years ago