混合华为云专项总结

Posted by jintang on 2022-01-25

一、背景

今天给大家总结一下我负责的华为云专项中的内容,很多人不知道为什么会有华为云专线,我简单介绍一下背景,根据集团的国企赛道战略导向,未来的主要客群会是国企客户,国企客户对云的选择会偏向华为云或国资云,那么集团与华为云达成了战略合作,而咱们云链为了切入到国企赛道就必须兼容华为云,也就是说,我们需要把咱们的SAAS服务同时运行在阿里云和华为云上。

刚接触华为云专项时,第一感觉就是无非就是把代码部署到华为云的机器上,跟私有化类似的。但细细想想并不难么简单,主要有以下几个区别:

  1. 场景不同,私有化一般是单租户的,不管是用户量和流量都是单一租户的,所以性能要求不高,数据库简单用rds就能包的住。但是华为云是需要承载标准saas的服务,未来会有大量的租户在华为云上,所以需要在各项云产品的功能齐全、性能、易用性等都需要对标阿里云,比如华为云的高斯DB对标阿里云的polarDB
  2. 入口不同,私有化租户可以通过打私有包进行使用,但是华为云作为承载SAAS服务,入口只能同一个标准包,毕竟不能要求华为云的客户下载一个华为云的APP

针对以上区别,需要对华为云对标阿里云的云产品进行方位的评估,以确保SAAS服务能够稳定运行在华为云上,另外从自身业务考虑,需要进行哪些兼容改造,才能同时支持阿里云和华为云。

难就难在了,涉及的面很广:兼容不同云的开发场景、服务的部署发布、sql发布、运行时管理中的监控和告警、日志服务、离线业务、大数据清洗、dmp报表等等,涉及的团队和系统很多:包括云链内部的各个组,对外协同华为云团队给我们做支持,兑现一些没有的功能等。

提示:未来要上华为云的小伙伴,重点看二-1,三-1,四和五章节。

二、多云兼容解决方案

云链SAAS服务进行混合云,我们以租户维度来划分不同的云,也就是一部分租户在华为云,一部分租户在阿里云。从运维的角度,为了保证不同云流量的独立性,我们不同的云采用各自的独立域名,从开发的角度,一套代码兼容两个云,就需要一个云标识来区分,从客户的角度,不管租户在哪个云上对于客户来说应该是无感知的。所以我拉通了云链各组共同确定了以下”多云兼容解决方案”,同时统一对齐各组的在此专项中的工作。

duoyun.png

从图中可以看到几个核心信息:

  1. 因为APP入口只有一个,所以app需要先内置好多个云的整套域名地址,并且根据租户的云标识来切换域名配置;
  2. 不同的云通过不同的域名来把流量分开,服务在各自的云上实现自闭环,也就是说阿里云的租户,直接使用阿里云的一整套saas服务,华为云亦如此;
  3. 为了提高兼容能力,只要把配置库中的部分表和通行证数据库进行双向同步,这样一来不管是代码和数据在各个云都是一份完整的备份,可以做到在用户不管往那个云上登录都可以。另外,这样的设计也具备一定的跨云灾备能力,一个云上的基础服务挂了,只要更改基础服务的地址配置就能快速恢复。
  4. 根据第三点的设定,对于对象云存储来说,应该也是使用各自云自身的云储存产品,如阿里的oss,对应华为云就是obs。考虑到目前ifile组件和app底座均未兼容华为云的obs,而未来租户需要从阿里云迁移至华为云上,对象云存储上的文件需要迁移是一个难题,一是完整的地址是写入到了数据库中,二是没有成熟的迁移工具。(架构组有此工具的开发计划,可以后序关注),所以结论是把阿里云的oss看作是第三方的云存储服务,华为云上也继续使用阿里云的oss;
  5. 华为云与阿里云在大数据云产品存在一定的差异,比如华为云上没有高性能分析数据库(ADB),无法在华为云上搭建对标阿里云的大数据服务。所以大数据组提出引用天际数芯产品,利用数芯的能力技能满足大数据服务需求,也能提升下云能力,符合集团的战略定位。

多云兼容解决方案是结合各组各子系统兼容华为云的总体方案指导,下面是实现兼容的更多的细节。

1、配置库的同步及云标识

上面多云解决方案核心信息第3点提到,通信证是可以支持任一云登录,实现这一个功能是需要配置库的部分表进行双向同步,目前正在同步的有以下11个表:

mycommunity_config:

  • application
  • application_product_mapping
  • area
  • authorization
  • contract
  • function
  • ggcp_tenant_code_mapping
  • m_gray_release
  • mapping_area_rds
  • tenant
  • tenant_platform_map

每一个表同步的意义这里不一一说明,有一些表是为了未来迁移租户方便,比如m_gray_release灰度表,如果某一个租户在阿里云上具备灰度功能,迁移到华为云之后,灰度保证不丢失。值得关注的是tenant表,双向同步说明不管在哪个云都是完整的备份,哪个都认识所有的租户,那么问题来了:既然两边云的配置库中tenant表都一样,有如何区分哪些租户运行在华为云上,哪些在阿里云上?答案是”云标识”。

在配置库的rds表中,新增了rds_provider字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE TABLE `rds` (
`id` char(36) NOT NULL,
`label` varchar(32) DEFAULT NULL COMMENT '标签',
`name` varchar(50) DEFAULT NULL,
`host` varchar(500) DEFAULT NULL COMMENT '主数据库地址',
`is_master` tinyint(1) DEFAULT '0' COMMENT '是否主库',
`instance_id` varchar(100) DEFAULT NULL COMMENT '主RDS实例ID',
`account` varchar(500) DEFAULT NULL,
`pwd` varchar(500) DEFAULT NULL,
`port` int(11) DEFAULT NULL,
`readonly_host` varchar(500) DEFAULT NULL COMMENT '只读数据库地址',
`reader` varchar(500) DEFAULT NULL COMMENT '只读账号',
`is_dw` tinyint(8) DEFAULT '0' COMMENT '是否是DW库',
`is_show` tinyint(4) DEFAULT '0' COMMENT '是否显示在创建租户界面上',
`slave_host` varchar(500) NOT NULL DEFAULT '' COMMENT '从库rds的host信息',
`open_host` varchar(500) DEFAULT '' COMMENT '外网host信息',
`created_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
`created_by` char(36) DEFAULT NULL COMMENT '记录创建者Id',
`modified_on` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录修改时间',
`modified_by` char(36) DEFAULT NULL COMMENT '记录修改者Id',
`out_host` varchar(500) DEFAULT NULL COMMENT '主数据库外网地址',
`db_type` varchar(20) NOT NULL DEFAULT 'rds' COMMENT '数据库类型,目前rds和polardb',
`rds_provider` varchar(50) NOT NULL DEFAULT 'aliyun' COMMENT 'rds 供应商',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2、超级APP的改造

混合阿里云与华为云,不同租户运行在不同的云上,两个云使用的域名不同,但是咱们超级APP入口只能是应用市场上的标准版,为了让用户无感知,不会提供两个不同云版本的APP,所以需要超级APP根据用户所登录的租户来决定流量走哪个云的域名。

下面给出架构组 @陈烁 和 @张蓉 给出的改造方案与实现

改造前:

0e19e00acda87d54ed12a81e0d0db28c.png

改造后:

b698b31b1b8d804dd70f6b86a92c4329.png

概括来说,超级APP需要预先内置好多套不同云的域名集合配置,app启动时默认使用阿里云的域名集合,当用户登录选择租户或者是切换租户时,先获取该租户的”云标识”,然后根据”云标识”来更新window下的环境变量,达到切换域名配置的目的。

超级APP作为前端底座,实现多云域名动态变更之后,window下的环境配置会更新,业务组需要在请求接口的时候动态的获取当前域名,具体可以参考下面 @王伟 在移动质检的实现方案:

proxy.png

获取 url 地址时,使用代理(Proxy)的方式,动态获取当前配置的域名,和对应接口的路径拼接,得到正确的 url

proxy获取全局域名配置

3、Passport的改造

上面第二点解决了APP端单一入口的多云切换问题,但是对于通过浏览器访问的PC端来说,域名也是两套。

考虑到pc后台地址一般都存在于客户的收藏夹里,并且都是阿里云的pc后台地址,那么为了让客户无感知,我们实现了pc端自动跳转,也即是说无论你打开的是阿里云的移动质检pc后台还是华为云的,登录后都能自动根据该租户所属云标识来跳转到对应的域名上。

下面是公共产品组 @黄煜捷 针对passport跳转的时序图

passport.png

上面时序图可以很清晰的看出整个跳转过程,比较有特色的是,在云错误的场景下,访问云不一致时,会对用户提交的数据进行临时加密处理成凭证,一同携带至正确云上进行解密、校验和会话处理,最终实现自动跳转,简单高效,并且保证了安全性。

三、华为云高斯DB

咱们云链SAAS服务,后端都数据库的性能是有要求的,毕竟租户多,数据量也大,除了优化自身业务逻辑和慢SQL治理,咱们直接采用阿里云PolarDB高性能数据库,对比普通的RDS有6倍的性能提升。

那么华为云需要支撑云链SAAS服务,必然也需要对标PolarDB的高性能数据库,就是高斯DB

华为云的数据库专家专门为我们讲解过高斯DB的底层架构原理,经过对标评估,基本上也支持存算分离,主从架构等,还有比较多的优秀特性如:1写15读、秒级主备切换、分钟级弹性扩容、NDP算子下推等,同样的配置下,性能是要优于阿里的PolarDB。以上特性不是本文的重点,有兴趣可以去参考资料链接了解一下。

下面聊一下高斯DB使用过程中遇到的问题及解决方案

1. 处理的升级MySQL8.0的兼容问题

咱们云链使用阿里云PolarDB的MySQL版本是5.6,华为云的高斯DB是MySQL8.0,升级的版本跨度比较大,必然存在很多的用法上的兼容问题,在MySQL官方文档中有列出不同版本的不兼容修改,我总结层下面文档,可以下载附件来查看。

我结合官方不兼容修改文档对移动质检的代码进行评估和排查,实际遇到的问题以及实际与到的问题,总结下来目前只遇到下面两类兼容问题:

datetime类型字段与空字符串比较报错问题

我们业务表中基本约定必须带有update_timestamp、created_on和modified_on等datetime类型字段,另外也有不少也是datetime类型的字段,比如有以下表:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `q_config` (
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名称',
`value` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '值',
`description` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '描述',
`created_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
`created_by` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '记录创建者Id',
`modified_on` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录修改时间',
`modified_by` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '记录修改者Id',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='全局配置'

分别在MySQL5.6和MySQL8.0下面sql执行:

1
2
3
4
5
6
7
8
9
MySQL 5.6 > SELECT name, value FROM q_config WHERE created_on > '';
Empty set, 1 warning (0.01 sec)

mysql 5.6> show warnings \G
*************************** 1. row ***************************
Level: Warning
Code: 1292
Message: Incorrect datetime value: '' for column 'created_on' at row 1
1 row in set (0.39 sec)
1
2
MySQL 8.0 > select name, value from q_config where created_on > '';
ERROR 1525 (HY000): Incorrect DATETIME value: ''

可以看出来datetime类型字段与空字符串比较时:created_on > ‘’,5.6只会有一个warning,但是8.0会出现报错。

如果把空字符串改成 0000-00-00 00:00:00,则不会报错

1
2
MySQL 8.0 > select name, value from q_config where created_on > '0000-00-00 00:00:00';
Empty set (0.00 sec)

出现这个问题的原因是:将 datetime 值与常量字符串进行比较时,MySQL 首先尝试将字符串转换为 datetime,然后执行比较。在 8.0 ( 8.0.16 ) 之前,当转换失败时,MySQL 将 datetime 视为字符串执行比较。现在在这种情况下,如果将字符串转换为 datetime 失败,则比较失败并显示ER_WRONG_VALUE。这个兼容问题,MySQL官方有定义为Bug:Bug #96361
注意:DATE、TIME类型也是同样的问题

在SQL_MODE有两个值:NO_ZERO_IN_DATE,NO_ZERO_DATE。这个配置只能避免插入的时候数据的时候出现 0000-00-00 00:00:00 这样的0值,即使设置了该参数,上面的问题仍会报错。

另外说明一下,MySQL的配置中的SQL_MODE参数,不管在阿里云上和华云上,此参数我们都是采用默认为空值。

解决办法

因为PHP弱类型语言,类型不那么严谨,所以在与日期时间相关的业务场景,基本上很难避免查询SQL把datetime类型字段与空字符串比较,尤其是接口参数需要提供日期时间的参数,如果没有判空一路往下传就会到达repository层时直接作为sql的where条件值。

为了不让日期类型不与空字符串比较,最直接的做法就是在repository层的sql执行之前,对日期时间参数进行判空,这是非常容易的事情。当然你也可以controller层就对日期参数进行校验,避免下层传递也可以。

虽然判空很容易,但是历史代码都没有那么严谨,不好保证处处都有判空,需要整个系统所有代码都需要进行排查,工作量就非常的大。我在解决这个问题的时候,有两种思路:

  1. 从业务角度出发,考虑哪些业务与时间有强关联,比如进度管理,离线数据下载,这种都是与与时间关系强相关的业务,然后针对性地排查这部分业务的接口参数,在入口进行控制。这样相对不容易改出问题,但是容易遗漏。
  2. 直接在仓储层进行处理,排查代码中用到了所有日期类型的查询,然后在sql执行前进行判空,并且赋予初始值。这种做法工作量大,但是能够覆盖全。

实际解决这个问题我是按第二种思路。下面详细介绍我的解决过程:

第一步:首先需要找到所有的datetime、date类型字段

以通过下面sql,从系统表中查询所有日期类型的字段:

1
select table_name, data_type, group_concat(distinct column_name) as columns from information_schema.COLUMNS where TABLE_SCHEMA = 'mycommunity_zjfunctest' and TABLE_NAME like "q_%" and DATA_TYPE IN ('datetime', 'date') and COLUMN_NAME not in ('update_timestamp', 'created_on', 'modified_on') group by table_name;

备注:mycommunity_zjfunctest是质检的测试库名称,”q_”表前缀是质检的表前缀。update_timestamp、created_on和modified_on是我已知的字段类型,所以我排除掉。

第二步:基于第一步的字段结果,寻找规律,并且编写查找代码的正则表达式

比如移动质检会有如下日期类型字段(只列出部分):

start_time、wash_start_date、wash_end_date、remind_datetime、start_on、finish_on、deadline

我们能发现,其实在命名上是可以找到后缀规律的,如_time,_date和_on,有一些是直接一个单词,没有后缀。

考虑yii2框架,通常操作数据库有两种方式:

  1. 直接拼接sql,然后$db->createdCommend($sql, [])->query()
  2. 使用query查询器提供的方法,如$query->where(),$query->andWhere()等

实际的业务代码里,两种方式都有,代码写法不同,我们需要针对两种方式编写正则

第一种正则如下:

1
(\s|\.)+\b(((wash_start|wash_end|node_update|\w+))_(datetime|date|on|time|at|remark|timestamp|deadline)+|time|deadline|date|birthday)\b\s+(>|<|=|!=|bewteen)

第二种正则如下:

1
'(>|<|>=|<=)',\s+'((\w)+\.)?\b(((user_modified|recent_update|node_update|\w+))_(datetime|date|on|time|at|remark|timestamp|deadline)+|time|deadline|date|birthday)\b'

注意:上面的两个正则不能直接使用,需要根据自己的字段来进行修改和调整。 正则的目的是为了找到出现日期类型字段的查询操作。

第三步:基于正则查找代码的查找结果,根据业务逻辑适当地判空并赋予默认值

时间类型的0值格式为:0000-00-00 00:00:00

在此之前,我们先思考一下日期类型查询的不同值的意义,如查询条件格式为 start_time < birthday < end_time:

  1. ‘1995-01-01 00:00:00’ < birthday < ‘2021-01-01 00:00:00’:得到结果是 95年出生到21年的人
  2. ‘’ < birthday < ‘2021-01-01 00:00:00’:得到结果是 21年以前出生的人
  3. ‘0000-00-00 00:00:00’ < birthday < ‘2021-01-01 00:00:00’:得到结果是 21年以前出生的人
  4. birthday < ‘2021-01-01 00:00:00’: 得到结果是 21年以前出生的人
  5. ‘1995-01-01 00:00:00’ < birthday < ‘’:得到结果是 没有结果
  6. ‘1995-01-01 00:00:00’ < birthday < ‘0000-00-00 00:00:00’:得到结果是 没有结果
  7. ‘1995-01-01 00:00:00’ < birthday:得到结果是 95年后出生的人

上面的结果来看:

  • 对于start_time来说,2、3、4结果都是一样的,也就是说,判断start_time为空,直接赋予初始值:0000-00-00 00:00:00 进行查询,是不会影响业务逻辑的。
  • 对于end_time来说,空值、初始值与不给条件结果截然不同,判断end_time为空时,需要去掉条件,这样的修改才不会影响业务逻辑。

在兼容这个问题,需要考虑对业务的影响来做出正确改动。下面举个例子:

如图有这样的代码:

判空修改

参数中有一个$time变量,从查询的条件来看,因为是$time <= update_timestamp,如果$time变量为空,则重新赋值为 0000-00-00 00:00:00 是不应影响业务的。这样的改动是最小的,不需要管下面sql多复杂,只要增加一段判空和赋值的代码即可,比起去掉这个where条件要简单很多,但去掉where条件可能更加合理,我是按照最小改动去兼容的。

但是对于 update_timestamp <= $endTime,如果$endTime为空,则不能通过重新赋值 0000-00-00 00:00:00来查询,去掉条件才是合理的做法,可以通过下面两种2种方式:

1
2
3
4
5
6
7
// 直接判空
if (empty($endTime)) {
$query->andWhere(['<=', 'update_timestamp', $endTime]);
}

// 使用yii2的andFilterWhere,这个函数会自动判断值是否为空,为空则不添加条件。推荐!
$query->andFilterWhere(['<=', 'update_timestamp', $endTime]);

另外,$endTime为空时,默认重新赋值为当前时间,在某些场合下也是可以的,具体业务具体考虑。

union all语法问题

如果有这样的sql:

1
2
3
select id, name, value from q_config where name = 'aaa' limit 10
union all
select id, name, value from q_config where name = 'bbb' limit 10

在MySQL8.0执行会报语法错误:

1
2
3
4
5
MySQL 8.0 > select id, name, value from q_config where name = 'aaa' limit 10 \
-> union all \
-> select id, name, value from q_config where name = 'bbb';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'union all
select id, name, value from q_config where name = 'bbb'' at line 2

报错的原因是因为 union all 两边的子句中,带有limit关键字时,会报错。具体原因没有查明,可能mysql8.0的词法分析器修改了,不允许这样的sql。

解决的办法也很简单,只要在union all两边的子句中,添加括号括起来即可:

1
2
3
(select id, name, value from q_config where name = 'aaa' limit 10)
union all
(select id, name, value from q_config where name = 'bbb' limit 10)

具体可以业务代码搜索 union all 关键字,然后无脑地两边加上括号即可。

2. proxy层问题

什么是数据库的proxy层?就是在数据库实例之上做一层代理层,在此层可以做流量转发,词法分析等,实现读写分离、主备切换等功能。(这是我的理解,并非标准定义,大概是那个意思,哈哈哈)

阿里云polarDB要先于华为云的高斯DB好几年,在功能的完整性上,polarDB确实要有很大的优势,它提供的proxy层功能也是齐全的,高斯DB在实现proxy层产品上也是学习polarDB的。那么高斯DB的proxy有什么问题呢?目前主要有3个:

  1. 高斯DB只支持单proxy地址
  2. 高斯DB的proxy层读写分离功能,仅支持读写模式,不支持只读模式
  3. 读写分离的一直性问题

高斯DB只支持单proxy地址

先看下面的图,对比一下polarDB与高斯DB的proxy层就能大致了解

dbproxy.png

阿里云PolarDB的proxy层支持自定义多个proxy地址,每一个proxy能够代理多个节点,这样的特性可以轻松地划分不同组的使用,比如示意图所示:定义一个proxy地址A,自动实现读写分离到1个主节点做写操作和3个从节点负载做读操作,适合把proxy地址A给到业务组使用。另外定义一个proxy地址B,只代理两个从节点实现负载读操作,适合把proxy地址B分配给大数据组使用。这样的划分就非常的清晰。

高斯DB的proxy层读写分离功能,仅支持读写模式,不支持只读模式

proxy层有一个模式配置,分为读写模式和只读模式,意义很好理解,就是读写模式支持读写操作,只读模式只允许读操作,用于做读写的权限管控。这与代理层代理到了主节点或从节点是没有关系的,比如:

读写模式下代理一主一从: 此时读写流量都能进来,自动实现读写分离,写操作到主节点,读操作到从节点上

只读模式下代理一主已从: 此时只能有读流量进来,自动按照读权重进行读负载,一部分读操作在主节点,一部分读操作在从节点

duxie.png

在单proxy下,业务组与大数据组都将使用同一个proxy地址代理的1主多从,高斯DB是不支持只读模式的,那么在读写模式下,是没办法控制大数据组的写操作的。最终提供的解决方案是,通过分配给大数据组一个只读账号来限制写操作。

读写分离的一致性问题

说明会话一致性问题之前,先了解一个场景:

zucong.png

在一个”刚刚写入,马上要查询”的操作中,由于proxy实现的读写分离是往主节点写,往从库中读取,主从同步哪怕是在内网也存在这一定的延时,必然会有出现刚刚写入的数据马上查是查不到的情况。

针对以上问题,PolarDB的proxy是有三种一致性级别可选,用于满足不同的场景需要:最终一致性会话一致性全局一致性

最终一致性

PolarDB是读写分离的架构,传统的读写分离只提供最终一致性的保证,主从复制延迟会导致从不同节点查询到的结果不同,例如在一个会话内连续执行如下查询,最后的SELECT结果可能会不同(具体的访问结果由主从复制的延迟决定)。

1
2
3
INSERT INTO t1(id, price) VALUES(111, 96);
UPDATE t1 SET price = 100 WHERE id=111;
SELECT price FROM t1;

上面的sql语句,最终一致性级别下,会拆开两个语句,insert和update往主节点去做写操作,select语句往从节点去查询。

zhucong.png

但是如果把sql语句显式地放在一个事务里面,如这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
$db = Yii::$app->db;
$transaction = $db->beginTransaction();
try {
$db->createCommand("INSERT INTO t1(id, price) VALUES (111, 96)")->execute();
$db->createCommand("UPDATE t1 SET price = 100 WHERE id = 111")->execute();

$db->createCommand("SELECT price FROM t1")->query();

$transaction->commit();
} catch(\Exception $e) {
$transaction->rollBack();
// ...
}

不管读写操作的sql都往主节点上发,这样就能解决主从延时带来的问题

zyuzg.png

高斯DB只支持最终一致性的级别,貌似也没有什么问题,问题就在于我们的业务代码,基本都是默认使用autocommit来提交事务,很少有主动把单表的更新使用事务。

这样的显式事务,多见于多表同时更新时方便失败时回滚。所以如果要用上最终一致性来解决主从延时问题,就需要修改大量的代码,成本代价太大,甚至有些地方还不好改。

会话一致性

最终一致性是把一个事务的读写sql都往主节点上发,而会话一致性则不需要显式使用事务,而是Session LSN来解决,这里引用官方解释:

在PolarDB的链路中间层做读写分离的同时,中间层会追踪各个节点已经应用的Redo日志位点,即日志序号(Log Sequence Number,简称LSN)。同时每次数据更新时PolarDB会记录此次更新的位点为Session LSN。当有新请求到来时,PolarDB会比较Session LSN和当前各个节点的LSN,仅将请求发往LSN大于或等于Session LSN的节点,从而保证了会话一致性。表面上看该方案可能导致主节点压力大,但是因为PolarDB是物理复制,速度极快。

huihuayizhi.png

会话一致性可以不修改任何代码,就能满足我们的场景,而且是能够满足大部分场景,当然代价就是主节点会承担了更多的读压力。

全局一致性

一致性的级别越高,对主节点的压力就越大,所以全局一致性很少使用,会话一致性基本上已经满足大部分场景了,此处就不多解释了,具体可以在参考资料的链接中了解。

注意:因为目前高斯DB的proxy代理层只支持最终一致性级别,其他功能也并不完善,是没办法满足我们当前的需要,所以在530完成功能兑现之前,我们目前放弃使用高斯DB的proxy。

四、定时任务的兼容问题

从”多云解决方案”了解到,不同云在各云内是一整套完整的自闭环服务,所以定时任务也是两边云都有,而租户是不同云有不同的租户,所以定时任务只需要管自身云的租户即可。

所以混合华为云之后,定时任务就会出现兼容问题。简单来说就是,定时脚本在获取全租户code的时候,只要获取自己所在云的租户即可,否则会做对不是自己云的租户跑业务逻辑,会有很多无用功甚至报错。

兼容方法:就是通过环境变量获取云标识,然后结合rds表中的rds_provider字段来对租户code进行筛选

环境变量可以直接在星舟的应用配置上设置(阿里云和华为云上都需要配置):

星舟应用配置

在config_impl/config.php配置文件中添加:

1
2
3
4
5
6
7
8
9
10
11

[
// ...

'params' => [
'cloudProvider' => '@{{cloud/provider}}',
],

// ...
]

然后修改获取全租户code的方法:

getalltenant

查询tenant表的时候,增加rds表联查。当前tenant和rds数据量想都很小,增加一个联表,基本没什么影响。

五、SQL发布问题

阿里云和华为云都是支撑云链标准SAAS业务,我们在迭代更新发布的时候,需要两边云都发布,同样地数据库表结构和数据也需要保持一致。

为了sql发布方便,我们可以在SQL发布平台的标签管理中,增加标签来增加便捷性:

sql发布平台标签管理

对于租户库来说,sql发布直接选上两边的所有租户库即可。但是配置库有一点要注意的,与环境相关的配置,是需要分开发的,因为同样的配置在不同云上是不一样的,比如site表中的域名地址。

六、总结

本文为你介绍了我负责的混合华为云专项,包括多云兼容解决方案,各组很多需要改造的地方,还有当前高斯DB对于我们业务来说存在那些问题和解决方法,还有很多不是很重要但是很细的细节没有一一列出。希望你了解华为云专项,或即将计划上华为云,提供帮助,也希望对你有所启发。

截止至2022年1月17日,已上线了5家租户到华为云上运行。

肝文章不易,看官点个赞再走,比心~

七、参考资料