Redis Doc

Redis(REmote DIctionary Server) 的 文档,以下按照 Redis 文档顺序介绍。主要是 About, Getting started, User interfaces, Data types 这几部分

官方给出的使用场景 use cases

  • real-time data store 实时数据存储
  • caching & session storage 缓存 & Session 存储
  • streaming & messaging 流 & 消息传递

About

数据结构:

  • strings, hashes, lists, sets, sorted sets
  • 特殊:bitmaps, hyperloglogs, geospatial indexes, streams

磁盘持久化 on-disk persistence

高可用

其他功能:

  • Transactions
  • Pub/Sub
  • Lua scripting
  • Keys with a limited time-to-live
  • LRU eviction of keys
  • Automatic failover

Getting started

测试本地的 Redis

1
2
$ redis-cli ping
PONG

redis-cli 默认连接端口 6379,非默认端口需要查看 redis-cli --help

Redis 安全

默认绑定到所有接口(0.0.0.0),没有任何身份验证。解决:

  • 防火墙限制 6379 端口(集群+ 16379,哨兵 +26379)
  • 配置 bind 属性(例如:仅 loopback(即本地))

    @q 这里的 bind 不是很理解,是对外开放的权限,类似白名单?

  • 通过 requirepass 属性设置密码
  • 加密 server/client 通道,例如 HTTPS

持久化

how Redis persistence works on this page

默认是 data set snapshot 模式(for instance after at least five minutes if you have at least 100 changes in your data @a 不知道是不是 5 分钟内 100 变化)

手动持久化指令 redis-cli SAVE
建议使用 redis-cli SHUTDOWN 来关闭 Redis

安装

建议使用 init script 来安装

FAQ 有些资源可以参考一下

User interfaces

  • CLI

  • RedisInsight 官方的 GUI 工具

    参考 Tools 还有更多

Data types 数据类型

简介

From

  • keys 二进制安全,可以是任何东西,String 或者 JPEG file

  • keys 规则

    1. 不要太长。超过 1024B 会浪费内存
    2. 不要太短。u1000flw 并不比 user:1000:followers 好,不要仅仅为了短而放弃语义
    3. 最好有格式。例如:object-type:id
    4. 最大为 512MB
  • keys command

    • exists <key> 返回 0/1
    • del <key>
    • type <key> 如果不存在,返回 none
  • keys 过期(到期自动销毁)

    精度可以是秒/毫秒(实际上都是毫秒);Redis 记录的是过期的时刻,所以即使 Redis 关机,到点还是会自动销毁

    • expire <key> <time> 设置 TTL(已经设置 key 的情况下)
    • set <key> <value> ex <time> 同时设置 key 的内容和 TTL
    • ttl <key> 查看某个 key 剩下 TTL
    • persist <key> 取消过期

strings

使用场景:缓存 HTML 段/页

command

  • set <key> <value> [nx/xx]mset <key> <value>[<key1> <value1>...]

    nx 不存在才设置;xx 存在才设置

  • get <key>mget <key>[<key1> <key2>...]

  • incr/decr <key>incr-by-float

  • incrby/decrby <key> <num> 增加/减少指定数(使用 incr 需要 value 为 num)

其他:

  • value 最大 512MB

lists

底层是 linked-list,头尾插入/取出很快,索引检索慢。为什么用 linked-list:

  1. 长列表添加元素快
  2. Redis Lists can be taken at constant length in constant time

使用场景

command

  • llen <key>

  • lpush/rpush <key> <value> [value1 value2...] 会返回当前 list 长度

  • lpop/rpop <key> 从左/右取值(如果不存在返回 nil)

    注意,当所有元素被 pop 之后,exists <key> 为0,即自动删除该 list

  • lrange <key> <start-index> <end-index>

    其中,-1 是最后一个元素。打印所有列表为 0,-1。大致 O(n) 操作,除非只是查头尾的少部分数据

    @a 这里 lrange 开头的 l 应该是 list 的意思

  • ltrim <key> <start-index> <end-index> 取特定索引变成新列表

  • brpop/blpop <key> [time] block 取值,后面的时间表示最长等待时间(为 0 的话永久等待,超时结束返回 null)

    主要用于实现 生产-消费者 模式。使用非 block 指令的话需要消费端做额外处理(例如,定时请求。缺点:1. 无效沟通(没有的话返回 null);2. 定时的间隔带来延迟)

    注意:可以等待多个 list;如果多个 client 在等待,则按开始等待的时间轮询逐个消费;返回一个数组,包含 list-key, pop-value 两个内容

  • 另:指令 lmove, blmove 实现 safer queues or rotating queues

    两个 list 之间移动元素,LMOVE <source> <target> <LEFT|RIGHT> <LEFT|RIGHT>(后面的两个左右分别表示从 source 的左/右取出内容放到 target 的左/右)

  • 更多参考

hashes

field-value pairs

command(hset 的 h 表示 hashes)

  • hset <key> <field> <value> [field1 value1 field2 value2 ...]

  • hget <key> <field>hmget <key> <field1> [field2 field3 ...]

    hmget 返回数组

  • hgetall <key> O(n) 时间复杂度

  • hincrby <key> <field> [num] 给 hashes 的某个 field 自增(要求保存的内容可以转为数字)

sets

使用场景

  • 表达对象之间的关系(可以用来实现 tag 功能)

    1
    2
    3
    4
    5
    6
    7
    // 为文章保存对应的 tags
    > sadd news:1000:tags 1 2 5 77
    (integer) 4

    // 具体的 tag 下面有哪些文章
    > sadd tag:1:news 1000
    (integer) 1

    @a 通过冗余的方式来保存,因为 Redis hashes 的 value 没有能力保存 Redis 结构,只能通过简介的方式来查询(缺少引用这种数据结构)

  • 跟踪唯一的项目(例如:跟踪 blog 文章的访问 IP)

  • 集合操作(例如:交集/并集/补集)

command(sadd 的 s 表示 sets)

  • sadd <key> <value> [value1 value2 ...]
  • smemebers <key> 打印 sets(O(n) 时间复杂度,可以考虑 sscan 指令)
  • scard <key> 获取长度(在 set 中,长度一般对应英文 cardinality)
  • sismember <key> <value> 判断是否存在(返回 0/1)
  • sinter <key> [key1 key2 key3...] 取交集
  • sunionstore <key> [key1 key2 key3...] 取并集
  • spop <key> 取出任意一个元素(s-rand-member 打印任意一个元素,不会取出)

sorted sets

类似 set 和 hash 的混合

实现原理:

  • add 元素时需要为元素指定一个 score(float 值)。先按 score 排序,如果同,则按 value 的字典序排序
  • Sorted sets are implemented via a dual-ported data structure containing both a skip list and a hash table, so every time we add an element Redis performs an O(log(N)) operation. 底层:跳表+哈希表

使用场景:

  • 排行榜 leaderboards(通过更新 score 实现)
  • API 限流。滑动窗口 sliding-window

command(zadd 的 z 表示 sorted sets)

  • zadd <key> <score> <value> [score1 value1 score2 value2...]

  • zrange/zrevrange <key> <start-index> <end-index> [withscores] 正序/逆序 打印(后面的 withscores 指定是否需要打印 scores 值)

    • 获取前三名 zrange leaderboards 0 2 rev withscores(注意的 rev 参数)
  • zrangebyscore <key> <start-score> <end-score> 筛选打印(==边界的也会打印)

  • z-rem-range-by-score <key> <start-score> <end-score> 移除选中的值(==边界的也会被移除)

    其中,可用 -inf 表示负无穷大

  • zrank/zrevrank <key> <value> 获取某个值的正序/逆序 index

  • zdd <key> <new-score> <exists-value> 更新 score(O(log(N)) 操作)

  • 字典序操作指令 z-range-by-lex z-rev-range-by-lex z-rem-range-by-lex z-lex-count

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 这里的 score 都是一样的
    > zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0 "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"
    0 "Linus Torvalds" 0 "Alan Turing"

    # 这里的 [B [P 语法
    > zrangebylex hackers [B [P
    1) "Claude Shannon"
    2) "Hedy Lamarr"
    3) "Linus Torvalds"

bitmaps

Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type. 不是一种实际的数据类型,只是在 String 类型上面的位操作

因为 key 有 512MB 的限制,所有 bitmaps 最大有 2^32 bits(2^9 * 2^10 * 2^10 * 2^3),大概 40 亿(2^30 等于 十亿)

使用场景:

  • 各种各样的实时分析
  • 储存与对象ID相关的空间有效但性能高的布尔信息(Storing space efficient but high performance boolean information associated with object IDs)
  • 例子
    • One of the biggest advantages of bitmaps is that they often provide extreme space savings when storing information. For example in a system where different users are represented by incremental user IDs, it is possible to remember a single bit information (for example, knowing whether a user wants to receive a newsletter) of 4 billion of users using just 512 MB of memory. 只需使用 512MB 的内存,就可以记住 40亿 用户的一个比特信息(例如,知道一个用户是否想收到新信息)

    • 统计 “最长连续登录天数”

      • 一个用户一个 bitmap
      • 某个用户登录,setbit login-days:users:1 count-day(today - website-public-day) 1
      • 使用 bitcount 统计用户总登录天数;使用 bitpos 统计最长连续登录天数
    • 统计 “日活跃用户数” setbit daily-active:2023-01-01 f(user_id) 1

      其中 f(user_id) 如果是递增 ID 可以是 user_id%(2^32)(不是递增 ID 的话可以考虑注册时间)

      仅仅只是数目的话,还可以考虑 hyperloglog

    • 传统的指标计算只能通过批处理处理,使用 bitmap 可以实时计算,参考

command

  • setbit <key> <bit-number> <1/0>

  • getbit <key> <bit-number> 越界返回 0

  • bitcount <key> 计算有多少个 1

  • bitop <AND | OR | XOR | NOT> destkey key [key ...] 通过 and/or/xor/not 操作 bitmap 并将结果放到 destkey 的 bitmap 中(@a 应该较少使用)

  • bitpos key bit [start [end [BYTE | BIT]]] 返回字符串里面第一个被设置为 1 或者 0 的 bit 位 参考

    BYTE | BIT 用来指定 start/end 的单位,缺省是 BYTE

hyperLogLogs, HLL

HyperLogLog is a data structure that estimates the cardinality of a set. 用来估计 set 的大小(对比 set 优点是:仅占用大致恒定的内存,最大 12KB,误差在 0.81%)

1
2
3
4
> pfadd hll a b c d
(integer) 1
> pfcount hll
(integer) 4

使用场景:counting unique queries performed by users in a search form every day 计算用户每天在表单中输入的不重复查询条件的数目

总结

  1. 大部分基础类型的大部分操作都是 O(1) 的,sorted set 是 O(log(N))

  2. string 批量操作需要特殊命令 mget,mset,其他的基础类型不需要

    list: lpush, set: sadd, hash: hset

Using Redis

@a 感觉像是 Redis 客户端开发或者直连 Redis 才会用到

Redis Pub/Sub

command

  • publish <channel> <message> 返回接受到消息的 client 编号

  • subscribe <channel> [channel1 channel2 ...]

  • psubscribe <channel-name-pattern> 订阅 channel 名符合规则的 channel

  • unsubscribe <channel> [channel1 channel2 ...]

    redis-cli 无法使用该指令,只能 Ctrl-C 退出

  • spublish, ssubscribe, sunsubscribe 针对 sharded channel 的操作

    @a shard 分片。sharded channel 主要使用在集群模式下,可以减少集群中消息的交换,大概是会跟踪到 sub 所在的位置,然后往特定的主机发送

Managing Redis

Security

  • 3.2.0 版本开始,在默认配置的情况下,以 protected mode 启动,只能以 127.0.0.1 访问

  • 建议使用 ACL

    比在 redis.conf 中配置 requirepass 属性更为安全

  • 启用 TLS

  • 重命名特定指令,例如 config 指令

可用性保证

  1. HA with Sentinel 哨兵
    功能:监控、通知、自动故障转移、配置 provider

Fundamental things to know about Sentinel before deploying

  • 三台分离的物理/虚拟机
  • 需要客户端支持
  1. Replication 镜像
  2. Scalling 集群

Persistence 持久化

  • RDB(Redis Database) point-in-time snapshots
  • AOF(Append Only File) logs every write operation received by the server

优缺:

  • RDB 恢复比 AOF 快
  • RDB 可能丢失更多的数据(取决于定时时间)
  • RDB 使用 fork() 来执行 snapshots,可能影响性能(数据多的情况下更明显)
  • 在 log 过大的时候 AOF 会自动重写,而且依然安全(重写出生成当前数据的最简指令)
  • AOF 在无意中执行了 flushall 之后恢复的几率更大
  • AOF 文件一般比 RDB 大

可以同时启动,如果同时启动之后需要恢复优先使用 AOF,而且 Redis 可以保证在创建 snapshots 的时候不会执行 AOF 的重写,在执行重写的时候不会创建 snapshots