Redis
Contents
常规数据类型
string
- SDS 简单动态字符串
- 二进制安全(意思是输入任何字节都能正确处理,不会依赖于\0来判断字符是否结束),所以可以存储任何数据
- 最大能存储512MB
- 不仅能是字符串,还可以是数字
- 常用命令
- 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
- 场景
- 少量数据
- 普通缓存:存储对象,
- 将对象名和属性名结合到一起作为 key,值作为value
- 利用json存储对象 set key {id:10,name:duan} get
- 计数(因为是单线程的无需考虑并发问题):比如控制数据库表主键id,长地址转短地址的计数器
- 时效性(setex):比如投票,一段时间内只能投一票
hash
- 短的时候用类数组(压缩列表?)、长的时候用字典(HashMap)。
- 常用命令
- hget
- hset
- hdel
- hmget
- 使用场景
- 多数据,部分变更
- 存储
部分变更数据
,比如用户信息 - 购物车
list
- 常用命令
- lpush
- rpush
- lpop
- rpop
- 使用场景
- 多数据,
有顺序
- 关注列表:最新消息的展示
- 消息队列:多路日志合并,多服务器日志有序合并!
- 多数据,
set
- 常用命令
- sadd
- spop
- smembers key 得到所有数据
- srandmember key 随机得到一个数据!
- sunion
- 使用场景
- 大量数据,便于查询,不重复元素
- 朋友圈,交并差
- 网站访问量统计
- PV(网站被访问次数) string 计数器
- UV(网站被不同用户访问的次数) set记录不同的cookie数量
- IP(网站被不同IP地址访问的总次数 set记录不同的ip
- 用字典的key来存储数据,不仅查询快,还不允许重复!
zset
- 常用命令
- zadd key value
- zcard key :查询集合的成员个数
- zrank key value:成员排名(下标位置)
- zscore key value 获取指定值的分数
- 使用场景
- 大量数据、不重复、有序
- 排行榜
Redis的底层数据结构
简单动态字符串
- c语言的字符串本质是以‘\0’为结尾的字符数组(字符指针)
- redis的简单动态字符串本质同样是字符指针,但多了一个header,其中包括字符串的长度、容量等内容
- 简单动态字符串的指针指向的并不是其header的开头,而是真正数据的开头。
- 简单动态字符串是二进制安全的。
- https://segmentfault.com/p/1210000009155695/read#top
字典
- redis的字典本质就是哈希表。
- redis的rehash部分采用的是渐进式哈希,所谓的渐进式哈希简单来说就是分配一个新的哈希表,但并不立即把旧的哈希表中的元素转移到新的哈希表,而是将此过程分摊到之后的每一次的增删改查上,增操作直接在新表上进行,其余操作先在旧表上操作,找不到时再到新表上操作。每次增删改查操作之后都会判断一下是否正在进行rehash操作,并用一个变量来标注rehash的进度,用以在操作完成之后进行一次rehash操作。将以此变量为下标的旧表中的值转移到新表中,并将此变量加1.
- 何时进行rehash
- 服务器没有执行 bgsave :哈希表负载因子大于等于1
- 服务器正在执行 bgsave:负载因子大于等于5
双向链表
压缩列表
- 压缩列表和数组类似,但是不同于数组的是:数组的每个元素大小必须相同,但是为了节省空间,redis的压缩列表取消了这一限制,通过保存上一个节点的长度、数据长度等内容来计算每个元素的下标。
跳表
整数集合
持久化
RDB
- 保存数据快照
- save
- 立即执行
- bgsave
- 在后台选择合适的时间执行 save 操作。
- save配置何时启动 RDB
4 优点
- 存储的是二进制文件,存储效率高
- 存储的是某个时间点的数据快照,适用于数据备份
- 恢复数据的速度比AOF快
- 缺点
- 无法做大实时持久化,只能对某个时间点做备份
- bgsave需要fork子进程
- redis多版本的rdb文件格式不统一
AOF(Append-only file)
- 以独立日志的方式记录每次写命令,重启时在重新执行命令以恢复数据
- 当客户端发送一条指令时,先将命令存放到缓冲区,再按照一定的策略将命令写入aof文件,然后再执行
- aof写数据的三种策略
- always(每次)
- everysec(每秒)
- no(系统控制)
AOF and RDB
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 小(二进制,压缩) | 大(指令) |
存储速度 | 慢(快照) | 快(部分指令) |
恢复速度 | 快 | 慢(指令重新执行) |
数据安全性 | 会丢失数据 | 依据策略决定 |
启动优先级 | 低 | 高 |
如何选择
- 对数据非常敏感,建议使用AOF
- 数据呈现阶段有效性,建议使用RDB,比如游戏回档
分布式锁
- setnx key value 获得锁,如果 key为空,则获取锁成功,否则失败
- 使用完之后用 del 操作释放锁
- 解决死锁问题:在加锁之后,为key设置一个时间限制。
- expire lock-key second
- pexpire lock-key millionseconds
主从复制
- 定义
- master 写数据/数据同步,slaves 读数据
- 作用(高可用)
- 读写分离
- 负载均衡
- 故障恢复
- 工作流程
- 建立连接(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是否在线
- master
- 建立连接(slave连master)
- 哨兵
- 作用
- 监控
- 故障转移
- 监控
- 获取其他哨兵的状态
- 获取master的状态
- 获取所有slave的状态。
- 故障转移
- 哨兵发现 master 挂了,会将这个信息发送到哨兵内网中,如果超过半数的哨兵都认为 master 挂了,就说明 master 真的挂了。
- 每个哨兵都竞选任命新 master 的任务
- 每个哨兵都发起投票,每个哨兵都只有一票。
- 获得超过半数票的哨兵来进行新 master 的任命。
- 将在线的、反应快的、与原 master 断开时间最短的 slave 任命为 master。
- 作用
- 集群
redis 单线程模型
- 指的是 server 和 client 之间交互过程是单线程的。
集群
常见问题
缓存预热
- 问题
- 服务器启动后快速宕机
- 问题排查
- 请求数量高
- 主从之间数据吞吐量较大,数据同步操作频率高
- 解决方案
- 统计访问频率较高的热点数据
- 利用LRu数据删除策略
- 根据统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据
- 利用分布式多服务器同时进行数据读取,提速数据加载过程
- 使用脚本程序固定出发数据预热过程
缓存雪崩
- 问题
- 系统平稳运行过程中,忽然数据库连接量激增
- 应用服务器无法及时处理请求
- 大量408,500错误页面
- 客户反复刷新页面获取数据
- 数据库崩溃
- 应用服务崩溃
- 重启应用服务器无效
- redis服务器崩溃
- redis集群崩溃
- 问题排查
- 在一个较短时间内,缓存中较多的key集中过期!
- 在周期内请求访问过期数据,redis未命中,redis向数据库获取数据
- 数据库同时接收到大量请求无法及时处理
- redis大量请求被积压,开始出现超时
- 数据库流量激增,数据库崩溃
- 解决方案
- 随机设置redis缓存过期时间,如果过期时间相同,容易造成雪崩。
- redis集群分布,把热点key放到不同的节点上
- 跑定时任务,定时刷缓存
- 更多页面静态化处理
- 构建多级缓存架构
- NGinx+redis+ehcache
- 灾难预警
- 限流、降级
缓存击穿
- 问题本质
- 热点数据缓存失效,然后同一时间大量请求直接涌入数据库,导致数据库崩溃
- 解决方案
- 缓存永不过期
- 分布式锁:请求数据库操作加锁,只有一个线程可以抢到锁,其他抢不到锁的线程先睡几毫秒,然后重新去redis读取即可。
缓存穿透
- 本质
- 恶意用户请求数据库中不存在的数据,从而导致该请求穿透redis缓存,直达数据库
- 解决方案
- 如果数据库中没有对应数据,在redis中设置一个空值对应这个请求
- 对请求规范性进行检查
- 对恶意ip进行拉黑
- 布隆过滤器:通过布隆过滤器快速判断数据是否存在,如果不存在及时返回
Author 段新朋
LastMod 2020-07-01