常规数据类型

string

  1. SDS 简单动态字符串
  2. 二进制安全(意思是输入任何字节都能正确处理,不会依赖于\0来判断字符是否结束),所以可以存储任何数据
  3. 最大能存储512MB
  4. 不仅能是字符串,还可以是数字
  5. 常用命令
    • get key
    • set key value
    • incr key :遇到这种指令时,会转换为int
    • decr key
    • mset key value [key value]
    • setex key seconds value :设置时效性
    • psetex key millionseconds value
    • strlen key
    • append key value
  6. 场景
    • 少量数据
    • 普通缓存:存储对象,
      • 将对象名和属性名结合到一起作为 key,值作为value
      • 利用json存储对象 set key {id:10,name:duan} get
    • 计数(因为是单线程的无需考虑并发问题):比如控制数据库表主键id,长地址转短地址的计数器
    • 时效性(setex):比如投票,一段时间内只能投一票

hash

  1. 短的时候用类数组(压缩列表?)、长的时候用字典(HashMap)。
  2. 常用命令
    • hget
    • hset
    • hdel
    • hmget
  3. 使用场景
    • 多数据,部分变更
    • 存储部分变更数据,比如用户信息
    • 购物车

list

  1. 常用命令
    • lpush
    • rpush
    • lpop
    • rpop
  2. 使用场景
    • 多数据,有顺序
    • 关注列表:最新消息的展示
    • 消息队列:多路日志合并,多服务器日志有序合并!

set

  1. 常用命令
    • sadd
    • spop
    • smembers key 得到所有数据
    • srandmember key 随机得到一个数据!
    • sunion
  2. 使用场景
    • 大量数据,便于查询,不重复元素
    • 朋友圈,交并差
    • 网站访问量统计
      • PV(网站被访问次数) string 计数器
      • UV(网站被不同用户访问的次数) set记录不同的cookie数量
      • IP(网站被不同IP地址访问的总次数 set记录不同的ip
  3. 用字典的key来存储数据,不仅查询快,还不允许重复!

zset

  1. 常用命令
    • zadd key value
    • zcard key :查询集合的成员个数
    • zrank key value:成员排名(下标位置)
    • zscore key value 获取指定值的分数
  2. 使用场景
    • 大量数据、不重复、有序
    • 排行榜

Redis的底层数据结构

简单动态字符串

  1. c语言的字符串本质是以‘\0’为结尾的字符数组(字符指针)
  2. redis的简单动态字符串本质同样是字符指针,但多了一个header,其中包括字符串的长度、容量等内容
  3. 简单动态字符串的指针指向的并不是其header的开头,而是真正数据的开头。
  4. 简单动态字符串是二进制安全的。
  5. https://segmentfault.com/p/1210000009155695/read#top

字典

  1. redis的字典本质就是哈希表。
  2. redis的rehash部分采用的是渐进式哈希,所谓的渐进式哈希简单来说就是分配一个新的哈希表,但并不立即把旧的哈希表中的元素转移到新的哈希表,而是将此过程分摊到之后的每一次的增删改查上,增操作直接在新表上进行,其余操作先在旧表上操作,找不到时再到新表上操作。每次增删改查操作之后都会判断一下是否正在进行rehash操作,并用一个变量来标注rehash的进度,用以在操作完成之后进行一次rehash操作。将以此变量为下标的旧表中的值转移到新表中,并将此变量加1.
  3. 何时进行rehash
    • 服务器没有执行 bgsave :哈希表负载因子大于等于1
    • 服务器正在执行 bgsave:负载因子大于等于5

双向链表

压缩列表

  1. 压缩列表和数组类似,但是不同于数组的是:数组的每个元素大小必须相同,但是为了节省空间,redis的压缩列表取消了这一限制,通过保存上一个节点的长度、数据长度等内容来计算每个元素的下标。

跳表

整数集合

持久化

RDB

  1. 保存数据快照
  2. save
    • 立即执行
  3. bgsave
    • 在后台选择合适的时间执行 save 操作。
  4. save配置何时启动 RDB 4 优点
    • 存储的是二进制文件,存储效率高
    • 存储的是某个时间点的数据快照,适用于数据备份
    • 恢复数据的速度比AOF快
  5. 缺点
    • 无法做大实时持久化,只能对某个时间点做备份
    • bgsave需要fork子进程
    • redis多版本的rdb文件格式不统一

AOF(Append-only file)

  1. 以独立日志的方式记录每次写命令,重启时在重新执行命令以恢复数据
  2. 当客户端发送一条指令时,先将命令存放到缓冲区,再按照一定的策略将命令写入aof文件,然后再执行
  3. aof写数据的三种策略
    • always(每次)
    • everysec(每秒)
    • no(系统控制)

AOF and RDB

持久化方式 RDB AOF
占用存储空间 小(二进制,压缩) 大(指令)
存储速度 慢(快照) 快(部分指令)
恢复速度 慢(指令重新执行)
数据安全性 会丢失数据 依据策略决定
启动优先级

如何选择

  1. 对数据非常敏感,建议使用AOF
  2. 数据呈现阶段有效性,建议使用RDB,比如游戏回档

分布式锁

  1. setnx key value 获得锁,如果 key为空,则获取锁成功,否则失败
  2. 使用完之后用 del 操作释放锁
  3. 解决死锁问题:在加锁之后,为key设置一个时间限制。
    • expire lock-key second
    • pexpire lock-key millionseconds

主从复制

  1. 定义
    • master 写数据/数据同步,slaves 读数据
  2. 作用(高可用)
    • 读写分离
    • 负载均衡
    • 故障恢复
  3. 工作流程
    • 建立连接(slave连master)
      • slave: slaveof(master_ip,master_port)
      • master 接收到指令,做出响应
      • slave 保存 master 的 ip,port
      • slave 根据保存信息建立 master 的socket
      • slave 周期性发送命令:ping
      • master 响应:pong
      • master验证授权
      • slave 发送自己的端口号
      • master 保存 slave 的端口号
    • 数据同步
      • slave: psync2 请求同步数据
      • master:bgsave(RDB)
      • master:创建命令缓冲区
      • master:生成RDB文件,通过socket发送给slave
      • slave:接收RDB,清空数据,执行RDB恢复过程(以上是全量复制)
      • slave:发送命令告知master RDB恢复完成。
      • master:发送复制缓冲区信息。
      • slave:接收信息,执行 bgrewriteaof,恢复数据。(以上是增量复制/部分复制,获取master在RDB期间收到的数据)
      • master:保存 slave 当前数据同步的位置。
    • 命令传播阶段

    • 复制缓冲区
      • 是一个先进先出的队列,用于存储服务器执行过的命令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区。
      • 将命令按照AOF存储格式拆分并添加\r\n之后,按照字节写入复制缓冲区。
      • 根据offset来区分不同slave当前数据传播的差异。
      • master和slave需要同时记录 offset 值。
    • 心跳机制
      • master
        • 指令:ping
        • 周期:10s
        • 作用:判断slave是否在线
      • slave
        • 指令:REPLCONF ACK(offset)
        • 周期:1s
        • 作用:汇报自己的复制偏移量,判断master是否在线
  4. 哨兵
    • 作用
      • 监控
      • 故障转移
    • 监控
      • 获取其他哨兵的状态
      • 获取master的状态
      • 获取所有slave的状态。
    • 故障转移
      • 哨兵发现 master 挂了,会将这个信息发送到哨兵内网中,如果超过半数的哨兵都认为 master 挂了,就说明 master 真的挂了。
      • 每个哨兵都竞选任命新 master 的任务
      • 每个哨兵都发起投票,每个哨兵都只有一票。
      • 获得超过半数票的哨兵来进行新 master 的任命。
      • 将在线的、反应快的、与原 master 断开时间最短的 slave 任命为 master。
  5. 集群

redis 单线程模型

  1. 指的是 server 和 client 之间交互过程是单线程的。

集群

常见问题

缓存预热

  1. 问题
    • 服务器启动后快速宕机
  2. 问题排查
    • 请求数量高
    • 主从之间数据吞吐量较大,数据同步操作频率高
  3. 解决方案
    • 统计访问频率较高的热点数据
    • 利用LRu数据删除策略
    • 根据统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据
    • 利用分布式多服务器同时进行数据读取,提速数据加载过程
    • 使用脚本程序固定出发数据预热过程

缓存雪崩

  1. 问题
    • 系统平稳运行过程中,忽然数据库连接量激增
    • 应用服务器无法及时处理请求
    • 大量408,500错误页面
    • 客户反复刷新页面获取数据
    • 数据库崩溃
    • 应用服务崩溃
    • 重启应用服务器无效
    • redis服务器崩溃
    • redis集群崩溃
  2. 问题排查
    • 在一个较短时间内,缓存中较多的key集中过期!
    • 在周期内请求访问过期数据,redis未命中,redis向数据库获取数据
    • 数据库同时接收到大量请求无法及时处理
    • redis大量请求被积压,开始出现超时
    • 数据库流量激增,数据库崩溃
  3. 解决方案
    • 随机设置redis缓存过期时间,如果过期时间相同,容易造成雪崩。
    • redis集群分布,把热点key放到不同的节点上
    • 跑定时任务,定时刷缓存
    • 更多页面静态化处理
    • 构建多级缓存架构
      • NGinx+redis+ehcache
    • 灾难预警
    • 限流、降级

缓存击穿

  1. 问题本质
    • 热点数据缓存失效,然后同一时间大量请求直接涌入数据库,导致数据库崩溃
  2. 解决方案
    • 缓存永不过期
    • 分布式锁:请求数据库操作加锁,只有一个线程可以抢到锁,其他抢不到锁的线程先睡几毫秒,然后重新去redis读取即可。

缓存穿透

  1. 本质
    • 恶意用户请求数据库中不存在的数据,从而导致该请求穿透redis缓存,直达数据库
  2. 解决方案
    • 如果数据库中没有对应数据,在redis中设置一个空值对应这个请求
    • 对请求规范性进行检查
    • 对恶意ip进行拉黑
    • 布隆过滤器:通过布隆过滤器快速判断数据是否存在,如果不存在及时返回