AI 制药平台的架构思想:服务拆分、数据隔离与资源调度

梳理 AI 制药平台的轻量服务化架构,理解 Java 与 Python AI 服务的职责划分、异构资源调度、逻辑删除和多租户数据隔离。

2026/8/8
6 分钟阅读
目录
  1. Java 独占任务状态机是什么意思?

批量任务“现在进行到哪一步、是否成功失败、能不能重试”这些状态,只能由 Java 主后端管理和保存;Python AI 服务只负责计算。

AI制药平台的架构思想

  • 分布式 + 服务化架构,不是完整意义上的微服务
  • 异构资源调度
  • [GPU 机器]  ai-service (Python 推理)
                    ▲ HTTP
    [大内存机器] java-backend ──┬──▶ [DB 机器] PostgreSQL + Redis
                    ▲ HTTP      
    [Nginx/CDN]  frontend (静态资源)
    
    • AI 服务吃 GPU/显存
    • Java 业务吃 内存/CPU
    • 数据库吃 快磁盘/IO

微服务

  1. 微服务是按业务领域拆(订单服务、用户服务、支付服务……),你是按技术职责拆(Java 管业务 / Python 管 AI 计算)。你更像"前后端分离 + 后端按技术分层",是 SOA(面向服务架构)的轻量形态。
  2. 微服务讲究"每个服务一个库"(database per service)
  3. 真正的微服务通常还配套服务注册发现、API 网关、配置中心、链路追踪、独立 CI/CD。你现在没有这些(也不需要)。

数据库字段

deleted 字段 = 逻辑删除(软删除)

它解决什么问题:普通的 DELETE FROM molecules WHERE id=1物理删除,数据真没了,找不回来。逻辑删除是假删——不真删行,只把 deleted 字段从 0 改成 1,标记"这条已删除"。

这个框架的配置application.yml):

logic-delete-field: deleted        # 用 deleted 这一列标记
logic-delete-value: 1              # 1 = 已删除
logic-not-delete-value: 0          # 0 = 正常

机制(对你透明)

  • 你调用 MyBatis-Plus 的 deleteById(1),它实际执行的不是 DELETE,而是 UPDATE molecules SET deleted=1 WHERE id=1
  • 你写 SELECT * FROM molecules,框架自动改写成 SELECT * FROM molecules WHERE deleted=0,已删的行你查不到。
  • 所以你平时写代码感觉就是"删了就没了",但数据其实还在库里。

为什么企业都这么做:误删能恢复、保留历史审计、关联数据不断链、满足合规(很多业务数据法律上不允许真删)。

一个你要知道的坑(正好影响 molecules):逻辑删除下,"删掉的旧行还占在表里"。如果你给 canonical_smiles 建普通唯一索引,删掉阿司匹林后再想重新录入,会因为旧的那条(deleted=1)还在而唯一冲突报错。PostgreSQL 的正解是用部分唯一索引 UNIQUE (tenant_id, canonical_smiles) WHERE deleted=0——只对"未删除"的行约束唯一。这点我会写进给 codex 的任务清单。

多租户

多租户是什么:一套系统、一个数据库,同时服务多个"租户"(可以理解成多个公司/组织),租户之间数据互相隔离、彼此看不见。典型场景是 SaaS——A 公司和 B 公司用同一个系统,但各自只能看到自己的数据。

这个框架用的是"行级多租户"(共享库、共享表,靠一列 tenant_id 区分):

  • 需要隔离的表加一列 tenant_id
  • 拦截器(TenantLineInnerInterceptor + OinsistTenantLineHandler)在每条 SQL 自动加 tenant_id 条件
  • 当前租户哪来的?前端请求头带 Tenant-Id(默认 000000),后端取出来用

举例,你写:

SELECT * FROM molecules WHERE mol_weight > 100

拦截器自动改写成:

SELECT * FROM molecules WHERE mol_weight > 100 AND tenant_id = '当前租户'

你写业务代码时几乎感知不到 tenant_id 的存在,它由框架自动加。

是不是每张表都要加?——不是,分两类(这正是 OinsistTenantLineHandler 里那个白名单的意义):

类型加 tenant_id 吗例子
租户隔离表(各租户私有的业务数据)✅ 加sys_usersys_rolemoleculesprediction_tasks
全局共享表(所有租户公用的系统/字典数据)❌ 不加,进 IGNORE_TABLES 白名单sys_menu(菜单全系统一套)、sys_tenant(租户表自己)、sys_role_menu

规则:默认就是"加 tenant_id"(拦截器默认拦截所有表),只有当这张表是全系统共享的字典/系统表时,才把它放进白名单豁免