热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

地理分布式微服务及其数据库:对抗高延迟的方法icode9精华分享大全(2022已更新)(今日/科普)

应用层的微服务实例分散在全球范围内的云选择区域。由KongGateway提供支持的API层允许微服务通过简单的REST端点相互通信。全局负载均衡器在最近的PoP(存在点)拦截用户请

应用层的微服务实例分散在全球范围内的云选择区域。由KongGateway提供支持的API层允许微服务通过简单的REST端点相互通信。

全局负载均衡器在最近的PoP(存在点)拦截用户请求,并将请求转发到地理上离用户最近的微服务实例。

一旦微服务实例收到来自负载均衡器的请求,服务很可能需要从数据库中读取数据或向其写入更改。而这一步可能会成为一个痛苦的瓶颈——如果数据(数据库)远离微服务实例和用户,您将需要解决这个问题。

在本文中,我将选择一些多区域数据库部署选项,并演示如何保持数据库查询的读写延迟较低,而不管用户的位置如何。

所以,如果你还在我的旅途中陪我,那么,就像海盗们常说的那样,“称重锚,起锚!” 这意味着,“拉起锚,让这艘船航行!”


多区域数据库部署选项

多区域数据库部署没有灵丹妙药。这都是关于权衡的。每个选项都提供优势,而其他选项会让您付出代价。

YugabyteDB是我选择的分布式SQL数据库,它支持地理分布式应用程序使用的四种多区域数据库部署选项:




  1. 单一延伸集群:数据库集群跨多个区域“延伸”。这些地区通常位于相对较近的地方(例如,美国中西部和东部地区)。



  2. 具有只读副本的单一延伸集群:与前一个选项一样,集群部署在一个地理位置(例如,北美),跨云区域相对较近(例如,美国中西部和东部地区)。但是使用此选项,您可以将只读副本节点添加到遥远的地理位置(例如欧洲和亚洲),以提高读取工作负载的性能。



  3. 单一地理分区集群:数据库集群分布在多个遥远的地理位置(例如北美、欧洲和亚洲)。每个地理位置都有自己的一组节点,它们部署在一个或多个非常接近的本地区域(例如,美国中西部和东部地区)。根据地理分区列的值,数据会自动固定到特定的节点组。使用此部署选项,您可以在全球范围内实现读取和写入工作负载的低延迟。



  4. 具有异步复制的多个集群:多个独立集群部署在各个区域。这些区域可以位于相对较近的位置(例如,美国中西部和东部地区)或位于较远的位置(例如,美国东部和亚洲南部地区)。更改在集群之间异步复制。您将在全球范围内实现读取和写入工作负载的低延迟,与之前的选项相同,但您将处理异步交换更改的多个独立集群。


好的,伙计,让我们继续并查看地理信使用例的前三个部署选项。我将跳过第四个,因为它不适合需要单个数据库集群的信使架构。


美国的单一延伸集群

第一个集群跨越美国的三个地区——美国西部、中部和东部。

图片说明

应用程序/微服务实例和API服务器(图中未显示)在相同的位置以及欧洲西部和亚洲东部地区运行。


美国流量的数据库读/写延迟

假设Blue女士本周在美国爱荷华州工作。她打开地理信使向公司频道发送便条。她的流量将由部署在美国中部地区的微服务实例处理。

在Blue女士可以发送消息之前,USCentral微服务实例必须加载通道的消息历史记录。哪个数据库节点将为该读取请求提供服务?

在我的例子中,美国中部地区被配置为YugabyteDB的首选地区,这意味着来自该地区的数据库节点将处理来自应用层的所有读取请求。将频道的消息历史从美国中央数据库节点加载到同一区域的应用程序实例需要10-15毫秒。这是我的SpringBoot日志的输出,最后一行显示了查询执行时间:


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
INFO11744---[-nio-80-exec-10]i.StatisticalLoggingSessionEventListener:SessionMetrics{
1337790nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1413081nanosecondsspentpreparing1JDBCstatements;
14788369nanosecondsspentexecuting1JDBCstatements;(14ms!)

接下来,当Blue女士将消息发送到通道中时,微服务和数据库之间的延迟将在90毫秒左右。比之前的操作要花更多的时间,因为Hibernate为我的方法调用生成了多个SQL查询JpaRepository.save(message)(当然可以优化),然后YugabyteDB需要在跨美国西部、中部和东部运行的所有节点上存储消息的副本. 以下是输出和延迟的样子:


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
31908nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
461058nanosecondsspentpreparing3JDBCstatements;
91272173nanosecondsspentexecuting3JDBCstatements;(90ms)

亚太地区流量的数据库读/写延迟

请记住,每次多区域数据库部署都会有一些权衡。在当前的数据库部署中,美国流量的读/写延迟较低,但来自其他更远位置的流量较高。让我们看一个例子。

想象一下,Blue女士的同事Red先生收到来自Blue女士的最新消息的推送通知。由于Red先生在台湾出差,他的流量将由部署在该岛上的应用程序实例处理。

但是台湾附近没有部署数据库节点,所以微服务实例要查询美国运行的数据库节点。这就是为什么在Red先生看到Blue女士的消息之前平均需要165毫秒来加载整个频道的历史记录:


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
[p-nio-80-exec-8]i.StatisticalLoggingSessionEventListener:SessionMetrics{
153152267nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
153217915nanosecondsspentpreparing1JDBCstatements;
165798894nanosecondsspentexecuting1JDBCstatements;(165ms)

当Red先生在同一频道响应Blue女士时,Hibernate大约需要450毫秒来准备、发送和存储消息到数据库中。好吧,由于物理定律,带有消息的数据包必须从台湾穿越太平洋,然后消息副本必须存储在美国西部、中部和东部:


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
selectnextval('message_id_seq')
insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
23488nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
137247nanosecondsspentpreparing3JDBCstatements;
454281135nanosecondsspentexecuting3JDBCstatements(450ms);

现在,对于不针对该地区用户的应用程序来说,基于亚太地区的流量的高延迟可能不是什么大问题,但在我的情况下并非如此。我的地理信使必须在全球范围内顺利运行。让我们对抗这种高延迟,伙计!我们将从阅读开始!


在远程位置部署只读副本

改善YugabyteDB中读取工作负载延迟的最直接方法是在远程位置部署只读副本节点。这是一项可以在实时集群上执行的纯操作任务。

因此,我将一个副本节点“附加”到我的基于美国的实时数据库集群,并且该副本被放置在为Mr.Red提供请求的微服务实例附近的亚洲东部地区。

图片说明

然后我请求台湾的应用程序实例使用该副本节点进行数据库查询。Mr.Red的频道历史预加载延迟时间从165毫秒降至10-15毫秒这与在美国的Blue女士一样快。


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?orderbymessage0_.idasc
i.StatisticalLoggingSessionEventListener:SessionMetrics
1210615nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1255989nanosecondsspentpreparing1JDBCstatements;
12772870nanosecondsspentexecuting1JDBCstatements;(12mseconds)

因此,使用只读副本,我的地理信使可以以低延迟服务读取请求,而不管用户的位置如何!

但现在庆祝还为时过早。写入仍然太慢。

想象一下,Mr.Red向频道发送了另一条消息。来自台湾的微服务实例将要求副本节点执行查询。并且副本会将Hibernate生成的几乎所有请求转发到存储主要记录副本的美国节点。因此,亚太地区流量的延迟仍可能高达640毫秒:


Hibernate:settransactionreadwrite;
Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
23215nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
141888nanosecondsspentpreparing4JDBCstatements;
640199316nanosecondsspentexecuting4JDBCstatements;(640ms)

最后,伙计,让我们一劳永逸地解决这个问题!


切换到全球地理分区集群

全局地理分区集群可以跨远程位置提供快速读取和写入,但需要您在数据库模式中引入特殊的地理分区列。根据列的值,数据库将自动决定一条记录属于哪个地理区域的哪个数据库节点。


数据库架构更改

简而言之,我的表(例如Messageone)定义了country_code列:


CREATETABLEMessage(
idintegerNOTNULLDEFAULTnextval('message_id_seq'),
channel_idinteger,
sender_idintegerNOTNULL,
messagetextNOTNULL,
attachmentbooleanNOTNULLDEFAULTFALSE,
sent_atTIMESTAMPNOTNULLDEFAULTNOW(),
country_codevarchar(3)NOTNULL
)PARTITIONBYLIST(country_code);

根据该列的值,可以将记录放置在以下数据库分区之一中:


CREATETABLEMessage_USA
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('USA')TABLESPACEus_central1_ts;
CREATETABLEMessage_EU
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('DEU')TABLESPACEeurope_west3_ts;
CREATETABLEMessage_APAC
PARTITIONOFMessage
(id,channel_id,sender_id,message,sent_at,country_code,sender_country_code,
PRIMARYKEY(id,country_code))
FORVALUESIN('TWN')TABLESPACEasia_east1_ts;

每个分区都映射到其中一个表空间:


CREATETABLESPACEus_central1_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"us-central1","zone":"us-central1-b","min_num_replicas":1}]}'
);
CREATETABLESPACEeurope_west3_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"europe-west3","zone":"europe-west3-b","min_num_replicas":1}]}'
);
CREATETABLESPACEasia_east1_tsWITH(
replica_placement='{"num_replicas":1,"placement_blocks":
[{"cloud":"gcp","region":"asia-east1","zone":"asia-east1-b","min_num_replicas":1}]}'
);

每个表空间都属于来自特定地理位置的一组数据库节点。例如,所有国家代码为台湾country_code=TWN如果您想了解地理分区的详细信息,请查看以下文章。


跨大陆的低读/写延迟

因此,我在美国中部、欧洲西部和亚洲东部地区部署了一个三节点地理分区集群。

图片说明

现在,让我们确保Red先生的请求的读取延迟保持不变。与只读副本部署一样,加载通道的消息历史记录仍需要5-15毫秒(对于属于APAC区域的通道和消息):


Hibernate:selectmessage0_.country_codeascountry_1_1_,message0_.idasid2_1_,message0_.attachmentasattachme3_1_,message0_.channel_idaschannel_4_1_,message0_.messageasmessage5_1_,message0_.sender_country_codeassender_c6_1_,message0_.sender_idassender_i7_1_,message0_.sent_atassent_at8_1_frommessagemessage0_wheremessage0_.channel_id=?andmessage0_.country_code=?orderbymessage0_.idasc
i.StatisticalLoggingSessionEventListener:SessionMetrics
1516450nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
1640860nanosecondsspentpreparing1JDBCstatements;
7495719nanosecondsspentexecuting1JDBCstatements;(7ms)

并且……请鼓掌……当Red先生向APAC频道发布消息时,写入延迟已从平均400-650毫秒降至6毫秒!


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
1123280nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
123249nanosecondsspentpreparing3JDBCstatements;
6597471nanosecondsspentexecuting3JDBCstatements;(6ms)

任务完成,伙计!现在,我的geo-messenger的数据库可以跨国家和大洲以低延迟提供读取和写入服务。我只需要告诉我的信使在哪里部署微服务实例和数据库节点。


跨大陆查询的案例

现在快速评论一下为什么我跳过了具有多个独立YugabyteDB集群的数据库部署选项。

对我来说,为Messenger提供一个数据库很重要,这样:



  • 用户可以加入属于任何地区的讨论频道。

  • 任何位置的微服务实例都可以访问任何位置的数据。

例如,如果Red先生加入属于美国节点的讨论频道country_code=’USA'(-基于对应物。此操作的延迟由三个SQL请求组成,大约为165毫秒


Hibernate:selectmessage0_.country_codeascountry_1_1_0_,message0_.idasid2_1_0_,message0_.attachmentasattachme3_1_0_,message0_.channel_idaschannel_4_1_0_,message0_.messageasmessage5_1_0_,message0_.sender_country_codeassender_c6_1_0_,message0_.sender_idassender_i7_1_0_,message0_.sent_atassent_at8_1_0_frommessagemessage0_wheremessage0_.country_code=?andmessage0_.id=?
Hibernate:selectnextval('message_id_seq')
Hibernate:insertintomessage(attachment,channel_id,message,sender_country_code,sender_id,sent_at,country_code,id)values(?,?,?,?,?,?,?,?)
i.StatisticalLoggingSessionEventListener:SessionMetrics
1310550nanosecondsspentacquiring1JDBCconnections;
0nanosecondsspentreleasing0JDBCconnections;
159080nanosecondsspentpreparing3JDBCstatements;
164660288nanosecondsspentexecuting3JDBCstatements;(164ms)

165毫秒无疑高于6毫秒(Red先生将消息发布到基于亚太地区的本地频道时的延迟),但这里重要的是能够在必要时通过单个数据库连接发出跨大陆请求。另外,正如执行计划所示,在Hibernate级别有很大的优化空间。目前,Hibernate将我的JpaRepository.save(message)调用转换为3条JDBC语句。这是可以进一步优化的,以将跨大陆请求的延迟从165毫秒降低到低得多的值。

以上就是今天(2022年11月9日19:35:58)小编为大家整理的icode9【精华分享大全】,希望对大家有所帮助。


推荐阅读
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 本文详细介绍了 Java 中 org.apache.xmlbeans.SchemaType 类的 getBaseEnumType() 方法,提供了多个代码示例,并解释了其在不同场景下的使用方法。 ... [详细]
  • 本文由瀚高PG实验室撰写,详细介绍了如何在PostgreSQL中创建、管理和删除模式。文章涵盖了创建模式的基本命令、public模式的特性、权限设置以及通过角色对象简化操作的方法。 ... [详细]
  • 本文详细介绍了 MySQL 中 LAST_INSERT_ID() 函数的使用方法及其工作原理,包括如何获取最后一个插入记录的自增 ID、多行插入时的行为以及在不同客户端环境下的表现。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 本文探讨了领域驱动设计(DDD)的核心概念、应用场景及其实现方式,详细介绍了其在企业级软件开发中的优势和挑战。通过对比事务脚本与领域模型,展示了DDD如何提升系统的可维护性和扩展性。 ... [详细]
  • 本文探讨了MariaDB在当前数据库市场中的地位和挑战,分析其可能面临的困境,并提出了对未来发展的几点看法。 ... [详细]
  • 本文探讨了在Oracle数据库中,动态SQL语句的执行及其对事务管理的影响,特别是关于回滚操作的有效性。重点讨论了一个具体场景:将预警短信从当前表迁移到历史表时遇到的字段长度不匹配问题及相应的异常处理。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 使用Pandas高效读取SQL脚本中的数据
    本文详细介绍了如何利用Pandas直接读取和解析SQL脚本,提供了一种高效的数据处理方法。该方法适用于各种数据库导出的SQL脚本,并且能够显著提升数据导入的速度和效率。 ... [详细]
  • MySQL DateTime 类型数据处理及.0 尾数去除方法
    本文介绍如何在 MySQL 中处理 DateTime 类型的数据,并解决获取数据时出现的.0尾数问题。同时,探讨了不同场景下的解决方案,确保数据格式的一致性和准确性。 ... [详细]
  • 通过Web界面管理Linux日志的解决方案
    本指南介绍了一种利用rsyslog、MariaDB和LogAnalyzer搭建集中式日志管理平台的方法,使用户可以通过Web界面查看和分析Linux系统的日志记录。此方案不仅适用于服务器环境,还提供了详细的步骤来确保系统的稳定性和安全性。 ... [详细]
author-avatar
dx152
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有