RedisRoute
介绍
Redis学习路线 (37小时)
学习路线
- https://coding.imooc.com/class/151.html (作者:阿里redis开发规范)
- https://coding.imooc.com/class/467.html (从实战角度使用redis)
Redis特点
- 内存数据块,速度快,支持数据的持久化
- 支持数据备份(主从模式)以及集群分片存储,哨兵监控机制
- 支持事务
- Redis 是纯内存数据库
- Redis使用的非阻塞IO,IO多路复用,减少了线程切换时对上下文的切换和竞争
- Redis采用单线程模型,保证了操作原子性
Redis 基本数据类型
- 字符串(SDS)。采用预分配冗余空间的方式减少内存的频繁分配。最大为512M。应用场景,缓存,分布式锁。
- 散列。哈希表。用来存储对象信息。key->field:value。filed不能相同。有序。
- 列表。主要用于热搜榜单,Feed流。支持重复元素。无序。
- 集合。主要具备并集,交集,差集的功能。不支持插入重复元素。
- 有序集合。根据权重进行排序,比如游戏积分榜,设置优先级任务列表,学生成绩等。
- 位图。内部是使用字符串实现的。操作bit
- HyperLogLog。存在误差,无法取出存储数据。
- GEO 地理信息定位
Redis主要应用场景
- 缓存系统
- 计数器
- 排行榜
- 实时系统
- 消息队列系统
- 社交系统
Redis其他功能
- 慢查询
- 先进先出队列
- 固定长度
- 保存在内存中
- 配置
- slowlog-max-len。 默认值 128
- slowlog-log-slower-than .默认值 10000
- 定期持久化慢查询
- pipeline流水线
- pipline每次条数要控制
- 非原子性 操作
- 发布订阅
- 无法消息堆积,获取历史消息
Redis持久化
- 持久化概念:将内存数据保存到磁盘
- 持久化方式
- RDB。快照。
- 触发方式
- save 同步 阻塞。替换旧的RDB文件。
- bgsave 异步。fork进程
- 自动
- 主从复制
- debug reload
- shutdown
- 问题点
- 耗时,耗性能
- 不可控,容易丢失数据
- 触发方式
- AOF。写日志。
- 触发方式
- always
- everysec
- no
- AOF重写
- 减少磁盘占用
- 加快恢复速度
- 触发方式
- RDB与AOF的比较
- RDB。快照。
Redis主从复制
- 作用
- 数据副本
- 扩展读性能
- 实现方式
- slaveof命令
- 配置方式
Redis哨兵模式
- 三个定时任务
- 每10每个sentinel对master 和slave进行info
- 发现slave节点
- 确认主从关系
- 每2秒每个sentinel通过对master节点的channel交换信息
- 通过_sentinel_:hello 频道交互
- 交互对节点的看法和自身信息
- 每1秒每个sentinel 对其他sentinel 和 redis 执行ping.
- 每10每个sentinel对master 和slave进行info
- 主观下线和客观下线
- 主观下线:每个sentinel节点对redis节点失败的偏见
- 客观下线:所有sentinel节点对redis节点失败达成共识
- 领导者选举
- 选举通过sentinel is-master-down-by-addr命令都希望成为领导者
- 故障转移
- 从slave节点选出一个合适的节点作为新的master节点
- 对上面的slave节点执行 slave of no one 命令 让其成为master节点
- 向剩余的slave节点发送命令,让它们成为新master节点的slave节点,复制规则和parallel-sync参数有关
- 更新对原来master节点配置为slave,并保持对其的关注,当其恢复后命令它去复制新的master节点
- 注意点
- 客户端初始化连接的Sentinel节点集合,不再是具体的redis节点,但sentinel只是配置中心不是代理。
Redis 避坑指南
- 怎么正确的选择数据类型。
- 所有的数据都可以使用string去表示
- 都使用string去存储数据存在以下弊端
- 浪费存储空间,key也需要存储空间的
- 管理维护麻烦,Redis中存在大量的KV对象
- key冲突的几率变高(不同业务系统共用一个Redis服务)
- key的命令策略(建议)(业务线名称+ 工程名+ 模块名+有意义的键名)
- 同一个Redis服务被公司中的各个业务线共用
- 常用的数据类型及其适用场景
- string : 应用最为广泛的数据类型:例如:计数器,session等键值
- hash : 存储结构化(对象的)数据,KV共同构成一个对象的信息
- list : 队列、栈、有界队列
- set : 去重、无序的数据结合。在类似于社交的业务功能上有引用。比如共同关注、共同喜好、数据去重
- sortedset:带有权重的集合,在类似于排行榜业务上有广泛应用,且可以实现范围查找
- 使用了事务功能,为何没回滚
- Redis事务:四个指令。MULTI :开启。EXEC :启动。DISCARD: 废弃。WATCH :观察
- 事务可以一次执行多个命令,并且可以进行单独的隔离操作,原子操作。
- Redis事务的两类错误
- 事务在执行EXEC之前,入队的命令出错。原因: 命令语法错误。服务器内存不足。
- Redis拒绝执行,自动放弃这个事务。
- 命令在EXEC调用之后失败。原因:命令语法错误
- 事务队列中的其他命令会继续执行,事务不会停止。
- 事务在执行EXEC之前,入队的命令出错。原因: 命令语法错误。服务器内存不足。
- Redis不支持回滚
- Redis命令只会因为错误的语法而失败,或者命令用了错误类型的键上面
- 因为不需要对回滚进行支持,所以Redis内容可以保持简单且快速
- Redis 性能上不去,可能有big key
- Big key 以及造成的伤害(数据量大的key,比如字符串value值非常大(>10KB),哈希、列表、集合、有序集合元素多(个数>5000))
- 内存不均
- 超时阻塞
- 网络流量拥塞
- 过期删除
- 迁移困难
- Big key 怎么产生的
- 社交类场景:典型的是粉丝列表,对于大V来说,需要特殊统计,否则就成为了big key
- 汇总统计类场景: 每天都会产生报表数据,报表数据汇总到一个key里,日积月累就成为了big key
- 日常缓存类场景:对DB中的数据进行缓存,大表很可能会产生big key
- 发现big key
- redis-cli 提供 --bigkey来查找bigkey ;给出了每种数据结构的top1 bigkey。同时给出每种数据类型的键值个数以及平均大小
- Redis 4.0开始提供memory usage 们能够令可以计算每个键值的字节数
- 删除big key ,不要贸然行动
- string : 可以直接使用del 命令,不会产生阻塞
- dict :使用hscan 命令每次获取部分field-value,再使用hdel删除每个field
- set :使用scan命令,每次获取部分元素哦,再使用srem命令删除每个元素
- sortedset : 使用zscan命令,每次获取部分元素,再使用zremrangebyrank命令删除元素
- Big key 以及造成的伤害(数据量大的key,比如字符串value值非常大(>10KB),哈希、列表、集合、有序集合元素多(个数>5000))
- Redis 内存耗尽
- 原因
- 业务不断发展,缓存数据不断增多,需要消耗Redisn内存
- 无效的(过期的)数据没有及时清理,需要消耗Redisn内存
- 没有对冷数据进行降级,需要消耗Redis内存
- 使用EXPIRE命令将KEY标记为Volatile的
- 使用EXPIRE命令给key设置生存时间,当key过期时,它会被自动删除
- 如果一个命令只是修改一个带生存时间的key的值而不是用一个新的key来代替它的,那么生存时间不会被改变
- 如果PERSIST 命令可以在不删除KEY情况下,移除KEY的生存时间,让key重新成为一个持久的key
- 可以对一个已经带有生存时间的key执行EXPIRE命令,所指定的生存时间会带旧的生存时间
- Redis删除策略
- 定时删除: 对于每一个设置过期时间的key,都会创建一个定时器,一旦到达过期时间就立即删除
- 惰性删除:当访问一个key时,才判断该key是否过期,过期则删除
- 定期删除:每隔一段时间,扫描Redis中过期key字典,并清除部分过期的key
- 原因
- 有了过期机制以后内存还不够用怎么办
- Redis内存的淘汰策略
- Redis内存的淘汰策略,是指内存达到maxmeory极限时,使用某种算法来决定清理掉哪些数据,以保证新数据的存入
- Redis的内存淘汰机制(volatile策略只会针对带过期时间的key进行淘汰,allkeys策略会对所有的key进行淘汰)
- noeviciton
- allkeys-lru
- allkeys-random
- volatile-lru
- volatile-random
- volatile-ttl
- 配置Redis:redis.confg。 maxmeory:1024000
- 策略选择
- 如果只是拿Redis做缓存,那应用使用allkeys下的淘汰策略
- 如果还同时使用Redis的持久化功能,那就是用volatile下的淘汰策略
- Redis内存的淘汰策略
- 如何解决频繁的命令往返造成的性能瓶颈
- Redis 客户端与服务端的交互模型
- Redis 是基于一个Request ,一个Response的同步请求服务
- 数据包往返于两端的时间被称之谓RTT
- 解决方案 :使用pipline
- Redis pipeline 与原生批量命令对比
- 原生批量命令是原子性,pipline是非原子性的
- 原生批量命令一次只能一种命令,pipline支持批量执行不同命令
- 原生批量命令是服务端实现,而pipline需要服务端与客户端共同完成。
- 使用pipline的注意点:
- 使用pipline组装的命令个数不能太多,不然数据量过大
- pipline会按照指令缓存的顺序执行,但可能会穿插其他客户端发送来的命令,即时序性不一定能保证
- pipline执行中间某一个指令出现异常,将会继续执行后续的指令,既不能保证原子性。
- Redis 客户端与服务端的交互模型
- Redis 持久化
- 为什么需要持久化
- 高并发场景只是用Redis作为存储设备,需要保证数据不会丢失
- 通常应用场景虽然会同时把数据写入mysql,但是直接从mysql中恢复数据性能低下,且缺失选择性
- RDB的持久化配置
- save : 时间策略
- dbfilename : 文件名称
- dir : 文件保存路径
- stop-writes-on-bgsvae-error:如果持久化出国,主进程是否停止写入
- rdbcompression:是否压缩
- rdbchecksum:导入时是否检查
- 触发原理
- 手动触发
- save会阻塞当前Redis服务器。直到持久化完成,线上应该禁止
- bgsave:该触发方式会fork一个子进程,由紫禁城负责持久化,因此只会阻塞发生在fork子进程的时候
- 定时触发
- 根据我们的save m n 配置规定自动触发
- 从节点全量复制时,主节点发送rdb文件从节点完成复制操作,主节点会触发bgsave
- 执行shutdown时,如果没有开启aof,也会触发
- 手动触发
- AOF的持久化配置
- appendonly: 是否开启aof
- appendfilename : 文件名称
- appendfsync:同步方式
- no-appendfsync-on-rewrite:aof重写期间是否同步
- aof-load-truncated:加载aof时,如果有错如何处理
- AOF 重写
- 当AOF文件的大小超过所设定的阀值时,Redis就会自动启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集
- 触发机制:Redis会记录上次重写时的AOF的大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64MB时触发
- 重写原理:将整个内存中的数据库内容用命令的方式重写一个新的AOF文件,这点和快照有点类似
- 恢复方式
- 存在AOF 加载AOF
- 不存在AOF 加载RDB
- 都不存在,启动失败
- 为什么需要持久化
- 缓存穿透的隐患
- 缓存穿透
- 概念:查询一个一定不存在的数据,如果规则是从数据库中查不到数据不写入缓存,这将导致这个不存在的数据每次请求都要到数据库中查询
- 缓存穿透的解决方案
- 缓存预热:系统上线后,提前将相关的缓存数据直接加载到缓存系统
- 如果查询数据库也为空,直接设置一个默认值存放到缓存,第二次就到缓冲中就有值了,而不会继续访问数据库
- 在数据处理层,进行数据校验,判断数据类型和数据范围
- 使用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个不存在的数据会被这个bitmap拦截掉
- 缓存预热:系统上线后,提前将相关的缓存数据直接加载到缓存系统
- 缓存穿透
- 缓存雪崩
- 缓存雪崩带来的问题
- 瞬时请求都会被转发到mysql上,给后端数据库造成瞬时的负载升高的压力
- 如果请求量增多,并发度高,可能会使mysql压力过大而导致系统宕机
- 解决方案
- 过期时间错开(可以在一个基础时间上增减一个随机值),避免热点数据集中失效
- 本地缓存+ hystrix限流&降级,避免数据库访问压力过大
- 缓存雪崩带来的问题