Redis 详解

数据结构

image-20230207182220403

String 应用场景

单值对象

对象缓存

 1. 可以直接 SET 对象,将对象序列化,存储 json 对象
 2. 可以 MSET 对象,将所有属性单独序列化,便于频繁修改对象属性值(只用修改这一个值)

分布式锁

1
2
3
SETNX product:10001 true // 返回 1 是取锁成功 0 是失败
DEL product:10001 // 执行释放锁
SET product:10001 true ex 10 nx // 防止程序意外终止导致死锁

计数器

1
INCR ariticle:readCount:{ 文章 id}

Web 集群 session 共享

spring session + Redis 实现共享

分布式系统全局序列号
1
iNCRBY orderId 1000 // Redis 批量生成序列号

哈希 Hash 应用场景

对象缓存

1
HMSET user {userId}:name dd {userId}:balance 555 // 即将对象属性拆分为一个 map 集合 大对象存储需要用分段存储

电商购物车

以用户 Id 为 key,商品 id 为 field,商品数量为 value

1
2
3
4
5
hset cart:1001 55 1 // 添加商品
hincrby cart:1001 55 1 // 添加数量
hlen cart:1001 // 商品总数
hdel cart:1001 55 // 删除商品
hgetall cart:1001 // 获取购物车所有商品

优缺点

优点

  • 同类数据归类整合存储,方便管理
  • 比 stirng 消耗内存与 cpu 更小
  • 比 string 更节省空间

缺点

  • 过期功能只能用在 key 上,不能用于 field 上
  • Redis 集群下不适合大规模使用(容易集中到一个机子上)

列表 list 应用场景

常用命令

image-20230208102557381

常用数据结构

Stack(栈) = LPUSH + LPOP =FILO

Queue(队列) = LPUSH + RPOP

Blocking MQ(阻塞队列) = LPUSH + BRPOP

微博和微信公众号消息流

​ 如发消息,使用队列,然后用 LRANGE 拿出来

微博小 V 使用 push 大 V 使用 pull

  • push 是小 V 放入 Redis 中,用户拿自己消息队列中的消息
  • pull 是用户去拿大 V 发过的消息排列

Set 应用场景

微信抽奖小程序

1
2
3
4
5
6
7
// 1. 参与抽奖
SADD key {userId}
// 2. 查看抽奖所有用户
SMEMBERS key
// 3. 抽取 count 名中奖用户
SRANDMEMBER key [count] // 抽完后数据都在
SPOP key [count] // 抽完后删除被抽取元素

微信微博点赞,收藏,标签

1
2
3
4
5
SADD like:{ 消息 ID} { 用户 ID} // 点赞
SREM like:{ 消息 ID} { 用户 ID} // 取消点赞
SISMEMBER like:{ 消息 ID} { 用户 ID} // 检查是否点赞
SMEMBERS like:{ 消息 ID} { 用户 ID} // 获取点赞用户列表
SCARD like:{ 消息 ID} // 获取点赞用户量

集合操作

1
2
3
SINTER set1 set2 set3 // 获取交集
SUNION set1 set2 set3 // 获取合集
SDIFF set1 set2 set3 // 以第一个合集为基础,减去 set2 和 set3 的合集,第一个合集剩下的元素的集

微博微信关注模型

1
2
3
4
5
6
7
AAset -> {CC, DD} // AA 关注人
BBset -> {AA, CC, DD, EE} // BB 关注人
CCset -> {AA, BB, DD, EE, FF} // CC 关注人
SINTER AAset BBset -> {CC, DD} // AA 和 BB 共同关注
SISMEMBER CCset BB // AA 关注的人 (CC) 也关注 BB
SISMEMBER DDset BB // AA 关注的人 (DD) 也关注 BB
SDIFF BBset AAset -> {AA, EE} // AA 可能认识的人

电商商品筛选

image-20230208111132336

Zset 集合应用场景

排行榜

1
2
3
4
ZINCRBY hotNews:20190819 1 // 点击新闻
ZREVRANGE hotNews:20190819 0 9 WITHSCORES // 展示排行前十
ZUNIONSTORE hotNews:20190813-20190819 7 // 七日搜索榜单
ZREVRANGE hotNews:20190813-201908190 9 WITHSCORES // 展示 7 日排行前十

Redis 的单线程和高性能

Redis 是单线程吗

​ 主要是指网络 IO 和键值对读写是单线程,但是其他功能,如持久化、异步删除、集群数据同步是额外线程执行

Redis 为什么单线程还快?

​ 所有数据都在内存中

Redis 单线程如何处理并发客户端连接?

​ IO 多路复用 (NIO),使用 epoll 实现的,将连接信息和事件放入队列中,依次放到文件事件分派器,分派器再将事件分发给事件处理器

​ 默认最大连接数 1w

Redis 如何做分页

1
scan cursor [MATCH pattern] [COUNT count] // cursor 游标,即查询起点

Redis 底层数据结构简析

​ 类似于 HashMap ,一个数组下的键值对的链表,cursor 即数组的位置,因为没有并发涉及,查询时插入会导致数据有误

Redis 持久化

RDB、AOF 及混合持久化

RDB 快照

1
2
save 60 10000 // 同步保存 每 60 秒内有 10000 次操作,将其保存至.rdb 文件
bgsave // 异步保存 通过操作系统的写时复制,将其 fork 新的的线程

如果故障,服务器会丢失最近写入、未保存到快照中的数据

AOF 持久化

将 ** 修改 ** 的每一条指令记录文件 appendonly.aof 文件中(先写入 os cache,再隔一段时间 fsync 到磁盘)

1
2
3
4
appendonly yes // 开启持久化
apendonly always // 每次有新命令都追加到 aof 文件时,fsync 到磁盘
appendfsync everysec // 每秒 fsync 一次磁盘
appendfsync no // 从不主动 fsync,由操作系统处理
AOF 重写

定期根据内存最新数据生成 aof 文件

1
2
auto-aof-rewrite-percentage 100 // 自上次重写后增长 100% 再次触发重新
auto-aof-rewrite-min-size 64mb // aof 文件至少到 64M 才会自动重写,文件太小了恢复速度本来就很快,重写意义不大

重写也会 fork 一个子进程去做

AOF 与 RDB 比较

命令 RDB AOF
启动优先级
体积
恢复速度
数据安全性 容易丢数据 根据策略决定

混合持久化

1
aof-use-rdb-preamble yes

将每次 ** 重写 ** 时,将之前的内存做 RDB 快照处理,并将快照内容和 ** 增量 **AOF 修改命令存在一起,等重写完后再将名字改为
appendonly.aof,将原有文件覆盖

Redis 备份策略

  1. 写 crontab 定时调度脚本,每小时 copy 一份 rdb 或 aof 备份到目录中,仅保存最近 48 小时的备份
  2. 每天保留一份当日数据
  3. 每次 copy 备份时,把太久的删除
  4. 每晚当前机器备份复制到其他机器上

Redis 主从配置

配置步骤

  1. 复制 Redis.conf 文件

  2. 将相关配置修改

    1
    2
    3
    4
    port 6380
    pidfile /var/run/Redis_6380.pid # 把 pid 写进 pidfile 配置文件中
    logfile "6380.log"
    dir /usr/local/Redis5.0.3/data/6380 # 指定数据存放目录
  3. 配置主从复制

    1
    2
    replicaof 192.168.0.60 6379 # 从本机 6379 的 Redis 实例复制数据
    replica-read-only yes # 配置只读
  4. 启动从节点

    1
    Redis-server Redis.conf
  5. 连接从节点

    1
    Redis-cli -p 6380

原理