Redis

Table of Contents

1. 安装

  • 本地使用建议直接用 docker 启动(使用 Docker 安装没有配置文件,只能使用 CONFIG GET * 来查看) docker run --name redis-6 -d --restart=always -p 127.0.0.1:6379:6379 redis:6
  • 生产直接基于宿主机安装

2. 使用基础

2.1. 数据结构和应用场景

  • strings:最常用。
  • Lists:内存存储保持插入顺序,使用链表实现,首位插入和删除效率不受链表长度的影响,1 个和一百万个相同。 应用场景:
    • 消息队列(mq),LPUSH 生产消息,(B)RPOP 消费消息
  • Sets:唯一的无序的元素集合。
  • Sorted Set:和 Sets 类似,但是是有序的,顺序通过 score 来控制;可以通过顺序索引元素,比如前 10 个或者后十个。 应用场景:
    • 排行榜
  • Hashes:值是一个 hash 表。
  • 位图(bitmaps):存储都是 0 或者 1 ,可以通过位置设置 0 和 1, 提供一些换算的接口。
  • HyperLogLogs:用于统计集合中的计数。
  • Streams

2.2. Key 使用建议(标准化)

可以使用任何二进制的序列作为 key,当然最常用的是 string。

  • 不建议太长,比如 1024 字节的 key 在存储时不仅占内存,而且索引比较低效。对 key 统一做 SHA1 是个好主意。
  • 太短也不好,尽管占用空间小了,但是可读性变差了。需要在可读性和存储空间之间做一个权衡。统一使用一种规范是个好主意。 比如用 : 分割句子,用 . 分隔单词
  • 最大允许的 key 长度为 512MB。

3. Command

  • 启动: redis-server redis.conf
  • 关闭: redis-cli shutdown
  • 客户端连接: redis-cli -h <hostname> -p <port> -a <password>
  • 退出客户端: QUIT
  • INFO
  • CLIENT LIST: 连接的客户端列表
  • DBSIZE: 返回当前数据库的 KEY 的数量
  • MONITOR: 实时打印出 Redis 服务器接收到的命令,调试用
  • redis-cli KEYS "a0002*" | xargs redis-cli DEL: delete keys with pattern
  • FLUSHALL: 清空整个 Redis 服务器的数据
  • FLUSHDB: 清空当前数据库中的所有 key
  • LASTSAVE: 返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示
  • TYPE <key> 查看 key 的类型

4. 官方最佳实践

  • Pipelining 一次性发送多个命令,节省 RTT(round trip time,一次调用的往返时间)
  • Redis Pub/Sub 快速且稳定的发布、订阅消息系统(是及时通讯的,不带存储,所以 MQ 的很多场景覆盖不到)
  • Expires Redis 过期设计和回收机制。
    • 如果 key 创建的时候没有设置过期时间,key 会一直存在(除非 DEL)
    • Redis 2.4 中过期精准度在 0-1 秒之间;之后版本精准度在 0-1 毫秒
    • 过期(回收)机制:

      • 访问时触发检查,被动过期
      • 主动过期(随机测试),具体流程(Redis 每秒钟执行 10 次):
        1. 从有过期时间的 key 中随机测试 20 个 key
        2. 删除发现过期的 key
        3. 如果超过 25% 的 key 是过期的,从第一步继续重复执行

      简单的概率算法,找样本,保证可能过期 key 的百分比低于 25%。保证在任何时候,内存过期的密钥等于每秒中最大写入操作量的 1/4

  • LRU cache 配置 Redis 固定内存量,作为自动驱逐键的缓存(这种情况下只能用作 cache)。
    • maxmemory 指定最大内存使用限制,如果值为 0 表示不限制。 policies 配合 maxmemory 一起使用,设定内存满之后的驱逐策略,支持的驱逐策略:
      • noviction 内存满之后写入报错
      • allkeys-lru 所有 key 按照 LRU 算法驱逐
      • volatile-lru 有有效期的 key 按照 LRU 算法驱逐
      • allkey-random 随机驱逐
      • volatile-random 同理,在设置了有效期的 key 中随机驱逐
      • volatile-ttl 同理,在设置了有效期的 key 中按照 TTL 较短的驱逐
    • 驱逐进程工作原理:被动驱逐,由客户端执行命令触发
  • Redis Transactions:一组命令组合执行,保证原子性(同时成功、同时失败),MULTI、EXEC、DISCARD 和 WATCH 是基础
  • 客户端侧缓存 Redis 6 开始支持客户端侧缓存,数据缓存在内存中,不写入 Redis server。当客户端引入缓存时,也就是客户端本身 是有状态的,会面临数据同步问题(即 Tracking)
  • 批量插入数据
  • 分布式锁

5. Redis cluster

5.1. 数据分片

集群没使用一致的散列方式,而是使用不同形式的分片,每个 key 在概念上都称为 hash slot 的一部分。

在 Redis 集群中一共有 16384 个 hash slot,根据给定的 key 计算出属于哪一个 hash slot。算法为 =CRC16(key)/16384=。

集群中的每个节点都只负责 hash slot 的自己,因此假设你有 3 个节点的集群:

  • A 节点包含 0~5500 的 hash slot;
  • B 节点包含 5501~11000 的 hash slot;
  • C 节点包含 11001~16383 的 hash slot;

有关一致性 hash 可以查看我之前翻译的这篇文章:https://www.zhangjiee.com/blog/2018/consistent-hashing.html

6. 持久化

Redis 有两种持久化的方式:快照(RDB 文件)和追加式文件(AOF 文件):

  • RDB 持久化方式会在一个特定的间隔保存那个时间点的一个数据快照;
  • AOF 持久化方式则会记录每一个服务器收到的写操作。在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据。 写操作命令记录的格式跟 Redis 协议一致,以追加的方式进行保存;
  • Redis 的持久化是可以禁用的,就是说你可以让数据的生命周期只存在于服务器的运行时间里;
  • 两种方式的持久化是可以同时存在的,但是当 Redis 重启时,AOF 文件会被优先用于重建数据;

注意,Redis 一般作为单纯的缓存,也就意味着都是可被重建的数据,所以一般无需持久化(持久化会为运维带来成本)。 还有些公司会将 Redis 作为消息中间件(PUB/SUB),在安全性要求比较高的业务场景下,使用持久化也不咋靠谱, 单点就不说了,Cluster 在添加或者删除节点的时候也有丢失数据的风险,所以高安全性的消息中间件推荐使用 Kafka,而不是 Redis。

下面只是作为知识扩展:

6.1. RDB

优点:

  • RDB 文件是一个很简洁的单文件,它保存了某个时间点的 Redis 数据,很适合用于做备份。 你可以设定一个时间点对 RDB 文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本;
  • 基于上面所描述的特性,RDB 很适合用于灾备。单文件很方便就能传输到远程的服务器上;
  • RDB 的性能很好,需要进行持久化时,主进程会 fork 一个子进程出来, 然后把持久化的工作交给子进程,自己不会有相关的 I/O 操作;
  • 比起 AOF,在数据量比较大的情况下,RDB 的启动速度更快;

缺点:

  • RDB 容易造成数据的丢失。假设每 5 分钟保存一次快照,如果 Redis 因为某些原因不能正常工作,那么从上次产生快照到 Redis 出现问题这段时间的数据就会丢失了;
  • RDB 使用 fork() 产生子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成 Redis 停止服务几毫秒。如果数据量很大且 CPU 性能不是很好的时候,停止服务的时间甚至会到 1 秒;

6.2. AOF

快照并不是很可靠。如果你的电脑突然宕机了,或者电源断了,又或者不小心杀掉了进程,那么最新的数据就会丢失。而 AOF 文件则提供了一种更为可靠的持久化方式。每当 Redis 接受到会修改数据集的命令时,就会把命令追加到 AOF 文件里,当你重启 Redis 时,AOF 里的命令会被重新执行一次,重建数据。

优点:

  • 比 RDB 可靠。你可以制定不同的 fsync 策略:不进行 fsync、每秒 fsync 一次和每次查询进行 fsync。默认是每秒 fsync 一次。这意味着你最多丢失一秒钟的数据;
  • AOF 日志文件是一个纯追加的文件。就算是遇到突然停电的情况,也不会出现日志的定位或者损坏问题。甚至如果因为某些原因(例如磁盘满了)命令只写了一半到日志文件里,我们也可以用 redis-check-aof 这个工具很简单的进行修复;
  • 当 AOF 文件太大时,Redis 会自动在后台进行重写。重写很安全,因为重写是在一个新的文件上进行,同时 Redis 会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操作命令的集合。当新文件重写完,Redis 会把新旧文件进行切换,然后开始把数据写到新文件上;
  • AOF 把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如我们不小心用 FLUSHALL 命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来;

缺点:

  • 在相同的数据集下,AOF 文件的大小一般会比 RDB 文件大;
  • 在某些 fsync 策略下,AOF 的速度会比 RDB 慢。通常 fsync 设置为每秒一次就能获得比较高的性能,而在禁止 fsync 的情况下速度可以达到 RDB 的水平;
  • 在过去曾经发现一些很罕见的 BUG 导致使用 AOF 重建的数据跟原数据不一致的问题;

6.3. 从 RDB 切换到 AOF

这里只说 Redis >= 2.2 版本的方式:

备份一个最新的 dump.rdb 的文件,并把备份文件放在一个安全的地方。

运行以下两条命令:

$ redis-cli config set appendonly yes
$ redis-cli config set save ""
  • 确保数据跟切换前一致;
  • 确保数据正确的写到AOF文件里;

第二条命令是用来禁用 RDB 的持久化方式,但是这不是必须的,因为你可以同时启用两种持久化方式。

记得对配置文件 redis.conf 进行编辑启用 AOF,因为命令行方式修改配置在重启 Redis 后就会失效。

7. Resource

First created: 2017-12-04 10:58:35
Last updated: 2023-01-01 Sun 17:09
Power by Emacs 29.0.91 (Org mode 9.6.6)