在分布式系统中,特别是最近很火的微服务架构下,有没有或者能不能总结出一个业务静态数据的通用缓存处理机制或方案,这篇文章将结合一些实际的研发经验,尝试理清其中存在的关键问题以及探寻通用的解决之道。
这里静态数据是指不经常发生变化或者变化频率比较低的数据,比如车型库、用户基本信息、车辆基本信息等,车型库这种可能每个月会更新一次,用户和车辆基本信息的变化来源于用户注册、修改,这个操作的频率相对也是比较低的。
另外这类数据的另一个特点是要求准确率和实时性都比较高,不能出现丢失、错误,以及过长时间的陈旧读。
具体是不是应该归类为静态数据要看具体的业务,以及对变化频率高低的划分标准。在这里的业务定义中,上边这几类数据都归为静态数据。
在面向用户或车联网的业务场景中,车型信息、用户基本信息和车辆基本信息有着广泛而高频的业务需求,很多数据都需要对其进行关联处理。在这里缓存的目的就是为了提高数据查询效率。静态数据通常都保存在关系型数据库中,这类数据库的IO效率普遍不高,应对高并发的查询往往捉襟见肘。使用缓存可以极大的提升读操作的吞吐量,特别是KV类的缓存,没有复杂的关系操作,时间复杂度一般都在O(1)。注意这里说的缓存指内存缓存。
当然除了使用缓存,还可以通过其它手段来提高IO吞吐量,比如读写分离,分库分表,但是这类面向关系型数据库的方案更倾向于同时提高读写效率,对于单纯提升读吞吐量的需求,这类方案不够彻底,不能在有限的资源情况下发挥更好的作用。
下面将直接给出一个我认为的通用处理机制,然后会对其进行分析。
对于某个具体的业务,其涉及到六个核心程序:
以及两个外部定义:
下面以问答的形式来说明为什么是这样一种机制。
既然是微服务架构,当然离不开服务了,因为这里探讨的是业务静态数据,所以是业务服务。不过为了更好的理解,这里还是简单说下服务出现的原因。
当今业务往往需要在多个终端进行使用,比如PC、手机、平板等,既有网页的形式,又有APP的形式,另外某个数据可能在多种不同的业务被需要,如果将数据操作分布在多个程序中很可能产生数据不一致的情况,另外代码不可避免的冗余,读写性能更很难控制,变更也基本上是不敢变的。通过一个业务服务可以将对业务数据的操作有序的管理起来,并通过接口的形式对外提供操作能力,代码不用冗余了,性能也好优化了,数据不一致也得到了一定的控制,编写上层应用的人也舒服了。
很多开发语言都提供了进程内缓存的支持,即使没有提供直接操作缓存的包或库,也可以通过静态变量的方式来实现。对数据的查询请求直接在进程内存完成,效率可以说是杠杠滴了。但是进程内缓存存在两个问题:
Redis这类数据库可以解决进程内缓存的两个问题:
另外Redis提供了很好的读写性能,以及方便的水平扩容能力,还支持多种常用数据结构,使用起来比较方便,可以说是通用缓存首选。
队列在这里的目的是为了解耦,坦白的说这个方案中可以没有队列,业务服务在关系数据库操作完成后,直接更新到缓存也是可以的。 之所以加上这个队列是由于当前的业务开发有很明显的系统拆分的需求,特别是在微服务架构下,为了降低服务之间的耦合,使用队列是个常用选择,在某些开发模型中也是很推崇的,比如Actor模型。
举个例子,比如新注册一个用户,需要赠送其300积分,同时还要给其发个注册成功的邮件,如果将注册用户、赠送积分、发成功邮件都写到一起执行,会产生两个问题:一是注册操作耗时增加,二是其中某个处理引发整体不可用的几率增大,三是程序的扩展性不好;通多引入队列,将注册信息分别发到积分队列和通知队列,然后由积分模块和通知模块分别处理,用户、积分、通知三个模块的耦合降低了,相互影响变小了,以后再增加注册后的其它处理也就是增加个队列的事,整体的扩展性得到了增强。
队列作为一种常用的解耦方案,在缓存这里虽然产生的影响不大,但是除了缓存难免同时还会有其它业务处理,所以为了统一处理机制,这里保留了下来。(既然用了,就把它发扬光大。)
持久化是为了解决网络抖动或者崩溃导致数据丢失的问题,在数据从业务服务到队列,队列自身处理,再从队列到缓存处理程序,中间都可能丢失数据。为了解决丢失数据的问题,需要发送时确认、队列自身持久化、接收时确认;但是需要注意确认机制可能会导致重复数据的产生,因为在未收到确认时就需要重新发送或接收,而数据实际上可能被正常处理,只是确认丢失了;确认机制还会降低队列的吞吐量,但是根据我们的定义业务静态数据的变更频率应该不高,如果同时还需要较高的并发分片是个不错的选择。
这里持久化队列推荐选择RabbitMQ,虽然吞吐量支持的不是很大,但是各方面综合不错,并发够用就好。
在业务服务操作完关系数据库后,数据发送到队列之前(或者不用队列就是直接写入缓存之前),业务服务崩溃了,这时候数据就不能更新到缓存了。还有一种情况是Redis发生了故障转移,master中的更新没有同步到slaver。通过引入这么一个检查程序,定时的检查关系数据库数据和缓存数据的差别,如果缓存数据比较陈旧,则更新之。这样提供了一种极端情况下的挽救措施。
这个检查程序的运行频率需要综合考虑数据库压力和能够承受的数据陈旧时间,不能把数据库查死了,也不能陈旧太久导致大量数据不一致。可以通过设置上次检查时间点的方式,每次只检查从上次检查时间点(或者最近几次,防止Redis故障转移数据未同步的问题)到本次检查时间点发生变更的数据,这样每次检查只对增量变更,效率更高。
同时需要理解在分布式系统中,微服务架构下,数据不一致是经常出现的,必须在一致性和可用性之间做出权衡,尽力去降低影响,比如使用准实时或最终一致性。
假设没有缓存处理程序,通过定时同步关系数据库和缓存数据库是不是就够了呢?这还是取决于业务,如果是车型库这种数据,增加一个新的车型,本来之前就没有,时间上并不是很敏感,这个是可以的。但是对于新增了用户或者车辆,数据消费者还是希望能够马上使用最新的数据进行处理,越快越好,这时使用同步或者准同步更新就能更加贴近需求。Java架构交流学习圈:874811168 面向1-3年经验 Java开发人员 帮助突破瓶颈 提升思维能力
使用缓存过期机制可以不需要缓存处理程序和数据一致检查程序,业务服务首先从Redis查询数据,如果数据存在就直接返回,如果不存在则从关系数据库查询,然后写入Redis,然后再返回,这也是一种常用的缓存处理机制,网上可以查询到很多,很多人用的也很好。
但是缓存的过期时间是个问题:缓存多长时间过期,设置的短可以降低数据的陈旧,但是会增加缓存穿透的概率,即使采用随机的缓存过期时间,在Redis重启或者故障转移的情况下还是会可能导致缓存雪崩,雪崩的情况下采用数据预热机制,也可能会导致服务更长时间的不可用;设置的长可以提升缓存的使用率,但是增加了数据陈旧,在上边对静态数据的定义中对其准确率和实时性都有较高的要求,业务上能不能接受需要考虑。而且如果操作数据和查询存在波动的峰谷,是不是要引入动态TTL的机制,以达到缓存使用和直接访问数据库的一种平衡,这就需要权衡业务需求和技术方案。
通过上边的这些问题问答,再来看看上面提出的微服务架构下静态数据通用缓存处理机制。
对于微服务架构而言,这个机制借助队列这种通用的解耦方式,独立了缓存更新处理,通过准实时更新和定时检查,保证了缓存的实时性和极端情况下较短时间内达到最终一致,通过缓存的持久化机制消除了缓存穿透和雪崩,在缓存的数据较大或读取并发较高时支持水平扩容,可以认为对业务静态数据提供了一种广泛适用的缓存处理机制。
这个方案在某些情况下可能是没有必要的,比如你要缓存一个全国限行的城市列表,使用一个进程内缓存就够了。
最后剩下的就是工作量的问题了,这个会给开发和维护带来复杂性,队列有没有用的顺手的,人手是不是够,业务需求是什么样的,需要考虑清楚。
原网址: 访问
创建于: 2018-11-07 01:36:19
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论