在 DSC 集群的客户端连接配置中,有这么一组服务名参数:EP_SELECTOR、AUTO_RECONNECT、CHECK_FREQ,EPSELECTOROPTION。本文将介绍微服务架构下 DSC 集群的访问特点,剖析默认轮询连接方式下跨节点缓存交换演变为性能瓶颈的机制,并通过测试实验说明,配置这几个参数把不同微服务“钉”在不同节点上,能有效消除跨节点远程读与全局协调开销,显著提升集群整体服务性能;最后介绍如何以最小改动在原驱动上补上“节点恢复后自动切回”的能力。
围绕“微服务 + DMDSC 集群”这一场景,本文针对DmJdbcDriver8-8.1.5.41驱动主要做了三件事:
V$DSC_REQUEST_STATISTIC、V$DSC_GLS_SYS、V$SESSIONS 等系统动态视图取阶段增量,看远程读(remote read)、跨节点 latch、排他自等待等指标。EP_SELECTOR 手动定向(每个服务一个服务名,把目标节点排在列表首位);其二,驱动层 epSelectorOption=1 + appName 按哈希自动分发(一套服务名、n 个 appName 自动打散),适合微服务数量多、扩容频繁的场景。一个业务应用下通常有 n 个微服务,它们由同一基础镜像复制生成,共用同一套 DSC 连接配置;服务之间的数据所有权边界清晰:比如订单表只被订单服务访问,账户表只被账户服务访问。n 个微服务之间唯一的差异是部署时注入的 appName——这带来一个性能优化方向:以 appName 为唯一差异点,把路由决策交给驱动,而应用代码无需任何改动。
DM 数据共享集群(DMDSC)是共享存储的多活集群:多个实例同时读写同一份数据文件,每个实例拥有独立的 CPU、内存和数据缓冲区。与双机热备不同,DMDSC 没有闲置的备用节点,所有实例都对外提供服务;客户端连接时建议使用服务名——服务名是集群所有对外实例“IP:端口”集合的名称,在客户端 dm_svc.conf 中配置。
两个节点的缓冲区都可能缓存同一个数据页,DM 用 GBS/LBS(全局/本地缓冲服务)维护全局一致性:GBS 上记录每个数据页的闩权限、访问节点位图(Access MAP)、最新副本所在节点(Fresh EP)与最新页 LSN。当一个节点要访问的页其最新副本在另一个节点时,该页通过 MAL 内部网络传递过来,即缓存交换(Buffer Swap),也就是常说的远程读(remote read)。它比“写回磁盘再读出”快,但远比本地命中昂贵:伴随跨节点 latch 申请、授权、回收(revoke)以及全局封锁协调。
因此 DMDSC 有一个固有成本:多个节点越频繁地访问、修改相同的数据页,缓存交换就越频繁,内部网络流量与协调开销越高,直至成为性能瓶颈。
服务名参数 EP_SELECTOR 默认值为 0,含义是连接轮询均摊到各节点。在微服务架构下,这意味着订单服务的连接同时落在两个节点上,账户服务亦然。于是同一批数据页被两个节点轮流读写,页所有权在节点间不断转移:
CPU 与内网带宽被协调开销消耗,而非业务逻辑——这与缓冲池分片上的 X 锁热点争用是同一类问题:争抢本身不产生任何业务价值。
如果把一个微服务的全部连接固定到一个节点(下称“钉节点”),该服务的工作集就稳定驻留在该节点缓冲区:读操作命中本地数据页,写操作的最新副本也始终在同一节点,页所有权几乎不再跨节点转移,跨节点 latch 与 revoke 减少,控制页争用随之减少。节点故障时连接自动切换到幸存节点,节点恢复后又能自动切回——容灾能力并不损失。
值得注意的是,性能提升的本质不是“钉”这个动作,而是钉住之后实现的数据局部性:把共享存储集群从“两个节点抢同一批页”变成“各管一摊”。《DM9 数据共享集群》手册第 18.2 节的建议与此完全一致:“根据业务逻辑,把不同类别的数据库请求分别部署到不同的节点,减少不同节点对相同数据页或者数据库对象的共享访问和修改。”
连接服务名在 dm_svc.conf 中配置,与本文相关的参数(手册第 14 章《巧用服务名》):
服务名配置示例(手动钉节点方式):每个微服务一个独立服务名,目标节点排在列表第一位 + EP_SELECTOR=1 实现钉节点,AUTO_RECONNECT=2 保留切换与切回能力;需要轮询均摊时,直接使用 EP_SELECTOR=0 即可。若采用后文介绍的 epSelectorOption + appName 自动分发,则一个服务名即可供整个应用的 n 个微服务共用,无需为每个微服务单独建服务名。
# 全局缺省
SWITCH_TIMES=(5)
SWITCH_INTERVAL=(1000)
# 微服务A:66 在列表首位,定向连接第一个节点
svc_a=(192.168.202.66:6636,192.168.202.77:6637)
[svc_a]
EP_SELECTOR=(1)
AUTO_RECONNECT=(2)
CHECK_FREQ=(60000)
# 微服务B:77 在列表首位,定向连接第一个节点
svc_b=(192.168.202.77:6637,192.168.202.66:6636)
[svc_b]
EP_SELECTOR=(1)
AUTO_RECONNECT=(2)
CHECK_FREQ=(60000)
# 对照组:轮询均摊,代表“不分区”的默认状态
svc_rr=(192.168.202.66:6636,192.168.202.77:6637)
[svc_rr]
EP_SELECTOR=(0)
把目标节点放在列表首位、统一使用 EP_SELECTOR=1 更直观,也避开配置值超过节点数时按模取数的行为。dm_svc.conf 修改后需重启客户端进程生效。
用 JMeter 模拟一个应用下的两个微服务。真实场景中,一个应用通常包含 n 个微服务,n 个微服务对应 n 个 appName,整个应用共用一套 DSC 连接配置,仅以 appName 区分服务。模拟实验与真实架构的对应关系如下:
| 真实微服务架构 | 实验中的对应 |
|---|---|
| 整个应用共用一套 DSC 连接配置,仅以 appName 区分 | 每个线程组使用自己的服务名(svc_a / svc_b)显式钉节点,与 appName 钉节点在连接层等价 |
| 微服务的数据所有权边界(库表私有、互不访问) | A 表只被 A 线程组访问、B 表只被 B 线程组访问,严格不交叉 |
| 路由与容灾在驱动层统一实现、应用无感知 | EP_SELECTOR / AUTO_RECONNECT / CHECK_FREQ 均在驱动层生效 |
| 真实事务混合(读多写少) | 50% 点查 + 20% 范围 + 20% 更新 + 10% 插入 |
需要说明,JMeter 模拟的是微服务的流量特征而非服务治理本身;实验中把“每个 appName 钉一个节点”显式化为两个服务名,与真实场景“一套 dsc + n 个 appName”的自动分发在连接层等价,后文“进一步演进”一节会直接用同一服务名 + appName 验证。
环境为两节点 DMDSC(192.168.202.66 / 192.168.202.77),JMeter 独立部署,100 并发(两服务各 50),每阶段 5 分钟,除连接方式外其他条件完全一致。阶段 1 两服务均用 svc_rr(轮询,实测会话分布 52/53);阶段 2 分别用 svc_a / svc_b(钉节点,用 SELECT NAME FROM V$INSTANCE 验证连接正确)。
观测分两个层面。客户端用 JMeter 聚合报告统计 TPS 与延迟百分位(Average、90/95/99% Line);服务端分别登录两个节点,对以下动态视图采集阶段前后的累计值做差,得到该阶段的增量。各视图与指标的含义如下:
| 视图 | 指标(TYPE / 列) | 含义 |
|---|---|---|
| V$DSC_REQUEST_STATISTIC | lbs_stat_request_fast | 本地缓冲区快路径命中次数:页就在本节点、无需全局协调,即“有效本地读” |
| V$DSC_REQUEST_STATISTIC | lbs_stat_vio_remote_no_rlog_flush、lbs_stat_vio_remote_rlog_flush | 远程页传递次数,即远程读(remote read):本地缓冲区所需页的最新副本在对端节点,需经内网把页传过来——本文核心观测指标 |
| V$DSC_REQUEST_STATISTIC | lbs_stat_request_gbs_remote_x / _s | 发往对端节点的排他/共享 latch 请求次数:跨节点协调的频率 |
| V$DSC_REQUEST_STATISTIC | lbs_stat_request_gbs_local_x / _s | 本地 latch 请求次数,与跨节点请求对照 |
| V$DSC_REQUEST_STATISTIC | lbs_stat_request_self_wait_x | 排他自等待次数:线程排队等待“正在跨节点传输或被对端持有”的页的程度 |
| V$DSC_REQUEST_STATISTIC | lbs_stat_page_flush | 数据页写回次数 |
| V$DSC_GLS_SYS | TOTAL_SUCCESS / TOTAL_WAIT / TOTAL_DEADLOCK | 全局封锁成功 / 冲突等待 / 死锁次数:跨节点锁冲突水平 |
| V$SESSIONS | COUNT(*) | 登录节点的会话数:验证连接分布(轮询应对半,钉节点应按线程组收敛) |
| V$DSC_EP_INFO | EP_MODE / EP_STATUS | 节点角色(控制节点/普通节点)与状态:确认集群健康 |
使用提醒:V$DSC_REQUEST_STATISTIC 只反映当前登录节点,必须分别登录各节点采样。
微观层面(查询动态视图指标):
分别登录两个节点,执行查询命令,得到多个如下结果:
SELECT TYPE, TOTAL_REQUEST_COUNT, AVERAGE_REQUEST_TIME
FROM V$DSC_REQUEST_STATISTIC
ORDER BY TYPE;
行号 TYPE TOTAL_REQUEST_COUNT AVERAGE_REQUEST_TIME
---------- ----------------------------- -------------------- --------------------
1 lbs_stat_page_flush 83118 38351
2 lbs_stat_request_fast 1895022057 0
3 lbs_stat_request_gbs_local_f 0 0
4 lbs_stat_request_gbs_local_s 27619 9170
5 lbs_stat_request_gbs_local_x 37644 9206
6 lbs_stat_request_gbs_remote_f 0 0
7 lbs_stat_request_gbs_remote_s 25386 15377
8 lbs_stat_request_gbs_remote_x 36727 14624
9 lbs_stat_request_owned_f 0 0
10 lbs_stat_request_owned_s 6729 797
11 lbs_stat_request_owned_x 485 1258
行号 TYPE TOTAL_REQUEST_COUNT AVERAGE_REQUEST_TIME
---------- --------------------------------- -------------------- --------------------
12 lbs_stat_request_self_wait_f 0 0
13 lbs_stat_request_self_wait_s 6617 23230
14 lbs_stat_request_self_wait_x 15658 71132
15 lbs_stat_type_count 0 0
16 lbs_stat_vio_local 280 128
17 lbs_stat_vio_none 107019411 0
18 lbs_stat_vio_remote_no_rlog_flush 25286 0
19 lbs_stat_vio_remote_rlog_flush 146 0
20 lls_stat_dict_remove 336 3930
21 lls_stat_lock_release 0 0
22 lls_stat_lock_table 1912 4677
行号 TYPE TOTAL_REQUEST_COUNT AVERAGE_REQUEST_TIME
---------- ----------------- -------------------- --------------------
23 lls_stat_lock_tid 29 3575
23 rows got
已用时间: 650.400(毫秒). 执行号:631.
对阶段一、阶段二前后两次采样的累计值做差,得到压测期间各指标的增量,整理成下表:
| 指标 | 轮询 66节点/77节点 | 钉节点 66节点/77节点 | 变化 |
|---|---|---|---|
| 远程页传递(remote read) | 19,949 / 20,407 | 618 / 550 | -97% |
| 跨节点 latch 请求 | 19,939 / 23,164 | 3,144 / 3,654 | -84% |
| 排他自等待(self_wait_x) | 17,945 / 16,999 | 6 / 1 | ≈-100% |
| 本地快路径命中 | 11.95 亿 / 11.30 亿 | 17.69 亿 / 18.48 亿 | +48% / +64% |
| 页写回 | 26,063 / 25,512 | 15,948 / 16,342 | -37% |
宏观层面(聚合报告):
通过 JMeter 保存压测聚合报告
轮询均摊结果:
钉节点结果:
| 指标 | 轮询 | 钉节点 | 变化 |
|---|---|---|---|
| 总 TPS | 939.7 | 1553.1 | +65.3% |
| 平均响应时间 | 95 ms | 57 ms | -40% |
| P90 / P95 / P99 | 257 / 381 / 735 ms | 100 / 271 / 682 ms | -61% / -29% / -7% |
从上表可以得出以下结论:
宏观指标分析:
吞吐量:钉节点模式下各操作类型的 TPS 均高于轮询模式,总吞吐提升 65.3%——同样的并发与时间,系统完成了更多有效业务。
平均响应时间:钉节点较轮询降低 40%。分操作看,点查降约 40%,更新降约 47%(P95 从 252 ms 降到 99 ms),插入降 84%(371 ms → 57 ms)。插入改善最显著的原因正是前文剖析的控制页跨节点回收授权在钉节点后几乎消失。
尾部延迟:P90 下降 61%,说明钉节点在压力下能更稳定地维持服务质量、减少长尾请求——轮询模式下线程大量时间花在跨节点等待上,正是长尾的主要来源。
微观指标分析:
远程页传递(remote read):从每节点约 2 万次降到约 600 次,降幅 97%。轮询模式下数据页的最新副本在两个节点间来回转移,每一次跨节点取页都计为一次远程读;钉节点后各服务的工作集稳定驻留在本节点缓冲区,远程读自然消失。残余的数百次可能来自系统目录、序列等共享全局对象。
跨节点 latch 请求:下降 84%。每一次远程读都伴随发往对端节点的排他/共享 latch 请求(gbs_remote_x/s),还可能触发对端回收(revoke);页所有权不再跨节点转移后,这部分协调同步减少。
排他自等待(self_wait_x):从 1.7 万/1.7 万降到个位数,约 -100%。该指标度量线程排队等待“正在跨节点传输或被对端持有”的页,是轮询模式下延迟的主要来源,钉节点后基本消失。
本地快路径命中:+48% / +64%。同样 5 分钟、同样 100 并发,完成的本地读次数多了近一半——原本消耗在跨节点协调上的时间被转化为有效业务处理,这是总 TPS 提升 65.3% 的直接来源。
页写回:下降 37%。写入收敛到单节点后,脏页不再因对端索取而跨节点传递后再写回,写回压力随之降低。
由此可见,在“一个应用 n 个微服务、n 个微服务对应 n 个 appName、共用一套 DSC”的架构下,按 appName 把不同微服务的连接固定到不同 DSC 节点(小规模场景也可用 EP_SELECTOR 手动排序、不同服务名实现),能大幅减少远程页传递与跨节点协调开销,提升业务性能。
钉节点配置把连接固定到指定节点,生产上还要回答一个问题:指定节点故障时,连接是否自动迁到幸存节点?故障节点恢复后,又是否自动切回原节点?服务名参数中,前者由 EP_SELECTOR“仅当当前节点不可用时才顺延下一个”与 SWITCH_TIMES/SWITCH_INTERVAL 保证;后者由 AUTO_RECONNECT=2(配合 EP_SELECTOR≥1)控制,切回检测周期为 CHECK_FREQ。我们用一个小实验对两者做了验证。
每个服务 1 个探针线程,每 10 秒执行一次 SELECT NAME FROM V$INSTANCE 并把“时间戳+实例名”追加写入时间线文件,从而完整记录连接的落地节点变化。服务名配置为钉节点模式(svc_a 钉 66、svc_b 钉 77),AUTO_RECONNECT=(2)、CHECK_FREQ=(60000)、SWITCH_TIMES=(5)、SWITCH_INTERVAL=(1000)。集群已配置故障自动拉起。时间线:先稳态运行,随后在节点 77 上以 kill -9 结束 DMSERVER 进程模拟崩溃,不人工干预,等待 DMCSS 心跳判定、自动拉起、崩溃恢复并重加入集群,直至测试结束。
**踩坑点:**第一次测试采用较大的取值 SWITCH_TIMES=(99),现象很有迷惑性:kill 之后 B 探针直接“沉默”了两分钟,既没有切到 DMDSC01,也没有报错,等 77 被集群自动拉起后,B 又像什么都没发生一样在 DMDSC02 上恢复——看起来就像“故障切换不生效”。
原因是:SWITCH_TIMES 和 SWITCH_INTERVAL 构成客户端在故障期间的重试预算;而集群侧“心跳判定 + 自动拉起 + 崩溃恢复”这条链路恰好在重试时间内。客户端对着已死节点反复重试,等重试预算耗尽、终于准备切换时,故障节点已经被集群拉起来了,于是连接直接在原节点重建——“切到幸存节点”就不会发生了。
将 SWITCH_TIMES 取值为 5 再做一次实验:
该实验结果总结如下:
| 时间 | B-WHOAMI 返回 | 含义 |
|---|---|---|
| 11:18:30 ~ 11:19:40 | DMDSC02 | 稳态,连接如预期钉在 77 |
| 11:20:07 | Connection has been switched | 故障检出,驱动完成连接切换 |
| 11:20:17 ~ 11:22:07 | DMDSC01 | 整个宕机期运行在幸存节点 |
| 11:22:17 起 | DMDSC02 | 77 自动拉起重加入后,一个 CHECK_FREQ 周期内自动切回 |
与此同时,钉在 66 上的 A 探针全程保持 DMDSC01,不受 77 故障影响。至此,“故障自动切换、恢复自动切回”在钉节点配置下完整成立,钉节点并不牺牲可用性。
在参数取值上,当生产环境有上层应用、且应用具备重试能力时,可配置 AUTO_RECONNECT=3(即 1 与 2 的组合):连接异常时自动切换到其他节点,且无论切换成功还是失败都会抛出一个 SQLException,通知上层应用对失败事务做相应处理;同时,若服务名列表中靠前的节点恢复,连接也会自动切回该节点。
前文介绍了 EP_SELECTOR 等服务名参数:当不同微服务访问的数据相互独立时,可以为每个微服务指定优先节点,获得钉节点带来的数据局部性。但 EP_SELECTOR 方式对应用不透明——需要手动指定每个服务名的节点顺序,扩容节点、新增微服务都要改配置,实施负担大。为此,驱动层提供了更自动化的连接分发方式:连接串新增配置项 epSelectorOption,配合 appName 使用。epSelectorOption=1 时,驱动按 appName 的值计算哈希,优先连接第(哈希值 % 服务名中 IP 个数)+ 1 个节点;相同 appName 的微服务稳定聚合到同一实例,不同 appName 自动打散,无需手动排序。真实场景中,一个应用共用一套服务名配置,n 个微服务只需在连接串上使用不同的 appName。以两节点集群为例,两个微服务的连接串如下:
jdbc:dm://dscT?dscT=(192.168.202.66:6636,192.168.202.77:6637)&epSelectorOption=1&appName=order_svc
jdbc:dm://dscT?dscT=(192.168.202.66:6636,192.168.202.77:6637)&epSelectorOption=1&appName=account_svc
我们沿用探针测例验证这种 URL 写法:两个探针线程各使用一条上述连接串,仅 appName 不同,每 10 秒采样一次“我在哪个节点”,持续 3 分钟。
结果 order_svc 的连接全部且稳定地落在 DMDSC02,account_svc 全部落在 DMDSC01,两者互不交叉——appName 哈希分发成立,效果与 EP_SELECTOR 手动钉节点等价。
继续压测并 kill 掉一个节点:存量连接自动转移到幸存节点;集群服务恢复后重新建立连接,又按 appName 分发回两个节点。
以上测试也暴露了剩下一个问题:该驱动版本下,采用 appName 方式连接,故障节点恢复后存量连接不会自动切回——新连接会按 appName 重新分发,但已落在幸存节点的连接会留在原地。手动重建会话能实现再分发,如果有让存量连接自动切回的需求,我们在原驱动上做最小改造,补上“节点恢复后自动切回”的能力,其原理与 AUTO_RECONNECT=2 的底层逻辑基本一致:周期探测优先节点是否恢复,恢复后把存量连接带回原节点。
原驱动已经具备连接管理的全部能力:TCP 建连与协议、SQL 执行、appName 哈希分发、故障时的自动切换,都由原驱动完成。缺的只有一件事——恢复后把存量连接带回原节点。所以改造不碰任何连接逻辑,只做两件事:
DmDriver.connect(String, Properties) 的每个返回点注入一行 conn = DmFailbackDriver.wrapNative(conn, url, info),再重打包 jar。原驱动其余 480+ 个类、SPI 注册、MANIFEST 全部原样保留,驱动类名、连接串(仍是 jdbc:dm://)与全部参数都不变,应用零改动。wrapNative 仅在连接串含 epSelectorOption=1 时,把返回的连接套一层 JDK 动态代理并登记进后台探针名单;其余连接原样透传,不套代理、不登记,零开销。“在建连入口加一个钩子”落到 jar 包里,实际只改一个类:dm/jdbc/driver/DmDriver.class 的 connect(String url, Properties info) 方法。它是驱动对外唯一的建连入口,所有连接都从这里返回,改这一个类即可让全部连接经过包装层;jar 内其余 480+ 个类、SPI 注册、MANIFEST 全部原样保留。改动前后示意(伪代码):
// 改前:DmDriver.connect 原样返回原生连接
Connection connect(String url, Properties info) {
... return conn; // 共 3 个返回点:null / 无过滤器直连 / 有过滤器
}
// 改后:每个返回点都经 DmFailbackDriver.wrapNative 包装
Connection connect(String url, Properties info) {
... return dmfb.DmFailbackDriver.wrapNative(conn, url, info);
}
改 jar 包的方式,以 IDEA 的 jareditor 插件为例(这也是我们实际采用的方式):
dm/jdbc/driver/DmDriver.class,jareditor 会把字节码反编译成可读的 Java 源码;connect(String, Properties) 方法,在每个返回语句前补一行 conn = dmfb.DmFailbackDriver.wrapNative(conn, url, info);,并把新增的 dmfb 包 5 个类(DmFailbackDriver、FbHandler、FbHolder、FbTickTask 及内部类)一起编译进 jar;如果希望改造可重复、驱动升级后一键重打,也可改用 ASM 字节码补丁脚本(读取 DmDriver.class → 在 connect 的每个返回点前插入 wrapNative 调用 → 写回 jar),效果与 jareditor 手工修改完全一致。
getHostName():getHostPort() 读取落地节点,驱动不提供该方法时才回退 SELECT NAME FROM V$INSTANCE,正常路径不增加 SQL 往返。dmfb: connection reset for failback to preferred node 为消息的 SQLException,促使应用重连;重连经原驱动的哈希路由自然落回原节点,即完成自动切回。故障期间的切换仍由原驱动完成,包装层只通过代理拦截调用异常,被动更新“连接当前所在节点”的感知,不干预切换过程;探针内部建连通过 SKIP_WRAP 标记防递归,不会把内部连接再套一层。整个补丁只新增 dmfb 包内 5 个类、注入 1 处字节码,改动面很小。
在压测过程中执行 SQL 验证,固定节点与切回后的落点均符合预期:
这一方案的代价很小:应用侧只需把补丁后的 jar 替换原驱动 jar,连接串、驱动类名、参数全部不变;运行侧开销仅为每个 appName 每 checkFreq(默认 60 秒)一条探针短连接,业务路径上仅多一层亚毫秒级的代理分发。补丁驱动的引入对原有功能与性能没有影响——它只是多了一个自动切回。我们沿用前文的压测方法做了性能验证:轮询基线(不配 appName)总 TPS 3549、平均延迟 23 ms、P95 101 ms;开启 epSelectorOption=1 + appName、叠加自动切回后,总 TPS 5029(+41.7%)、平均延迟 16 ms、P95 40 ms(-60%);微观层面远程读下降 99%、跨节点 latch 请求下降约 90%、排他自等待归零,与 EP_SELECTOR 手动钉节点的收益完全等价。这能证明新 jar 没有改变原驱动的任何行为——分发、故障切换、SQL 执行一切照旧,只是叠加了“恢复后自动切回”这一个能力。
本文从微服务架构与 DMDSC 集群结合场景下的一个普遍问题出发:默认轮询连接让多个节点频繁访问相同数据页,触发缓存交换与全局协调开销,直至演变为性能瓶颈。解法在连接层——巧用服务名参数,按微服务把连接钉到不同节点。围绕这一解法,我们做了三件事:从 TPS 与系统视图微观指标两方面验证固定分发到单节点比轮询更能提升性能;介绍两种固定节点分发的配置方式;按需求微调,在原驱动上补上“节点恢复后自动切回”的能力。
性能侧的实验表明:钉节点后跨节点远程读下降 97%、跨节点 latch 请求下降 84%、排他自等待基本归零;宏观上总 TPS 提升 65%、平均响应时间下降 40%、P90 延迟下降 61%。提升的本质不是“钉”这个动作,而是钉住之后实现的数据局部性——把共享存储集群从“两个节点抢同一批页”变成“各管一摊”。
可用性侧的容灾测试表明:钉节点配置下,节点故障后连接自动迁到幸存节点,故障节点被集群自动拉起并重加入后,连接在一个 CHECK_FREQ 周期内自动切回原节点,全程无需人工干预,由此可见这种连接方法并不牺牲可用性。
实现层面,连接分发有两种方式可选:其一,在 dm_svc.conf 中配置 EP_SELECTOR,为每个微服务手动指定优先节点;其二,驱动层 epSelectorOption + appName 按哈希自动分发,连接串无需感知节点顺序,适合微服务数量多、扩容频繁的场景。若采用 appName 自动分发,还可以通过在原驱动上打字节码补丁的方式叠加“节点恢复后自动切回”能力,连接串、驱动类名与应用代码均无需改动。
文章
阅读量
获赞
