热门标签 | 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【精华分享大全】,希望对大家有所帮助。


推荐阅读
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • mysql-cluster集群sql节点高可用keepalived的故障处理过程
    本文描述了mysql-cluster集群sql节点高可用keepalived的故障处理过程,包括故障发生时间、故障描述、故障分析等内容。根据keepalived的日志分析,发现bogus VRRP packet received on eth0 !!!等错误信息,进而导致vip地址失效,使得mysql-cluster的api无法访问。针对这个问题,本文提供了相应的解决方案。 ... [详细]
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社区 版权所有