常规数据类型

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进行拉黑
    • 布隆过滤器:通过布隆过滤器快速判断数据是否存在,如果不存在及时返回