高性能分布式缓存Redis
- `第一篇章`
- 1.1缓存发展史&缓存分类
- 1.1.1 大型网站中缓存的使用
- 带来的问题
- 1.1.2 常见缓存的分类及对比
- 与memcache对比
- 1.2 数据类型选择&应用场景
- 1.2.1 string
- 1.2.2 hash
- 1.2.3 链表
- 1.2.4 set
- 1.2.5 sortedset有序集合类型
- 1.2.6 总结
- 1.3 Redis高级应用&拓展功能
- 1.3.1 发布订阅
- redis提供发布订阅功能,可用于消息的传输
- 指令详情
- 使用场景
- 1.3.2 事务
- redis事务支持回滚吗
- redis 事务
- 1.3.2 lua脚本
- 1.3.3 慢查询日志
- `第二篇章`
- 2.1 持久化原理
- 2.1.1 原理
- 2.1.2 持久化流程
- 2.1.3 RDB
- 概念
- 特点
- 触发方式:指令手动触发和redis.conf自动触发
- RDB优势
- RDB劣势
- 2.1.4 AOF
- 概念
- 特点
- AOF持久化实现
- AOF重写原理
- 持久化优先级
- 2.1.5 降低fork阻塞
- 2.1.6 实践中的其它策略
- 2.2 过期删除策略
- 2.2.1 问题分析
- 1. 如何设置过期时间
- 2. 如何盘判定key已过期
- 3. 过期删除策略有哪些
- 4. Redis过期删除策略是什么
- 2.3 内存淘汰策略
- 2.3.1 概念
- 2.3.2 如何设置Redis的最大运行内存
- 2.3.3 Redis的八种内存淘汰策略
- 2.3.4 LRU算法和LFU算法有什么区别
- LRU传统算法
- Redis中的LRU算法
- LFU算法
- redis中LFU算法
- 2.4 Redis高可用
- 2.4.1 主从复制的出现
- 2.4.2 主从复制结构
- 2.4.3 主从复制实现
- 2.4.4 sentinel 哨兵模式
- 哨兵的出现
- 客观下线
- 哨兵挂了怎么办
- 2.4.5 主从+哨兵存在的问题
- `第三篇章`
- 3.1 分布式锁
- 何为分布式锁
- 分布锁的特点
- 如何设计一把良好的锁
- 3.2 布隆过滤器(BloomFilter)
- 3.3 Redis Cluster
- `第四篇章-FAQ`
- 4.1 如何保持缓存和数据库的一致性
- ==总结==
- 1)为什么使用redis来做缓存
- 2)缓存策略带来的问题
- 3)导致数据不一致原因
- 4)并发引起的一致性问题
- 5)删除缓存可以保证一致性吗
- 6)如何保证两步都执行成功
- 7)主从库延迟和延迟双删策略
- 8)保证更新数据库和删除缓存都能成功
- 4.2 redis是单线程架构还是多线程架构
- 4.3 单线程的redis为什么这么快
- 4.4 Redis6.x之后为何引入了多线程?
- 4.5 缓存穿透\缓存击穿\缓存雪崩
- 4.6 为什么用 Redis 作为 MySQL 的缓存?
- 4.7 Redis 如何实现数据不丢失?
第一篇章
1.1缓存发展史&缓存分类
1.1.1 大型网站中缓存的使用
分析:直接从数据库中的数据,是存储在磁盘中的,需要多次的IO,而且请求数据库是基于TCP连接,单机的mysql qps 1W+,而redis的qps达到10w+;所以可以在Tomcat和mysql中加入屏障,将热点数据放入redis,非热点数据放入数据库中,流程如下
带来的问题
- 读写缓存策略
- 读写穿透
- 异步缓存写入
- 数据库和缓存如何保证数据一致性
- 写策略
- 先更新缓存,再删除缓存
- 先删除缓存,再更新数据库
- 写策略
1.1.2 常见缓存的分类及对比
与memcache对比
共同点:
- 都是基于内存的数据库,一般都用来当做缓存使用。
- 都有过期策略。
- 两者的性能都非常高。
区别:
- Redis 支持的数据类型更丰富(String、Hash、List、Set、zset),而 Memcached 只支持最简单的 key-value 数据类型;
- Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了
- Redis 原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持
所以,很少使用memcached了
1.2 数据类型选择&应用场景
- 常见的五种数字类型:string、hash、list、set、zset
1.2.1 string
- redis并不是简单的使用c的string,而是构建了简单动态字符串,不光可以保存文本数据还可以保存二进制,并且获取字符串的长度为0
- 常用命令:set,get,strlen,exists
- 应用场景:缓存对象、常规计数、分布式锁、共享session
- 对象缓存:直接缓存整个对象的json
- 分布式锁:setnx product:10001 true;setnx product:10001 false
1.2.2 hash
- key-value,适合存储
- 适合做对象缓存
电商购物车
用户id为key;商品id为field;商品数量为value;Hest cart:1 1001 1 //向id为1的商品1001添加1间商品
- 适合做对象缓存
1.2.3 链表
- c的链表查询比较难,所以redis使用了双向链表,支持反向查找和遍历,更方便操作,不过带了了额外的内存开销
- 内部实现:quicklist
- 常用命令:rpush、lpop、lpush
- 应用场景:发布与订阅或者说消息队列,慢查询
- 常用数据结构
- Stack(栈)=LPUSH(左边放)+LPOP(左边取)–> FILO
- Queue(队列)=LPUSH(左边放)+RPOP右边取)
- BLocking queue(阻塞队列)=LPUSH(左边放)+ BRPOP(右边阻塞取:没有数据就阻塞!)
- 实现微波朋友圈等的关注列表显示
- 实现后发的消息在列表中最上方展示
- 小明关注了北京本地宝,京城美味君等公众号,这些订阅号发布消息时,通过推或拉的方式把消息LPUSH放入redis中属于小明的list中。其中key为msg:{小明D}。当小明要获取大V们发的消息时,使用LRANGE 命令从队列中获取指定个数的订阅号信息
Lpush msg:1 1001 //1001是北京本地宝,mag:1是小明的id
Lpush msg:1 1002
显示的结果是1002先显示
1.2.4 set
- 是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,可以交集,并集,差集等等,所以 set 类型除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集
- 底层实现:哈希表或整数集合
- 常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等
- 应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
1.2.5 sortedset有序集合类型
- 介绍:zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合zSet 来说,每个存储元素相当于有两值组成的,一个是有序集合的元素值,一个是排序值。
- 内部实现:压缩列表或跳表
- 常用命会: zadd,zcard,zscore,zrange,zrevrange,zrem等
- 应用场景: 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
1.2.6 总结
1.3 Redis高级应用&拓展功能
1.3.1 发布订阅
redis提供发布订阅功能,可用于消息的传输
redis的发布订阅包含三个部分,publisher(redis客户端),subscriber(redis客户端)、channel(服务器)
指令详情
- SUBSCRIBE/PSUBSCRIBE:订阅,精确、或者按匹配符UNSUBSCRIBE/PUNSUBSCRIBE:退订,精确、或者按匹配
- PUBLISH:发送;PUBSUB:查看消息列表
使用场景
- 在Redis哨兵模式中,哨兵通过发布与订阅的方式与Redis主服务器和Redis从服务器进行通信Redisson是一个分布式锁框架,在Redisso
- 分布式锁释放的时候,是使用发布与订阅的方式通知的注:重业务的消息,推荐用消息队列
1.3.2 事务
redis事务支持回滚吗
所谓事务,是指作为单个逻辑工作单元执行的一系列操作
mysql在执行事务时,会提供回滚机制,当事务执行发生错误时,事务中的所有操作都会撤销,已修改的数据也会被恢复到事务执行前的状态,但是redis并没有提供回滚机制,redis事务不一定能保证原子性
redis 事务
redis事务的本质是一组命令的集合:单词执行多个命令,一次性、排他性、顺序性
- redis事务是通过multi、exec、discrd、watch这四个命令来完成的
- redis的单个命令都是原子性的,所以这里需要确保事务的对象是命令集合
- redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- redis不能保障失败回滚
原理刨析:
在exec执行事务的一瞬间,判断监控的key是否变动
变动则取消事务队列,直接不执行
无变动则执行,提交事务
1.3.2 lua脚本
redis+lua脚本保持原子性
- lua是一种轻量小巧的脚本语言用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
- Lua应用场景
- 游戏开发、独立应用脚本、web应用脚本、查询库存扣减库存
- Nginx+lua开发高性能web应用,限流、防止Sql注入
时间复杂度:取决于执行的脚本。
- 使用Lua脚本的好处:
- 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无换事务。
- 复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
1.3.3 慢查询日志
日常使用redis为什么要用慢查询日志
客户端请求的生命周期的完整生命周期,4个阶段
慢查询只统计步骤3的时间
- 在生产环境中,慢查询功能可以有效地帮助我们找到Redis可能存在的瓶颈,但在实际使用过程中要注意以下几点:
- slowog-max-en:线上建议调大慢查询列表,记录慢査询时Redis会对长命令做阶段操作,并不会占用大量内存,增大慢查询列表可以减缓慢查询被剔除的可能,例如线上可设置为1000以上.
- 2slowlog-log-slower-than:默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值,慢查询只记录命令的执行时间,
并不包括命令排队和网络传输时间,因此客户端执行命令的时间会大于命令的实际执行时间,因为命2令执行排队机制,慢查询会导致其他命令级联阻塞,因此客户端出现请求超时时,需要检査该时间点是否有对应的慢查询,从而分析是否为慢查询导致的命令级联阻塞.
第二篇章
2.1 持久化原理
2.1.1 原理
redis是内存数据,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将redis中方的数据以某种形式从内存