Java

面试题:如何架构高性能读服务?

2023-04-17  本文已影响0人  喝杯Java润润BUG

要求

我们来分析一下读服务的特点,读是获取内容,是无状态的或是无副作用的,每一次读都和上一次请求无关,是纯粹地从存储介质中获取原始数据,进行一些逻辑处理,返回给前端展示给用户。

比如,你打开某个电商APP,会进入到首页展示商品,后台服务接到请求后从存储中查询数据进行简单的加工后返回。

针对以上分析我们可以总结出读服务在实现上要满足以几点要求:

cache-aside架构

即使对MySQL等关系型数据库采用了分库分表、读写分离、索引优化等手段,也很难将性能提升到200ms以内。

企业级应用中通常采用性能更高的Redis等内存型数据库来提升性能,常见的架构如下图所:

cache-aside架构也叫懒加载架构:

这种架构的好处是实现简单,采用缓存抵御部分流量,带来了一定的性能提升。但是缺点和带来的问题也很明显。

全量缓存架构

cache-aside架构虽然实现简单但带来很多问题,其中的性能毛刺问题没有好的方案完全解决,基于此演化出
全量缓存架构。

该架构是将需要提供高性能查询的历史数据和实时变更的数据全部同步到缓存,这样就天然的解决性能毛刺问题。

但是因为该架构也需要将MySQL变更的数据实时同步到缓存,所以也存在实时更新的分布式事务问题。

另外对数据的条件查询则需要额外的异构数据了,例如根据类型或状态查询,则需要维护一份根据类型或状态分类的数据id集合了。

基于Binlog的全量缓存架构

上述全量缓存架构存在数据库和缓存分布式事务问题,那如何解决呢?

全量缓存架构的痛点在于写请求对数据库修改的同时也要维护缓存,即程序复杂度增加,所有对数据库修改的地方都要同步修改缓存,所以能不能简化写请求的逻辑呢?答案是可以的。

写请求只修改数据库,同时,有一个模块消费binlog,将对数据库的变更同时施加到缓存。此时的架构如下图:

MySQL 的主从数据同步就是基于Binlog的,主库会将所有的变更按一定格式写入它本机的 Binlog 文件中。在主从同步时,从库会和主库建立连接,通过特定的协议串行地读取主库的 Binlog 文件,并在从库进行 Binlog 的回放,进而完成主从复制。

现在很多开源工具(如阿里的 Canal、MySQL_Streamer、Maxwell、Linkedin 的 Databus 等)可以模拟主从复制的协议。通过模拟协议读取主库的 Binlog 文件,可以理解为伪装成从库,从而获取主库的所有变更。对于这些变更,它们开放了各种接口供业务服务获取数据。

将 Binlog 的中间件挂载至目标数据库上,就可以实时获取该数据库的所有变更数据。对这些变更数据解析后,便可直接写入缓存里。

订阅Binlog那一刻起的,所有插入和更新的数据,都不会遗漏。

那在此之前的历史数据,怎么同步到缓存呢?

有一种简单的方式是:如果表里有类似【修改时间】这些非业务字段,可以在订阅Binlog之后,手动地更新所有历史数据的修改时间,就可以触发binlog,从而同步历史数据了。

采用基于Binlog的全量缓存架构,主要有以下优点:

不要为了架构而架构,架构不是一味的堆技术,适合自己的才是最好的。

基于Binglog的全量缓存架构在落地时会有很多的细节和需要解决的问题我们还没有详细分析,例如Binlog日志如何发送,如何高效消费Binlog,缓存的数据结构如何选择及缓存更新存在的问题如何解决,这些问题都会在下一篇文章中体现。

上一篇下一篇

猜你喜欢

热点阅读