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
高可用
其他功能:
- Transactions
- Pub/Sub
- Lua scripting
- Keys with a limited time-to-live
- LRU eviction of keys
- Automatic failover
Getting started
测试本地的 Redis
1 | $ redis-cli ping |
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 数据类型
简介
keys 二进制安全,可以是任何东西,String 或者 JPEG file
keys 规则
- 不要太长。超过 1024B 会浪费内存
- 不要太短。
u1000flw
并不比user:1000:followers
好,不要仅仅为了短而放弃语义 - 最好有格式。例如:
object-type:id
- 最大为 512MB
keys command
exists <key>
返回 0/1del <key>
type <key>
如果不存在,返回 none
keys 过期(到期自动销毁)
精度可以是秒/毫秒(实际上都是毫秒);Redis 记录的是过期的时刻,所以即使 Redis 关机,到点还是会自动销毁
expire <key> <time>
设置 TTL(已经设置 key 的情况下)set <key> <value> ex <time>
同时设置 key 的内容和 TTLttl <key>
查看某个 key 剩下 TTLpersist <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:
- 长列表添加元素快
Redis Lists can be taken at constant length in constant time
使用场景:
进程间交流,消费者/生产者 模式
For example both the popular Ruby libraries resque and sidekiq use Redis lists under the hood in order to implement background jobs.
记录用户最近更新的内容(社交网络)
The popular Twitter social network takes the latest tweets posted by users into Redis lists. @a 视频,大概在 14:20 开始,中文有关文章 Twitter的Timeline是怎样服务数亿用户的? & 系统设计面试题-实现Twitter时间线和搜索
类似,名人新发 twitter 就 LPUSH 到所有粉丝的 timeline list 中,粉丝刷新主页时就查该粉丝的
LRANGE 0 9
timeline list,返回最近的 10 张
command
llen <key>
lpush/rpush <key> <value> [value1 value2...]
会返回当前 list 长度lpop/rpop <key>
从左/右取值(如果不存在返回 nil)注意,当所有元素被 pop 之后,
exists <key>
为0,即自动删除该 listlrange <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>
获取某个值的正序/逆序 indexzdd <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>
越界返回 0bitcount <key>
计算有多少个 1bitop <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 | pfadd hll a b c d |
使用场景:counting unique queries performed by users in a search form every day
计算用户每天在表单中输入的不重复查询条件的数目
总结
大部分基础类型的大部分操作都是 O(1) 的,sorted set 是 O(log(N))
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 名符合规则的 channelunsubscribe <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 指令
可用性保证
- HA with Sentinel 哨兵
功能:监控、通知、自动故障转移、配置 provider
Fundamental things to know about Sentinel before deploying
- 三台分离的物理/虚拟机
- 需要客户端支持
- Replication 镜像
- 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