程序员快来学习缓存层场景实战数据收集—技术选型思路及整体方案

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 根据以上业务场景,项目组提炼出了6点业务需求,并针对业务需求梳理了技术选型相关思路。1)原始数据海量:对于这一点,初步考虑使用HBase进行持久化。2)对于埋点记录的请求响应要快:埋点记录服务会把原始埋点记录存放在一个缓存层,以此保证响应快速。关于这一点有多个缓存方案,稍后展开讨论。3)可通过后台查询原始数据:如果直接使用HBase作为查询引擎,查询速度太慢,所以还需要使用Elasticsearch来保存查询页面上作为查询条件的字段和活动ID。

技术选型思路

根据以上业务场景,项目组提炼出了6点业务需求,并针对业务需求梳理了技术选型相关思路。

1)原始数据海量:对于这一点,初步考虑使用HBase进行持久化。

2)对于埋点记录的请求响应要快:埋点记录服务会把原始埋点记录存放在一个缓存层,以此保证响应快速。关于这一点有多个缓存方案,稍后展开讨论。

3)可通过后台查询原始数据:如果直接使用HBase作为查询引擎,查询速度太慢,所以还需要使用Elasticsearch来保存查询页面上作为查询条件的字段和活动ID。

4)各种统计报表的需求:数据可视化工具也有很多选择,比如Kibana、Grafana等,考虑到使用过程的灵活性,最终选择自己设计功能。

5)能根据埋点日志生成费用结算数据:将费用结算数据保存在MySQL中。

6)需要一个框架将缓存中的数据进行处理,并保存到Elasticsearch、HBase和MySQL中:因为业务有准实时查询的需求,所以需要使用实时处理工具。目前流行的实时处理工具主要为Storm、Spark Streaming、Apache Flink这3种,稍后也会展开说明。

初步架构图如图6-1所示。

• 图6-1 数据收集初步架构图

仔细观察这张架构图,会发现图上还有两个地方打了问号,这是为什么?这就涉及接下来需要讨论的4个问题了。

使用什么技术保存埋点数据的第一现场

目前关于快速保存埋点数据的技术主要分为Redis、Kafka、本地日志这3种,针对这里的业务场景,项目组最终选择了本地日志。

那么,为什么不使用Redis或Kafka呢?先来说说Redis的AOF机制,这在写缓存那一章也有讲过。

Redis的AOF机制会持久化保存Redis所有的操作记录,用于服务器宕机后的数据还原。那Redis什么时候将AOF落盘呢?

在Redis中存在一个AOF配置项appendfsync,如果appendfsync配置为everysec,则AOF每秒落盘一次,不过这种配置方式有可能会丢失一秒的数据;如果appendfsync配置成always,每次操作请求的记录都是落盘后再返回成功信息给客户端,不过使用这种配置方式系统运行会很慢。因为对埋点记录的请求要求响应快,所们该项目没有选择Redis。

接下来讨论一下Kafka的技术方案。

Kafka的冗余设计是每个分区都有多个副本,其中一个副本是Leader,其他副本都是Follower,Leader主要负责处理所有的读写请求,并同步数据给其他Follower。

那么Kafka什么时候将数据从Leader同步给Follower?Kafka的Producer配置中也有acks配置项,其值有3种。

1)acks=0:不等Leader将数据落到日志,Kafka直接返回完成信号给客户端。这种方式虽然响应快,但数据持久化没有保障,数据如果没有落到本地日志,系统就会出现宕机,导致数据丢失。

2)acks=1:等Leader将数据落到本地日志,但是不等Follower同步数据,Kafka就直接返回完成信号给客户端。

3)acks=all:等Leader将数据落到日志,且等min.insync.replicas个Follower都同步数据后,Kafka再返回完成信号给客户端。这种配置方式下虽然数据有保证,但响应慢。

通过以上分析可以发现,使用Redis与Kafka都会出现问题。

如果想保证数据的可靠性,必然需要牺牲系统性能,那有没有一个方案可以性能和可靠性兼得呢?有。项目组最终决定把埋点数据保存到本地日志中。

使用什么技术收集日志数据到持久化层

关于这个问题,最简单的方式是通过Logstash直接把日志文件中的数据迁移到Elasticsearch,但会有一个问题:业务侧要求存放Elasticsearch中的记录(包含城市、性别、年龄等原始数据,这些字段需要调用业务系统的数据进行抽取),而这些原始数据日志文件中并没有,所以中间需要调用业务系统来获取一些数据跟日志文件的数据合起来加工。基于这个原因,项目组并没有选择直接从Logstash到Elasticsearch。

如果坚持通过Logstash把日志文件的数据迁移到Elasticsearch,这里分享3种实现方式。

1)自定义filter:先在Logstash自定义的Filter(过滤器)里封装业务数据,再保存到Elasticsearch。因为Logstash自定义的Filter是使用Ruby语言编写的,也就是说需要使用其他语言编写业务逻辑,所以此次项目中Logstash自定义Filter的方案被排除了。

2)修改客户端的埋点逻辑:每次记录埋点的数据发送到服务端之前,先在客户端将业务的相关字段提取出来再上传到服务端。这个方法也直接被业务端否决了,理由是后期业务侧每更新一次后台查询条件,就需要重新发一次版,实在太麻烦了。

3)修改埋点服务端的逻辑:每次服务端在记录埋点的数据发送到日志文件之前,先从数据库获取业务字段组合埋点记录。这个方法也被服务端否决了,因为这种操作会直接影响每个请求的效率,间接影响用户体验。

另外,没有选择用Logstash直接保存到持久化层还有两点原因。

1)日志文件中的数据需要同时到Elasticsearch和HBase两个输出源,因Logstash的多输出源基于同一个Pipeline(管道),如果一个输出源出错了,另一个输出源也会出错,即两者之间会互相影响。

2)MySQL中需要生成费用结算数据,而费用结算数据需要通过分析埋点的数据来动态计算,显然Logstash并不适用于这样的业务场景。

在此处的业务场景中,项目组最终决定引入一个计算框架,此时整个解决方案的架构如图6-2所示。

• 图6-2 基于Logstash的数据收集架构

这个方案就是先通过Logstash把日志文件迁移到MQ中,再通过实时计算框架处理MQ中的数据,最后保存处理转换得到的数据到持久层中。

实际上,引入实时计算框架是为了在原始的埋点数据中填充业务数据,并统计埋点数据生成费用结算数据,最后分别保存到持久层中。

最后,关于Logstash还需要强调几点。

Logstash系统是通过Ruby语言编写的,资源消耗大,所以官方又推出了一个轻量化的Filebeat。系统可以使用Filebeat收集数据,再通过Logstash进行数据过滤。如果不想使用Logstash的强大过滤功能,可以直接使用Filebeat来收集日志数据发送给Kafka。

但问题又来了,Filebeat是使用轮询方式采集文件变动信息的,存在一定延时(有时候很大),不像Logstash那样可直接监听文件变动,所以该项目最终选择继续使用Logstash(资源消耗在可接受范围内)。

接下来分别讨论Kafka和分布式实时计算框架。

为什么使用Kafka

Kafka是LinkedIn推出的开源消息中间件,它天生是为收集日志而设计的,而且具备超高的吞吐量和数据量扩展性,被称作无限堆积。、

根据LinkedIn的官方介绍,他们使用3台便宜的机器部署Kafka,就能每秒写入两百万条记录,如图6-3所示。

图6-3 官方介绍

为什么它的吞吐量这么高?这里介绍一下Kafka的存储结构。先看一张官方文档给出的示意图,如图6-4所示。

Kafka的存储结构中每个Topic分区相当于一个巨型文件,而每个巨型文件又是由多个Segment小文件组成的。其中,Producer负责对该巨型文件进行“顺序写”,Consumer负责对该文件进行“顺序读”。

这里可以把Kafka的存储架构简单理解为,Kafka写数据时通过追加数据到文件末尾来实现顺序写,读取数据时直接从文件中读,这样做的好处是读操作不会阻塞写操作,这也是其吞吐量大的原因。

• 图6-4 Kafka的存储结构

另外,理论上只要磁盘空间足够,Kafka就可以实现消息无限堆积,因此它特别适合处理日志收集这种场景。

使用什么技术把Kafka的数据迁移到持久化层

为了把Kafka的数据迁移到持久层,需要使用一个分布式实时计算框架,原因有两点。

1)数据量特别大,为此需要使用一个处理框架来将上亿的埋点数据每天进行快速分析和处理(且必须使用多个节点并发处理才来得及),再存放到Elasticsearch、HBase和MySQL中,即大数据计算,因此它有分布式计算的诉求。

2)业务要求实时查询统计报表数据,因此需要一个实时计算框架来处理埋点数据。

目前流行的分布式实时计算框架有3种:Storm、Spark Stream、ApacheFlink。那么使用哪个更好呢?

这3种都可以选用,就看公司的具体情况了。比如公司已经使用了实时计算框架,就不再需要考虑这个问题;如果公司还没有使用,那就看个人喜好了。

笔者更喜欢用Apache Flink,不仅因为它性能强(阿里采用这项技术后,活动期间一秒内能够处理17亿条数据),还因为它的容错机制能保证每条数据仅仅处理一次,而且它有时间窗口处理功能。

关于流处理的容错机制、时间窗口这两个概念,具体展开说明一下。

在流处理这个过程中,往往会引发一系列的问题,比如一条消息的处理过程中,如果系统出现故障该怎么办?会重试吗?如果重试会不会出现重复处理?如果不重试,消息是否会丢失?能保证每条消息最多或最少处理几次?

在不同流处理框架中采取不同的容错机制,能够保证不一样的一致性。

1)At-Most-Once:至多一次,表示一条消息不管后续处理成功与否只会被消费处理一次,存在数据丢失的可能。

2)Exactly-Once:精确一次,表示一条消息从其消费到后续的处理成功只会发生一次。

3)At-Least-Once:至少一次,表示一条消息从消费到后续的处理成功可能会发生多次,存在重复消费的可能。

以上3种方式中,Exactly-Once无疑是最优的选择,因为在正常的业务场景中,一般只要求消息处理一次,而Apache Flink的容错机制就可以保证所有消息只处理一次(Exactly-Once)的一致性,还能保证系统的安全性,所以很多人最终都会使用它。

接下来说说Apache Flink的时间窗口计算功能。以下是Apache Flink的一个代码示例,它把每个小时里发生事件的用户聚合在一个列表中。

日志中事件发生的时间有可能与计算框架处理消息的时间不一致。

假定实时计算框架收到消息的时间是2秒后,有一条消息中的事件发生时间是6:30,因接收到消息后处理的时间延后了2秒,即变成了6:32,所以当计算6:01~6:30的数据和时,这条消息并不会计算在内,这就不符合实际的业务需求了。

在实际业务场景中,如果需要按照时间窗口统计数据,往往是根据消息的事件时间来计算。Apache Flink的特性恰恰是使用了基于消息的事件时间,而不是基于计算框架的处理时间,这也是它的另一个撒手锏。

整体方案

此时整个架构设计方案如图6-5所示。

这个架构的流程如下。

1)后台服务端会记录所有的请求数据,存放到本地的日志文件。

2)使用数据收集框架Logstash,从日志文件抽取原始的日志数据,不加

工直接存放到Kafka当中。3)通过Apache Flink从Kafka中拉取原始的日志数据,并且经过业务加工,分别存放到Elasticsearch、HBase和MySQL中。

4)Elasticsearch用来处理用户针对请求日志的查询请求,它将查询关键字段的值和请求ID存放到索引中,跟进查询关键字获得结果ID的列表,再通过结果ID去HBase中获取详细的请求数据。

• 图6-5 数据收集整体方案

5)MySQL存放一些组合加工后的数据,用来做结算,结算的数据查询和处理请求量不大。

小结

本篇并没有讲解特别深入的架构设计方面的注意事项,而是主要阐述技术选型背后的思考过程,希望对架构思维的提升有所帮助。

前面几篇已有类似的场景,所以本篇没有详细讲解技术应用背后的场景。

学架构的过程就是经历一些基础的场景,而那些复杂的场景其实是简单场景的叠加复用。因此,之后对于比较简单或前面已经讲过的场景仅会简单介绍,以留出更多的篇幅来讲解其他重要知识。这样做可能会让后面的内容有些枯燥,但相信看到这里的读者已经可以带着对场景的理解和思考去吸收相关的知识了。

回到这个架构本身。方案落地以后,丢数据的情况并不多,而且其架构的扩展性也很好,之后日活达到了几千万,系统仍然可以使用,当然,还是要多加机器,并且定时清理一些旧的原始数据。写缓存介绍过,写缓存不能解决两个问题,一个就是长期高并发写数据,这在本章得到了解决。另外一个就是高并发且这些并发请求需要抢资源的情况,这就是下篇涉及的内容了。

接下来开始讲解秒杀架构。秒杀架构是一个综合性非常强的问题,并且在面试时经常被问到,所以是很重要的场景。

本文给大家讲解的内容是缓存层场景实战,技术选型思路及整体方案

  1. 下篇文章给大家讲解的内容是缓存层场景实战,秒杀架构,业务场景:设计秒杀架构必知必会的那些事以及整体的思路
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!
  4. 本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。
相关文章
|
4月前
|
消息中间件 canal 缓存
项目实战:一步步实现高效缓存与数据库的数据一致性方案
Hello,大家好!我是热爱分享技术的小米。今天探讨在个人项目中如何保证数据一致性,尤其是在缓存与数据库同步时面临的挑战。文中介绍了常见的CacheAside模式,以及结合消息队列和请求串行化的方法,确保数据一致性。通过不同方案的分析,希望能给大家带来启发。如果你对这些技术感兴趣,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
246 6
项目实战:一步步实现高效缓存与数据库的数据一致性方案
|
3月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
87 6
|
4月前
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
1天前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
|
5月前
|
缓存 安全 Android开发
Android经典实战之用Kotlin泛型实现键值对缓存
本文介绍了Kotlin中泛型的基础知识与实际应用。泛型能提升代码的重用性、类型安全及可读性。文中详细解释了泛型的基本语法、泛型函数、泛型约束以及协变和逆变的概念,并通过一个数据缓存系统的实例展示了泛型的强大功能。
48 2
|
1月前
|
缓存 NoSQL Java
Spring Boot中的分布式缓存方案
Spring Boot提供了简便的方式来集成和使用分布式缓存。通过Redis和Memcached等缓存方案,可以显著提升应用的性能和扩展性。合理配置和优化缓存策略,可以有效避免常见的缓存问题,保证系统的稳定性和高效运行。
50 3
|
3月前
|
存储 缓存 数据库
缓存技术有哪些应用场景呢
【10月更文挑战第19天】缓存技术有哪些应用场景呢
|
3月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
41 3
|
3月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
83 4
|
5月前
|
缓存 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)