Redis高级-多级缓存同步
5.缓存同步大多数情况下,浏览器查询到的都是缓存数据,如果缓存数据与数据库数据存在较大差异,可能会产生比较严重的后果。 所以我们必须保证数据库数据、缓存数据的一致性,这就是缓存与数据库的同步。 5.1.数据同步策略缓存数据同步的常见方式有三种: 设置有效期:给缓存设置有效期,到期后自动删除。再次查询时更新 优势:简单、方便 缺点:时效性差,缓存过期之前可能不一致 场景:更新频率较低,时效性要求低的业务 同步双写:在修改数据库的同时,直接修改缓存 优势:时效性强,缓存与数据库强一致 缺点:有代码侵入,耦合度高; 场景:对一致性、时效性要求较高的缓存数据 异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据 优势:低耦合,可以同时通知多个缓存服务 缺点:时效性一般,可能存在中间不一致状态 场景:时效性要求一般,有多个服务需要同步 而异步实现又可以基于MQ或者Canal来实现: 1)基于MQ的异步通知: 解读: 商品服务完成对数据的修改后,只需要发送一条消息到MQ中。 缓存服务监听MQ消息,然后完成对缓存的更新 依然有少量的代码侵入。 2)基于C...
Redis高级-多级缓存
3.Lua语法入门Nginx编程需要用到Lua语言,因此我们必须先入门Lua的基本语法。 3.1.初识LuaLua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。官网:https://www.lua.org/ Lua经常嵌入到C语言开发的程序中,例如游戏开发、游戏插件等。 Nginx本身也是C语言开发,因此也允许基于Lua做拓展。 3.1.HelloWorldCentOS7默认已经安装了Lua语言环境,所以可以直接运行Lua代码。 1)在Linux虚拟机的任意目录下,新建一个hello.lua文件 2)添加下面的内容 1print("Hello World!") 3)运行 3.2.变量和循环学习任何语言必然离不开变量,而变量的声明必须先知道数据的类型。 3.2.1.Lua的数据类型Lua中支持的常见数据类型包括: 另外,Lua提供了type()函数来判断一个变量的数据类型: 3.2.2.声明变量Lua声明变量的时候无需指定数据类型,而是用local...
Redis高级-JVM进程缓存
1.什么是多级缓存传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈 •Redis缓存失效时,会对数据库产生冲击 多级缓存就是充分利用请求处理的每个环节,分别添加缓存,减轻Tomcat压力,提升服务性能: 浏览器访问静态资源时,优先读取浏览器本地缓存 访问非静态资源(ajax查询数据)时,访问服务端 请求到达Nginx后,优先读取Nginx本地缓存 如果Nginx本地缓存未命中,则去直接查询Redis(不经过Tomcat) 如果Redis查询未命中,则查询Tomcat 请求进入Tomcat后,优先查询JVM进程缓存 如果JVM进程缓存未命中,则查询数据库 在多级缓存架构中,Nginx内部需要编写本地缓存查询、Redis查询、Tomcat查询的业务逻辑,因此这样的nginx服务不再是一个反向代理服务器,而是一个编写业务的Web服务器了。 因此这样的业务Nginx服务也需要搭建集群来提高并发,再有专门的nginx服务来做反向代理,如图: 另外,...
Redis高级-分片集群
4.Redis分片集群4.1.搭建分片集群主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决: 海量数据存储问题 高并发写的问题 使用分片集群可以解决上述问题,如图: 分片集群特征: 集群中有多个master,每个master保存不同数据 每个master都可以有多个slave节点 master之间通过ping监测彼此健康状态 客户端请求可以访问集群任意节点,最终都会被转发到正确节点 具体搭建流程参考课前资料《Redis集群.md》: 4.2.散列插槽4.2.1.插槽原理Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到: 数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况: key中包含”{}”,且“{}”中至少包含1个字符,“{}”中的部分是有效部分 key中不包含“{}”,整个key都是有效部分 例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利...
Redis高级-哨兵
3.Redis哨兵Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。 3.1.哨兵原理3.1.1.集群结构和作用哨兵的结构如图: 哨兵的作用如下: 监控:Sentinel 会不断检查您的master和slave是否按预期工作 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端 3.1.2.集群监控原理Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令: •主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。 •客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。 3.1.3.集群故障恢复原理一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据...
Redis高级-主从同步
2.Redis主从2.1.搭建主从架构单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 具体搭建流程参考课前资料《Redis集群.md》: 2.2.主从数据同步原理2.2.1.全量同步主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点,流程: 这里有一个问题,master如何得知slave是第一次来连接呢?? 有几个概念,可以作为判断依据: Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。 因此slave做数据同步,必须向master声明自己的replication id 和offset,master才...
Redis高级-数据持久化
分布式缓存– 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题: 0.学习目标1.Redis持久化Redis有两种持久化方案: RDB持久化 AOF持久化 1.1.RDB持久化RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。快照文件称为RDB文件,默认是保存在当前运行目录。 1.1.1.执行时机RDB持久化在四种情况下会执行: 执行save命令 执行bgsave命令 Redis停机时 触发RDB条件时 1)save命令 执行下面的命令,可以立即执行一次RDB: save命令会导致主进程执行RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。 2)bgsave命令 下面的命令可以异步执行RDB: 这个命令执行后会开启独立进程完成RDB,主进程可以持续处理用户请求,不受影响。 3)停机时 Redis停机时会执行一次save命令,实现RDB持久化。 4)...
延迟消息、死信队列
1.延迟消息在电商的支付业务中,对于一些库存有限的商品,为了更好的用户体验,通常都会在用户下单时立刻扣减商品库存。例如电影院购票、高铁购票,下单后就会锁定座位资源,其他人无法重复购买。 但是这样就存在一个问题,假如用户下单后一直不付款,就会一直占有库存资源,导致其他客户无法正常交易,最终导致商户利益受损! 因此,电商中通常的做法就是:对于超过一定时间未支付的订单,应该立刻取消订单并释放占用的库存。 例如,订单支付超时时间为30分钟,则我们应该在用户下单后的第30分钟检查订单支付状态,如果发现未支付,应该立刻取消订单,释放库存。 但问题来了:如何才能准确的实现在下单后第30分钟去检查支付状态呢? 像这种在一段时间以后才执行的任务,我们称之为延迟任务,而要实现延迟任务,最简单的方案就是利用MQ的延迟消息了。 在RabbitMQ中实现延迟消息也有两种方案: 死信交换机+TTL 延迟消息插件 这一章我们就一起研究下这两种方案的实现方式,以及优缺点。 1.1.死信交换机和延迟消息首先我们来学习一下基于死信交换机的延迟消息方案。 1.1.1.死信交换机什么是死信? 当一个队列中的消息...
消息可靠性
在昨天的练习作业中,我们改造了余额支付功能,在支付成功后利用RabbitMQ通知交易服务,更新业务订单状态为已支付。但是大家思考一下,如果这里MQ通知失败,支付服务中支付流水显示支付成功,而交易服务中的订单状态却显示未支付,数据出现了不一致。此时前端发送请求查询支付状态时,肯定是查询交易服务状态,会发现业务订单未支付,而用户自己知道已经支付成功,这就导致用户体验不一致。 因此,这里我们必须尽可能确保MQ消息的可靠性,即:消息应该至少被消费者处理1次那么问题来了: 我们该如何确保MQ消息的可靠性? 如果真的发送失败,有没有其它的兜底方案? 这些问题,在今天的学习中都会找到答案。 1.发送者的可靠性首先,我们一起分析一下消息丢失的可能性有哪些。消息从发送者发送消息,到消费者处理消息,需要经过的流程是这样的:消息从生产者到消费者的每一步都可能导致消息丢失: 发送消息时丢失: 生产者发送消息时连接MQ失败 生产者发送消息到达MQ后未找到Exchange 生产者发送消息到达MQ的Exchange后,未找到合适的Queue 消息到达MQ后,处理消息的进程发生异常 MQ导致消息丢...
交换机
1.交换机类型在之前的两个测试案例中,都没有交换机,生产者直接发送消息到队列。而一旦引入交换机,消息发送的模式会有很大变化: 可以看到,在订阅模型中,多了一个exchange角色,而且过程略有变化: Publisher:生产者,不再发送消息到队列中,而是发给交换机 Exchange:交换机,一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。 Queue:消息队列也与以前一样,接收消息、缓存消息。不过队列一定要与交换机绑定。 Consumer:消费者,与以前一样,订阅队列,没有变化 Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失! 交换机的类型有四种: Fanout:广播,将消息交给所有绑定到交换机的队列。我们最早在控制台使用的正是Fanout交换机 Direct:订阅,基于RoutingKey(路由key)发送给订阅了消息的队列 Topic:通配符订阅,与Dir...