29.【学习心得】学习心得-写缓存实现思路

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【学习心得】学习心得-写缓存实现思路

文档参考:书名:《从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战》-王伟杰

image.png

前文如下:


23.【学习心得】学习心得-冷热分离概述

24.【学习心得】学习心得-如何分离冷热数据

25.【学习心得】学习心得-基于MySQL的分表分库

26.【学习心得】学习心得-读缓存

27.【学习心得】学习心得-如何更新redis缓存

28.【学习心得】学习心得-写缓存


2.实现思路

网络异常,图片无法展示
|


看起来很简单,但该方案在具体实施过程中要考虑6个问题。


1)写请求与批量落库这两个操作同步还是异步?

2)如何触发批量落库?

3)缓冲数据存储在哪里?

4)缓存层并发操作需要注意什么?

5)批量落库失败了怎么办?

6)Redis的高可用配置。


2.1 写请求与批量落库这两个操作同步还是异步在回答这个问题前,先来对比一下同步与异步。


在回答这个问题前,先来对比一下同步与异步。


对于同步,写请求提交数据后,当前写操作的线程会等到批量落库完成后才开始启动。这种设计的优点是用户预约成功后,可在“我的预约”页面立即看到预约数据;缺点是用户提交预约后,还需要等待一段时间才能返回结果,且这个时间不定,有可能需要等待一个完整的时间窗。


对于异步,写请求提交数据后,会直接提示用户提交成功。这种设计的优点是用户能快速知道提交结果;缺点是用户提交完成后,如果查看“我的预约”页面,可能会出现没有数据的情况。


那到底应该使用哪种设计模式呢?下面再介绍下这两种设计模式的复杂度。


同步的实现原理是写请求提交数据时,写请求的线程被堵塞或者等待,待批量落库完成后再发送信号给写请求的线程, 这个线程获得落库完成的信号后,返回预约成功提示给用户。


不过,这个过程会引出一系列的问题,比如:

1)用户到底需要等待多久? 用户不可能无限期等待下去,此时还需要设置一个时间窗,比如每隔100毫秒批量落库一次。

2)如果批量落库超时了怎么办? 写请求不可能无限期等待,此时就需要给写请求线程的堵塞设置一个超时时间。

3)如果批量落库失败了怎么办? 是否需要重试?多久重试一次?

4)如果写请求一直堵塞,直到重试成功再返回吗?那需要重试几次? 这些逻辑其实与Spring Cloud组件、Hystrix请求合并功能(Hystrix 2018年已经停止更新)等类似。


如果使用异步的话,上面的第2)点、第4)点基本不用考虑,从复杂度的角度来看,异步比同步简单很多,因此项目直接选用异步的方式,预约数据保存到缓存层即可返回结果。


关于异步的用户体验设计,共有两种设计方案可供业务方选择。


1)在“我的预约”页面给用户一个提示:您的预约订单可能会有一定延迟。

2)用户预约成功后,直接进入预约完成详情页,此页面会定时发送请求去查询后台批量落库的状态,如果落库成功,则弹出成功提示,并跳转至下一个页面。其实,第一种方案在实际应用中也经常遇到,不过项目中主要还是使用第二种方案。因为在第二种方案中,大部分情况下用户是感受不到延迟的,用户体验比较好,而如果选择第一种方案,用户还要去思考:这个延迟是什么意思?是不是失败了?这无形中就影响了用户体验。


2.2 如何触发批量落库


关于批量落库触发逻辑,目前共分为两种。


1)写请求满足特定次数后就落库一次,比如10个请求落库一次。按照次数批量落库的优点是访问数据库的次数变为1/N,从数据库压力上来说会小很多。不过它也存在不足:如果访问数据库的次数未凑齐N次,用户的预约就一直无法落库。

2)每隔一个时间窗口落库一次,比如每隔一秒落库一次。按照时间窗口落库的优点是能保证用户等待的时间不会太久,其缺点是如果某个瞬间流量太大,在那个时间窗口落库的数据就会很多,多到在一次数据库访问中无法完成所有数据的插入操作(比如一秒内堆积了5000条数据),它们只能通过分批次来实现插入,这不就变回第一种逻辑了吗?


那到底哪种触发方式好呢?当时项目采用的方案是同时使用这两种方式。


具体实现逻辑如下。

1)每收集一次写请求,就插入预约数据到缓存中,再判断缓存中预约的总数是否达到一定数量,达到后直接触发批量落库。

2)开一个定时器,每隔一秒触发一次批量落库。


2.3 缓存数据存储在哪里


缓存数据不仅可以存放在本地内存中,也可以存放在分布式缓存中(比如Redis),其中最简单的方式是存放在本地内存中。


但是,Hystrix的请求合并也是存放在本地内存中,为什么不直接使用Hystrix?这是因为写缓存与Hystrix的请求合并有些不一样,请求合并更多考虑的是读请求的情况,不用担心数据丢失,而写请求需要考虑容灾问题:如果服务器宕机,内存数据就会丢失,用户的预约数据也就没有了。其实也可以考虑使用MQ来当缓存层,MQ的一个主要用途就是削峰,很适合这种场景。


不过这个项目选择了Redis,接下来需要考虑批量落库的设计了。批量落库主要是把Redis中的预约数据迁移到数据库中。而当新的数据一直增加时,批量落库可能会出现多个线程同时处理的情况,此时就要考虑并发性了。


2.4 批量落库失败了怎么办

在考虑落库失败这个问题之前,先来看看批量落库的实现逻辑。


1)当前线程从缓存中获取所有数据。因为每10条执行一次落库操作,不需要担心缓存中的数据量过多,所以也不用考虑将获得的数据分批操作了。

2)当前线程批量保存数据到数据库。

3)当前线程从缓存中删除对应数据(注意:不能直接清空缓存的数据,因为新的预约数据可能插入到缓存中了)。


以上各个步骤失败时的应对措施见表5-1。


网络异常,图片无法展示
|



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
SQL 缓存 关系型数据库
MySQL技能完整学习列表6、查询优化——3、查询缓存——4、SQL优化技巧
MySQL技能完整学习列表6、查询优化——3、查询缓存——4、SQL优化技巧
113 0
|
22天前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
22 3
|
5月前
|
存储 缓存 算法
数据结构和算法学习记录——总结顺序表和链表(双向带头循环链表)的优缺点、CPU高速缓存命中率
数据结构和算法学习记录——总结顺序表和链表(双向带头循环链表)的优缺点、CPU高速缓存命中率
46 0
|
3月前
|
缓存 JavaScript
Vue学习之--------编程式路由导航、缓存路由组件、新的钩子函数(4)(2022/9/5)
这篇文章介绍了Vue中编程式路由导航的方法,包括使用`$router.push`、`$router.replace`、`$router.forward`、`$router.back`和`$router.go`进行路由跳转和历史记录操作,以及如何利用`<keep-alive>`组件缓存路由组件,和Vue Router新增的两个生命周期钩子`activated`和`deactivated`的用法及其在项目中的应用和测试结果。
Vue学习之--------编程式路由导航、缓存路由组件、新的钩子函数(4)(2022/9/5)
|
5月前
|
缓存 负载均衡 NoSQL
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
77 1
|
5月前
|
存储 缓存 NoSQL
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
79 1
|
5月前
|
缓存 NoSQL Java
Redis系列学习文章分享---第四篇(Redis快速入门之Java客户端--商户查询缓存+更新+双写一致+穿透+雪崩+击穿+工具封装)
Redis系列学习文章分享---第四篇(Redis快速入门之Java客户端--商户查询缓存+更新+双写一致+穿透+雪崩+击穿+工具封装)
67 0
|
6月前
|
存储 缓存 前端开发
学习和理解前端缓存
前端缓存通过存储重复资源提升网页加载速度,减少服务器压力,优化用户体验。它涉及静态资源(如图片、CSS、JS)的HTTP缓存,动态数据(使用`localStorage`、`IndexedDB`)缓存,用户登录态、页面状态管理,以及预加载缓存。实现方式包括HTTP缓存(强缓存、协商缓存),浏览器存储(localStorage、sessionStorage、IndexedDB),Service Worker和Cache API。在项目中,应根据资源特性和需求选择合适的缓存策略。
|
6月前
|
缓存 NoSQL Java
SpringCache通用缓存学习
SpringCache通用缓存学习
119 1
|
6月前
|
缓存 NoSQL Java
SpringCache通用缓存学习
SpringCache通用缓存学习