暂无图片
暂无图片
4
暂无图片
暂无图片
暂无图片

OceanBase分布式架构,替代证券合规系统数据库群

原创 多明戈教你玩狼人杀 2025-09-19
321

合规风控系统是很多金融企业极其重要的业务系统,这里面既包含了金融监管的要求,也有企业内部管理的要求,而这类系统往往存在着上游系统较多、数据链路复杂、数据不同步等多种问题。本文将会以业务系统运维+DBA的视角,提供一个基于OceanBase改造的方案,旨在降低系统架构的复杂程度,减少ETL以及数据不同步带来的问题。

现有方案

如图所示,合规风控系统是基于Oracle数据库的系统,其中上游数据主要由如下几个部分组成:

  1. 实时交易数据。保存在上游的多个Oracle数据库中,为了尽可能降低业务时间对系统的负载,不会直接频繁读取,而是从Dataguard同步备库,再从备库同步到一个MySQL中间库。通过合规风控系统的调度任务,每分钟取数一次。实时性要求高,容错率低。主要用于实时的交易监控。
  2. 盘后交易数据。同样保存在上游的多个Oracle数据库中,每天盘后读取,直接从备库取数,无需中间库同步。与实时交易数据相比,对实时性要求较低,而且容错率较高。主要用于盘后的反洗钱、异常交易、风险控制等多个模块的计算。
  3. 外部文件。业务需要的数据文件,包含TA文件、估值文件、离线行情文件、黑名单文件、场外基金信息等等。这些既有行情文件这样实时读取的,也有每天定时读取的。特点是格式不统一、实时性要求不一,与其他数据存在着依赖关系。
  4. 实时行情数据。主要是交易所行情网管提供的流式行情数据,用于盘间的行情计算,在异常交易和风险控制两个模块有应用,其中数据的实时性要求很高。
  5. 资讯数据。包含了各类股票、基金、债券、市场公告、舆情等数据,通常以定时推送的方式由资讯供应商落盘方式写入到数据库中。这些资讯不仅仅是合规系统用,交易系统、财务都可能用到。

而数据接入都是依赖于应用系统的数据任务,每个具体的调度任务有自己的时间,并且各个调度任务有自己对应的依赖关系,比如反洗钱计算,需要获得最新的黑名单数据,结合客户数据交易数据来计算,风险控制,需要获得财务数据、持仓数据、行情数据共同计算。而到了月度、季度、年度时间节点,要对净资本、客户、交易多类数据做综合计算,生成报送数据上报监管。实际上不仅仅是一个系统,更加是一个特定主题的数仓。

当前方案呈现出如下几个问题:
  1. 数据库架构繁杂,上游数据中,数据库来源较多,交易柜台、财务系统、中间库、资讯数据等等,不同数据库之间版本管理、高可用、多副本、数据同步等等,关联复杂,维护起来成本较高。
  2. 存在单点风险,一旦中间库所在的服务器或者实例不可用,将会出现合规系统无法正常获取数据的情况。
  3. 数据不一致风险,Oracle与MySQL之间的跨平台数据交互,需额外处理兼容性问题,易引发数据一致性风险。
  4. 资源隔离问题:两个资讯数据库实际上是在同一个MySQL实例中,易出现资源争用,尤其是数据批量写入或采集时。

根据现有方案,我们来做一个替换方案,选用OceanBase分布式版本4.4。


替换方案

作为分布式多租户的数据库,在替换之前,我们要使用到如下的功能:

  1. 多租户机制:OceanBase通过租户实现资源隔离,每个租户类似于传统数据库的实例,可以创建自己的用户、数据库、表等所有客体对象,有自己独立的系统数据库和变量。这意味着你可以在一个OceanBase集群内为交易、财务、资讯等不同业务线创建独立租户,实现资源隔离与管理简化。

  2. 金融级高可用:OceanBase通过多副本(通常至少3个)架构保障高可用,数据以多副本方式存储在集群各个节点,保证RPO=0(恢复点目标),甚至支持异地多活。这对于合规系统至关重要。

  3. 高度兼容性:OceanBase高度兼容MySQL和Oracle数据库生态,与MySQL业务可无缝切换,并支持Oracle平滑迁移。这降低了从现有Oracle和MySQL迁移而来的复杂性。

  4. HTAP混合负载:OceanBase的分布式并行计算引擎对OLTP和OLAP应用都进行了很好优化,支持跨数据库节点的DQL和DML并发执行,一套引擎即可同时支持混合负载。这使得合规系统既能处理交易类业务,也能进行实时分析。

  5. 多副本:无论是及金融监管还是合规角度,都需要多副本的模式,而OceanBase的多副本以及定向访问模式,可以帮助我们在不干扰业务正常进行的情况下,对只读副本取数。

整个改造方案可以按照如下进行:

  1. 创建4个Oracle租户,分别为交易系统1、交易系统2、财务系统、合规系统使用。不同的租户之间实现资源与权限的隔离,并且考虑到交易系统和财务系统需要同步数据到合规系统,因此这三个业务系统的租户各自至少2个副本。并且配置定向访问。
  2. 创建2个MySQL租户,分别为万得资讯、聚源资讯。而这两个MySQL租户没有多副本的需求,可以直接通过访问原表的方式来获取数据。但是多配置一个租户完成高可用,总是好的。
  3. 不同租户之间的数据获取,可以依赖DBLink与数据库自己的定时任务,也可以依赖应用程序本身的调度任务,考虑到如果改用DBLink与定时任务,会对应用程序造成较多的改动,因此本方案依旧使用内置的调度任务。
  4. 离线文件OceanBase可以直接读取,同样因为对应用程序的改造比较多,因此保留原有的配置。


那么,整个分布式多租户和Zone的配置如下

租户租户类型主副本备副本只读副本
交易系统1(T1)OracleZone1Zone2Zone3
交易系统2(T2)OracleZone1Zone2Zone3
财务系统(F)OracleZone1Zone2Zone3
合规系统(C)OracleZone3Zone2
万得资讯(W)MySQLZone2Zone3Zone1
聚源资讯(J)MySQLZone2Zone3Zone1
  1. 主副本(Leader):部署在租户的 “Primary Zone”(如交易 1 的 Zone1),处理所有写事务和未指定路由的读事务。
  2. 备用主副本(Follower):部署在 “Standby Zone”(如交易 1 的 Zone2),同步主副本日志,主副本故障时自动升为主副本。
  3. 只读副本(Read-Only Follower):部署在 “Read Zone”(如交易 1 的 Zone3),仅同步日志供读查询,不参与主备切换,专门供合规系统访问。
  4. 合规系统自身:仅需部署主 + 备用主副本(2 副本),无需只读副本(无被查询需求),主副本优先放在 Zone3(与业务租户只读副本分离)。


除此之外,我们还需要考虑配置定向访问的内容,确保合规系统访问的都是各自的备用副本,而不是直接访问读写副本。因此还需要使用到OBProxy组件,来实现这个特性。最终总结一下整个系统的架构:


Zone1,交易系统和财务系统的Primary Zone,日常所有的业务入口都在这里。

Zone2,资讯数据同步的Primary Zone,外部推送都写入这个Zone。

Zone3,合规系统的Primary Zone,可以直接读取本Zone内的只读副本。

在每个Zone内部,每个业务系统用不同的OBServer做隔离,确保资源不会出现争用。而考虑到合规系统不需要强读一致性,在Zone3中访问只读副本完全可以匹配业务需求。如果出现Zone1不可用,那么Zone2可以接管Zone1的业务系统,继续提供服务。如果Zone2不可用,那么Zone1可以接管Zone2的资讯数据写入。如果Zone3不可用,那么Zone2接管Zone3的合规系统生产环境,重新调整定向访问,将访问业务系统和资讯数据各自的备副本。


实操步骤

1. 资源规划,我们有三个机房或者虚拟机机房,分别位于北京1,上海,北京2,分别对应Zone1、Zone2、Zone3其中交易系统和财务系统在北京1也就是Zone1,合规系统在Zone3,资讯数据库都在Zone2。
ALTER SYSTEM ADD ZONE zone1 IDC 'BJ1', REGION 'Beijing';
ALTER SYSTEM ADD ZONE zone2 IDC 'SH', REGION 'Shanghai';
ALTER SYSTEM ADD ZONE zone3 IDC 'BJ2', REGION 'Beijing';ALTER SYSTEM START ZONE zone1;ALTER SYSTEM START ZONE zone2;ALTER SYSTEM START ZONE zone3;

然后我们查一下状态:

obclient [(none)]> SELECT * FROM oceanbase.DBA_OB_ZONES;
+-------+----------------------------+----------------------------+--------+-----+----------+-----------+
| ZONE  | CREATE_TIME                | MODIFY_TIME                | STATUS | IDC | REGION   | TYPE      |
+-------+----------------------------+----------------------------+--------+-----+----------+-----------+
| zone1 | 2025-09-13 15:29:19.753281 | 2025-09-13 15:29:19.753281 | ACTIVE | BJ1 | Beijing  | ReadWrite |
| zone2 | 2025-09-13 15:29:53.418605 | 2025-09-13 15:29:53.418605 | ACTIVE | SH  | Shanghai | ReadWrite |
| zone3 | 2025-09-13 15:30:47.901368 | 2025-09-13 15:30:47.901368 | ACTIVE | BJ2 | Beijing  | ReadWrite |
+-------+----------------------------+----------------------------+--------+-----+----------+-----------+

添加OBServer

-- 1. 向zone1(192.168.1.0/24网段)添加节点
ALTER SYSTEM ADD SERVER '192.168.1.101:2882' TO ZONE 'zone1';  -- 交易系统1(T1)-zone1节点
ALTER SYSTEM ADD SERVER '192.168.1.102:2882' TO ZONE 'zone1';  -- 交易系统2(T2)-zone1节点
ALTER SYSTEM ADD SERVER '192.168.1.103:2882' TO ZONE 'zone1';  -- 财务系统(F)-zone1节点
ALTER SYSTEM ADD SERVER '192.168.1.104:2882' TO ZONE 'zone1';  -- 万得资讯(W)-zone1节点(只读)
ALTER SYSTEM ADD SERVER '192.168.1.105:2882' TO ZONE 'zone1';  -- 聚源资讯(J)-zone1节点(只读)

-- 2. 向zone2(10.0.2.0/24网段)添加节点
ALTER SYSTEM ADD SERVER '10.0.2.101:2882' TO ZONE 'zone2';     -- 交易系统1(T1)-zone2节点(备用)
ALTER SYSTEM ADD SERVER '10.0.2.102:2882' TO ZONE 'zone2';     -- 交易系统2(T2)-zone2节点(备用)
ALTER SYSTEM ADD SERVER '10.0.2.103:2882' TO ZONE 'zone2';     -- 财务系统(F)-zone2节点(备用)
ALTER SYSTEM ADD SERVER '10.0.2.104:2882' TO ZONE 'zone2';     -- 万得资讯(W)-zone2节点(主)
ALTER SYSTEM ADD SERVER '10.0.2.105:2882' TO ZONE 'zone2';     -- 聚源资讯(J)-zone2节点(主)
ALTER SYSTEM ADD SERVER '10.0.2.106:2882' TO ZONE 'zone2';     -- 合规系统(C)-zone2节点(备用)

-- 3. 向zone3(172.16.3.0/24网段)添加节点
ALTER SYSTEM ADD SERVER '172.16.3.101:2882' TO ZONE 'zone3';   -- 交易系统1(T1)-zone3节点(只读)
ALTER SYSTEM ADD SERVER '172.16.3.102:2882' TO ZONE 'zone3';   -- 交易系统2(T2)-zone3节点(只读)
ALTER SYSTEM ADD SERVER '172.16.3.103:2882' TO ZONE 'zone3';   -- 财务系统(F)-zone3节点(只读)
ALTER SYSTEM ADD SERVER '172.16.3.104:2882' TO ZONE 'zone3';   -- 万得资讯(W)-zone3节点(备用)
ALTER SYSTEM ADD SERVER '172.16.3.105:2882' TO ZONE 'zone3';   -- 聚源资讯(J)-zone3节点(备用)
ALTER SYSTEM ADD SERVER '172.16.3.106:2882' TO ZONE 'zone3';   -- 合规系统(C)-zone3节点(主)

确认状态

obclient [(none)]> SELECT SVR_IP, ZONE, STATUS FROM oceanbase.DBA_OB_SERVERS;
+----------------+----------+--------+
| SVR_IP         | ZONE     | STATUS |
+----------------+----------+--------+
| 192.168.1.101  | zone1    | ACTIVE |  -- 交易系统1(T1)-zone1节点
| 192.168.1.102  | zone1    | ACTIVE |  -- 交易系统2(T2)-zone1节点
| 192.168.1.103  | zone1    | ACTIVE |  -- 财务系统(F)-zone1节点
| 192.168.1.104  | zone1    | ACTIVE |  -- 万得资讯(W)-zone1节点
| 192.168.1.105  | zone1    | ACTIVE |  -- 聚源资讯(J)-zone1节点
| 10.0.2.101     | zone2    | ACTIVE |  -- 交易系统1(T1)-zone2节点
| 10.0.2.102     | zone2    | ACTIVE |  -- 交易系统2(T2)-zone2节点
| 10.0.2.103     | zone2    | ACTIVE |  -- 财务系统(F)-zone2节点
| 10.0.2.104     | zone2    | ACTIVE |  -- 万得资讯(W)-zone2节点
| 10.0.2.105     | zone2    | ACTIVE |  -- 聚源资讯(J)-zone2节点
| 10.0.2.106     | zone2    | ACTIVE |  -- 合规系统(C)-zone2节点
| 172.16.3.101   | zone3    | ACTIVE |  -- 交易系统1(T1)-zone3节点
| 172.16.3.102   | zone3    | ACTIVE |  -- 交易系统2(T2)-zone3节点
| 172.16.3.103   | zone3    | ACTIVE |  -- 财务系统(F)-zone3节点
| 172.16.3.104   | zone3    | ACTIVE |  -- 万得资讯(W)-zone3节点
| 172.16.3.105   | zone3    | ACTIVE |  -- 聚源资讯(J)-zone3节点
| 172.16.3.106   | zone3    | ACTIVE |  -- 合规系统(C)-zone3节点
+----------------+----------+--------+


2. 创建多租户

在sys租户下,先创建资源组

-- 1. 交易系统1(T1)资源单元(Oracle模式)
CREATE RESOURCE UNIT unit_1
MAX_CPU=24, MIN_CPU=24,
MEMORY_SIZE='64G', LOG_DISK_SIZE='500G';

 -- 2. 交易系统2(T2)资源单元(同T1)
CREATE RESOURCE UNIT unit_2
MAX_CPU=16, MIN_CPU=16,
MEMORY_SIZE='32G', LOG_DISK_SIZE='500G';

 -- 3. 财务系统(F)资源单元(同T1)
CREATE RESOURCE UNIT unit_3
MAX_CPU=24, MIN_CPU=24,
MEMORY_SIZE='64G', LOG_DISK_SIZE='500G'; 

-- 4. 合规系统(C)资源单元(仅主/备用,无只读)
CREATE RESOURCE UNIT unit_4
MAX_CPU=16, MIN_CPU=16,
MEMORY_SIZE='32G', LOG_DISK_SIZE='300G';

 -- 5. 万得资讯(W)资源单元(MySQL模式)
CREATE RESOURCE UNIT unit_5
MAX_CPU=16, MIN_CPU=16,
MEMORY_SIZE='32G', LOG_DISK_SIZE='400G';

 -- 6. 聚源资讯(J)资源单元(同W)

CREATE RESOURCE UNIT unit_6
MAX_CPU=16, MIN_CPU=16,
MEMORY_SIZE='32G', LOG_DISK_SIZE='400G';

创建资源池

-- 1. 交易系统1(T1)资源池
CREATE RESOURCE POOL pool_t1 
UNIT='unit1', UNIT_NUM=1,
ZONE_LIST=('zone1', 'zone2', 'zone3');

-- 2. 交易系统2(T2)资源池
CREATE RESOURCE POOL pool_t2 
UNIT='unit2', UNIT_NUM=1,
ZONE_LIST=('zone1', 'zone2', 'zone3');

-- 3. 财务系统(F)资源池
CREATE RESOURCE POOL pool_f 
UNIT='unit3', UNIT_NUM=1,
ZONE_LIST=('zone1', 'zone2', 'zone3');

-- 4. 合规系统(C)资源池(无只读副本)
CREATE RESOURCE POOL pool_c 
UNIT='unit4', UNIT_NUM=1,
ZONE_LIST=('zone3', 'zone2');

-- 5. 万得资讯(W)资源池
CREATE RESOURCE POOL pool_w 
UNIT='unit5', UNIT_NUM=1,
ZONE_LIST=('zone2', 'zone3', 'zone1');

-- 6. 聚源资讯(J)资源池
CREATE RESOURCE POOL pool_j 
UNIT='unit6', UNIT_NUM=1,
ZONE_LIST=('zone2', 'zone3', 'zone1');


创建租户

-- 1. 交易系统1(t1)- Oracle模式
CREATE TENANT IF NOT EXISTS tenant_t1
 LOCALITY = 'F@zone1,F@zone2,R@zone3'
  PRIMARY_ZONE = 'zone1'
    RESOURCE_POOL_LIST = ('pool_t1')
  SET
    ob_compatibility_mode = 'oracle',
    ob_tcp_invited_nodes = '%';       

 -- 2. 交易系统2(t2)- Oracle模式
CREATE TENANT IF NOT EXISTS tenant_t2
 LOCALITY = 'F@zone1,F@zone2,R@zone3'
  PRIMARY_ZONE = 'zone1'
    RESOURCE_POOL_LIST = ('pool_t2')
  SET
    ob_compatibility_mode = 'oracle',
    ob_tcp_invited_nodes = '%';    

-- 3. 财务系统(f)- Oracle模式
CREATE TENANT IF NOT EXISTS tenant_f
 LOCALITY = 'F@zone1,F@zone2,R@zone3'
  PRIMARY_ZONE = 'zone1'
    RESOURCE_POOL_LIST = ('pool_f')
  SET
    ob_compatibility_mode = 'oracle',
    ob_tcp_invited_nodes = '%';    

-- 4. 合规系统(c)- Oracle模式(主副本Zone为zone3)
CREATE TENANT IF NOT EXISTS tenant_c
LOCALITY = 'F@zone3,F@zone2'
  PRIMARY_ZONE = 'zone3', 
  RESOURCE_POOL_LIST = ('pool_c'),
  SET
    OB_TCP_INVITED_NODES = '%',
    ob_compatibility_mode = 'oracle'; 

-- 5. 万得资讯(w)- MySQL模式(兼容模式改为mysql)
CREATE TENANT IF NOT EXISTS tenant_w
  LOCALITY = 'F@zone2,F@zone3,R@zone1'
  PRIMARY_ZONE = 'zone2'
  RESOURCE_POOL_LIST = ('pool_w')
  SET
    ob_tcp_invited_nodes = '%';

 
-- 6. 聚源资讯(j)- MySQL模式
CREATE TENANT IF NOT EXISTS tenant_j
  LOCALITY = 'F@zone2,F@zone3,R@zone1'
  PRIMARY_ZONE = 'zone2'
  RESOURCE_POOL_LIST = ('pool_j')
  SET
    ob_tcp_invited_nodes = '%';


这样的话,所有资源分配和创建都完成了,还差一个重要步骤,部署OBProxy,并配置强读一致性,在每个租户执行

SET GLOBAL ob_read_consistency='STRONG';

这样确保我们应用程序在OB Proxy中的请求都能够分到Primary Zone中,实现定向访问。



案例总结

用OceanBase替代,解决了如下问题:

1. 数据库实例与版本较多,管理难度提高的。

2. 异构数据库数据库可能存在的数据不一致。

3. 解决单点风险,提供高可用多副本,符合监管要求。

4. 多租户不同OBServer解决资源隔离。

而ETL部分,暂时没有动,如果未来OceanBase能够有新的特性,比如跨租户用DBLink创建物化视图刷新,那么ETL部分也可以优化。

当然,这个方案还有几个点可以进一步优化:

1. 合规系统中部分表由行存替换列存,比如月度季度报表的计算。

2. Proxy的配置以及灾备切换,这部分仍然有更多的事情可以做。

3. 离线文件的处理,可以利用OceanBase旁路导入的特性来继续优化。

4. 存储过程的改写。原架构基于Oracle开发了各种存储过程,迁移到OceanBase之后,存储过程要做调整,来做性能优化。

在写这篇文章的时候,还是有一些感慨,早期国产数据库多以 “替代国外产品” 为目标,侧重兼容性;而 OceanBase 这类方案已进入自主创新阶段,例如通过 Primary Zone灵活指定主副本位置、多租户资源精细化管控、分布式事务强一致性等特性,不仅实现了功能上的对等,更在架构设计上超越了传统集中式数据库的局限。这种技术突破印证了国产数据库正在逐步从能用到用得好的质变,也期待越来越多的国产数据库厂商有更多创新和优化。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论