热门标签 | 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限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
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社区 版权所有