您好,欢迎来到华佗健康网。
搜索
您的当前位置:首页记一次微服务架构下数据库设计

记一次微服务架构下数据库设计

来源:华佗健康网
记⼀次微服务架构下数据库设计

项⽬背景:现项⽬主要是做关于机器⼈的调度系统,涉及到web端、移动端、⼩程序及服务端和实体机器⼈端;迭代背景:采⽤的敏捷开发,迭代周期为两周,本次迭代的US为机器⼈注册登录流程记录⽅向:记录⼀次微服务架构下数据库设计

记录时间:20200104,测试阻塞,反补测试功能点及测试⽤例后;

备注:该次记录主要分为三部分:该US需求、接⼝活动图、数据库设计、微服务架构的数据库设计

⼀、机器⼈注册登录需求

功能介绍:实现机器⼈登录、认证和状态管理功能;参与者:机器⼈,mars管理系统服务。

流程步骤:机器⼈或服务端服务⽀持的操作如下:    机器⼈可以通过接⼝获取MQTT登录信息;

    机器⼈可以通过获取到的MQTT登录信息连接MQTT Broker;    服务端服务可以监听设备上下线状态;

    服务端服务可以通过API主动查询客户端在线状态;1、需求详细描述1.1、连接安全

a、http接⼝使⽤https接⼝;

b、平台为每个机器⼈产品(同⼀个⼚家下的相同机器⼈类型确定⼀个产品)分配不同APPID/APPKEY,该信息内置到机器⼈应⽤中,机器⼈需要保证APPKEY的安全性,后续协议和设计过程中不可对外暴露相关信息;

c、针对不同⼚家的机器⼈产品使⽤不同的应⽤ID,每个应⽤ID只能获取与该应⽤ID绑定的设备的登录信息;d、每⼀个机器⼈的登录凭证只能⽤于该机器⼈的登录;

e、登录凭证由后端办法,每⼀个登录凭证有固定有效期,凭证不能包含设备敏感信息,有效期到达后需要重新获取凭证。凭证篡改后⽆效。

f、连接的clientId由平台分配,每个机器⼈使⽤不同clientId,机器⼈每次获取设备登录信息时重新⽣成新的clientId,同时旧id失效;g、每个产品的机器⼈可以制定不同的topic权限策略(只读、只写或读写),实现topic级别的ACL权限控制;h、MQTT连接设置选择使⽤TLS和⾮TLS的连接⽅式;1.2、broker信息

a、机器⼈连接的broker信息在请求时返回,返回信息包含连接地址、连接⽤户名和登陆令牌、加密⽅式等;b、broker信息和产品绑定,每⼀个产品对应的机器⼈可以使⽤不同的broker也可以多个产品使⽤相同broker;1.3、状态管理

a、机器⼈上下线时服务端可以及时感知出发相应动作;

b、服务端可以主动查询指定appID下指定设备的当前设备状态;

⼆、接⼝活动图

接⼝活动图就不在这⼉给贴出来了!

三、数据库设计

备注:外键只是为了⽅便查看依赖关系,在实际创建数据库时会删除所有外键

四、微服务架构下的数据库设计

=========该部分转载⾃:==================

单独的数据库:

微服务设计的⼀个关键是数据库设计,基本原则是每个服务都有⾃⼰单独的数据库,⽽且只有微服务本⾝可以访问这个数据库。它是基于下⾯三个原因。

优化服务接⼝:微服务之间的接⼝越⼩越好,最好只有服务调⽤接⼝(RPC或消息),没有其他接⼝。如果微服务不能独享⾃⼰的数据库,那么数据库也变成了接⼝的⼀部分,这⼤⼤拓展了接⼝范围。

错误诊断:⽣产环境中的错误⼤部分都是和数据库有关的,要么是数据出了问题,要么是数据库的使⽤⽅式出了问题。当你不能完全控制数据库的访问时,会有各种各样的错误发⽣。它可能是别的程序直接连到你的数据库或者是其他部门直接⽤客户端访问数据库的数据,⽽这些都是在程序中查不到的,增加了错误排查难度。如果是程序中的问题,只要修改了代码,那么这个错误就不会再有。⽽上⾯提到的错误,你永远都没法预测它们什么时候还会再次发⽣。

性能调优:性能调优也是⼀样,你需要对数据库有全权控制才能保证它的性能。如果其他部门⼀定要访问数据库,⽽且只是查询的话,那么可以另外创建⼀份只读数据库,让他们在另⼀个库中查询,这样才不会影响到你的库。

理想的设计是你的数据库只有你的服务能访问,你也只调⽤⾃⼰数据库中的数据,所有对别的微服务的访问都通过服务调⽤来实现。当然,在实际应⽤中,单纯的服务调⽤可能不能满⾜性能或其他要求,不同的微服务都多少需要共享⼀些数据。

共享数据:

微服务之间的数据共享可以有下四种⽅式。静态表:

有⼀些静态的数据库表,例如国家,可能会被很多程序⽤到,⽽且程序内部需要对国家这个表做连接(join)⽣成最终⽤户展⽰数据,这样⽤微服务调⽤的⽅式就效率不⾼,影响性能。⼀个办法是在每个微服务中配置⼀个这样的表,它是只读的,这样就可以做数据库连接了。当然你需要保证数据同步。这个⽅案在多数情况下都是可以接受的,因为以下两点:

1. 静态的数据库表结构基本不变:因为⼀旦表结构变了,你不但要更改所有微服务的数据库表,还要修改所有微服务的程序。

2. 数据库表中的数据变化不频繁:这样数据同步的⼯作量不⼤。另外当你同步数据库时总会有延迟,如果数据变化不频繁那么你有很多同步⽅式可供选择。

只读业务数据访问:

如果你需要读取别的数据库⾥的动态业务数据, 理想的⽅式是服务调⽤。如果你只是调⽤其他微服务做⼀些计算,⼀般情况下性能都是可以接受的。如果你需要做数据的连接,那么你可以⽤程序代码来做,⽽不是⽤SQL语句。如果测试之后性能不能满⾜要求,那你可以考虑在⾃⼰的数据库⾥建⼀套只读数据表。数据同步⽅式⼤致有两种。如果是事件驱动⽅式,就⽤发消息的⽅式进⾏同步,如果是RPC⽅式,就⽤数据库本⾝提供的同步⽅式或者第三⽅同步软件。

通常情况下,你可能只需要其他数据库的⼏张表,每张表只需要⼏个字段。这时,其他数据库是数据的最终来源,控制所有写操作以及相应的业务验证逻辑,我们叫它主表。你的只读库可以叫从表。 当⼀条数据写⼊主表后,会发⼀条⼴播消息,所有拥有从表的微服务监听消息并更新只读表中的数据。但这时你要特别⼩⼼,因为它的危险性要⽐静态表⼤得多。第⼀它的表结构变更会更频繁,⽽且它的变更完全不受你控制。第⼆业务数据不像静态表,它是经常更新的,这样对数据同步的要求就⽐较⾼。要根据具体的业务需求来决定多⼤的延迟是可以接受的。

另外它还有两个问题:

1. 数据的容量:数据库中的数据量是影响性能的主要因素。因为这个数据是外来的,不利于掌握它的流量规律,很难进⾏容量规划,也不能更好地进⾏性能调优。

2. ** 接⼝外泄**:微服务之间的接⼝本来只有服务调⽤接⼝,这时你可以对内部程序和数据库做任何更改,⽽不影响其他服务。现在数据库表结构也变成了接⼝的⼀部分。接⼝⼀旦发布之后,基本是不能更改的,这⼤⼤了你的灵活性。幸运的是因为另外建了⼀套表,有了⼀个缓冲,当主表修改时,从表也许不需要同步更新。除⾮你能⽤服务调⽤(没有本地只读数据库)的⽅式完成所有功能,不然不管你是⽤RPC⽅式还是事件驱动⽅式进⾏微服务集成,上⾯提到的问题都是不可避免的。但是你可以通过合理规划数据库更改,来减少上⾯问题带来的影响,下⾯将会详细讲解。读写业务数据访问:

这是最复杂的⼀种情况。⼀般情况下,你有⼀个表是主表,⽽其他表是从表。主表包含主要信息,⽽且这些主要信息被复制到从表,但微服务会有额外字段需要写⼊从表。这样本地微服务对从表就既有读也有写的操作。⽽且主表和从表有⼀个先后次序的关系。从表的主键来源于主表,因此⼀定先有主表,再有从表。

上图是例⼦。假设我们有两个与电影有关的微服务,⼀个是电影论坛,⽤户可以发表对电影的评论。另⼀个是电影商店。“movie”是共享表,左边的⼀个是电影论坛库,它的“movie”表是主表。右边的是电影商店库,它的“movie”表是从表。它们共享“id”字段(主键)。主表是数据的主要来源,但从表⾥的“quantity”和“price”字段主表⾥⾯没有。主表插⼊数据后,发消息,从表接到消息,插⼊⼀条数据到本

地“movie”表。并且从表还会修改表⾥的“quantity”和“price”字段。在这种情况下,要给每⼀个字段分配⼀个唯⼀源头(微服务),只有源头才有权利主动更改字段,其他微服务只能被动更改(接收源头发出的更改消息之后再改)。在本例⼦中, “quantity”和“price”字段的源头是右边的表,其他的字段的源头都是左边的表。本例⼦中“quantity”和“price”只在从表中存在,因此数据写⼊是单向的,⽅向是主表到从表。如果主表也需要这些字段,那么它们还要被回写,那数据写⼊就变成双向的。直接访问其它数据库:

这种⽅式是要绝对禁⽌的。⽣产环境中的许多程序错误和性能问题都是由这种⽅式产⽣的。上⾯的三种⽅式由于是另外新建了本地只读数据库表,产⽣了数据库的物理隔离,这样⼀个数据库的性能问题不会影响到另⼀个。另外,当主库中的表结构更改时,你可以暂时保持从库中的表不变,这样程序还可以运⾏。如果直接访问别⼈的库,主库⼀修改,别的微服务程序马上就会报错。

向后兼容的数据库更新:

从上⾯的论述可以看出,数据库表结构的修改是⼀个影响范围很⼴的事情。在微服务架构中,共享的表在别的服务中也会有⼀个只读的拷贝。现在当你要更改表结构时,还需要考虑到对别的微服务的影响。当在单体(Monolithic)架构中,为了保证程序部署能够回滚,数据库的更新是向后兼容的。需要兼容性的另⼀个原因是⽀持蓝绿发布(Blue-Green Deployment)。在这种部署⽅式中,你同时拥有新旧版本的代码,由负载均衡来决定每⼀个请求指向那个版本。它们可以共享⼀个数据库(这就要求数据库是向后兼容的),也可以使⽤不同的数据。数据库的更新简单来讲有以下⼏种类型:

增加表或字段:如果字段可取空值,这个操作是向后兼容的。如果是⾮空值就要插⼊⼀个缺省值。删除表或字段:可先暂时保留被删除表或字段,经过⼏个版本之后再删除。

修改字段名:新增加⼀个字段,把数据从旧字段拷贝到新字段,⽤数据库触发器(或程序)同步旧字段和新字段(供过渡时期使⽤)。 然后再在⼏个版本之后把原来的字段删除。

修改表名:如果数据库⽀持可更新视图,最简单的办法是先修改表的名字,然后创建⼀个可更新视图指向原来的表。如果数据库不⽀持可更新视图,使⽤的⽅法与修改字段名相似,需要创建新的表并做数据同步。修改字段类型:与修改字段名⼏乎相同,只是在拷贝数据时,需要做数据类型转换。

向后兼容的数据库更新的好处是,当程序部署出现问题时,如需进⾏回滚。只要回滚程序就⾏了,⽽不必回滚数据库。回滚时⼀般只回滚⼀个版本。凡是需要删除的表或字段在本次部署时都不做修改,等到⼀个或⼏个版本之后,确认没有问题了再删除。它的另⼀个好处就是不会对其他微服务中的共享表产⽣⽴刻的直接影响。当本微服务升级后,其他微服务可以评估这些数据库更新带来的影响再决定是否需要做相应的程序或数据库修改。

跨服务事物:

微服务的⼀个难点是如何实现跨服务的事物⽀持。两阶段提交(Two-Phase Commit)已被证明性能上不能满⾜需求,现在基本上没有⼈⽤。被⼀致认可的⽅法叫Saga。它的原理是为事物中的每个操作写⼀个补偿操作(Compensating Transaction),然后在回滚阶段挨个执⾏每⼀个补偿操作。⽰例如下图,在⼀个事物有3个操作T1,T2,T3。每⼀个操作要定义⼀个补偿操作,C1,C2,C3。事物执⾏时是按照正向顺序先执⾏T1,当回滚时是按照反向顺序先执⾏C3。 事物中的每⼀个操作(正向操作和补偿操作)都被包装成⼀个命令

(Command),Saga执⾏协调器(Saga Execution Coordinator (SEC))负责执⾏所有命令。在执⾏之前,所有的命令都会按顺序被存⼊⽇志中,然后Saga执⾏协调器从⽇志中取出命令,依次执⾏。当某个执⾏出现错误时,这个错误也被写⼊⽇志,并且所有正在执⾏的命令被停⽌,开始回滚操作。

Saga放松了对⼀致性(Consistency)的要求,它能保证的是最终⼀致性(Eventual Consistency),因此在事物执⾏过程中数据是不⼀致的,并且这种不⼀致会被别的进程看到。在⽣活中,⼤多数情况下,我们对⼀致性的要求并没有那么⾼,短暂的不⼀致性是可以接收的。例如银⾏的转账操作,它们在执⾏过程中都不是在⼀个数据库事物⾥执⾏的,⽽是⽤记账的⽅式分成两个动作来执⾏,保证的也是最终⼀致性。

Saga的原理看起来很简单,但要想正确的实施还是有⼀定难度的。它的核⼼问题在于对错误的处理,要把它完全讲明⽩需要另写⼀遍⽂章,我现在只讲⼀下要点。⽹络环境是不可靠的,正在执⾏的命令可能很长时间都没有返回结果,这时,第⼀,你要设定⼀个超时。第⼆,因为你不知道没有返回值的原因是,已经完成了命令但⽹络出了问题,还是没完成就牺牲了,因此不知道是否要执⾏补偿操作。这时正确的做法是重试原命令,直到得到完成确认,然后再执⾏补偿操作。但这对命令有⼀个要求,那就是这个操作必须是幂等的(Idempotent),也就是说它可以执⾏多次,但最终结果还是⼀样的。

另外,有些操作的补偿操作⽐较容易⽣成,例如付款操作,你只要把钱款退回就可以了。但有些操作,像发邮件,完成之后就没有办法回到之前的状态了,这时就只能再发⼀个邮件更正以前的信息。因此补偿操作不⼀定⾮要返回到原来的状态,⽽是抵消掉原来操作产⽣的效果。

微服务的拆分:

我们原来的程序⼤多数都是单体程序,但现在要把它拆分成微服务,应该怎样做才能降低对现有应⽤的影响呢?

我们⽤上⾯的图来做例⼦。它共有两个程序,⼀个是“Styling app”,另⼀个是“Warehouse app”,它们共享图中下⾯的数据库,库⾥有三张表,“core client”,“core sku”,“core item”。

假设我们要拆分出来⼀个微服务叫“client-service”,它需要访问“core client”表。第⼀步,我们先把程序从原来的代码⾥拆分出来,变成⼀个服务. 数据库不动,这个服务仍然指向原来的数据库。其他程序不再直接访问这个服务管理的表,⽽是通过服务调⽤或另建共享表来获取数据。

第⼆步,再把服务的数据库表拆分出来,这时微服务就拥有它⾃⼰的数据库了,⽽不再需要原来的共享数据库了。这时就成了⼀个真正意义上的的微服务。

上⾯只讲了拆分⼀个微服务,如果有多个需要拆分,则需⼀个⼀个按照上⾯讲的⽅法依次进⾏。

另外,Martin Fowler在他的⽂章⾥有⼀个很好的建议。那就是,当你把服务从单体程序⾥拆分时,不要只想着把代码拆分出来。因为现在的需求可能已经跟原来有所不同,原先的设计可能也不太适⽤了。⽽且,技术也已更新,代码也要作相应的改造。更好的办法是重写原来的功能(⽽不是重写原来的代码),把重点放在拆分业务功能上,⽽不是拆分代码上,⽤新的设计和技术来实现这个业务功能。

结论:

数据库设计是微服务设计的⼀个关键点,基本原则是每个微服务都有⾃⼰单独的数据库,⽽且只有微服务本⾝可以访问这个数据库。微服务之间的数据共享可以通过服务调⽤,或者主、从表的⽅式实现。在共享数据时,要找到合适的同步⽅式。在微服务架构中,数据库的修改影响⼴泛,需要保证这种修改是向后兼容的。实现跨服务事物的标准⽅法是Saga。当把单体程序拆分成微服务时,可以分步进⾏,以减少对现有程序的影响。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.com 版权所有 湘ICP备2023021991号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务