在Text2SQL场景中,构建准确的数据库表关系图谱是提升SQL生成质量的关键。但许多开发者面临这样的困境:无法直接访问业务代码仓库,导致无法通过解析MyBatis Mapper或ORM映射文件来提取表关系。
本文将介绍一种基于SQL执行日志自动提取表关系的替代方案,通过分析MySQL的实际执行SQL语句,自动构建表关系图谱并写入图数据库,为Text2SQL系统提供准确的关系信息。方案已支持从Binlog实时读取SQL,实现真正的实时关系提取和更新。
一、问题背景
1.1 传统方案的局限性
在Text2SQL系统中,了解数据库表之间的关系至关重要,它有助于生成更准确的JOIN语句、理解业务逻辑和数据流向,并优化查询性能。传统的表关系提取方案主要有以下几种,但各有不足:
| 方案 |
优点 |
缺点 |
| 解析MyBatis Mapper XML |
关系准确,包含业务语义 |
需要访问代码仓库权限 |
| 解析ORM实体类 |
结构清晰,易于解析 |
需要访问代码仓库权限 |
| 分析数据库外键约束 |
无需代码权限 |
很多项目为性能考虑不使用外键约束 |
| 手动维护关系配置 |
灵活可控 |
维护成本高,容易遗漏 |
1.2 解决思路
既然无法从代码层面提取关系,那么我们就从实际执行的SQL语句中提取。MySQL提供了多种日志机制(如General Log、Binlog、Slow Query Log、Performance Schema)来记录SQL执行历史。通过分析这些日志,我们可以:
- 提取JOIN语句中的表关系。
- 分析WHERE子句中的表关联。
- 识别子查询中的表引用关系。
- 最终自动构建出完整的表关系图谱。
二、技术方案设计
2.1 整体架构
方案分为三个核心模块:
┌─────────────────┐
│ SQL日志读取器 │ → 从多种数据源读取SQL语句
└────────┬────────┘
│
▼
┌─────────────────┐
│ 关系提取器 │ → 解析SQL,提取表关系
└────────┬────────┘
│
▼
┌─────────────────┐
│ Neo4j写入器 │ → 构建图数据库
└─────────────────┘
2.2 核心模块说明
模块一:SQL日志读取器
支持从多种数据源读取SQL,Binlog因其实时性强、支持增量更新,成为生产环境推荐方案。
模块二:关系提取器
采用多种策略从SQL中提取关系:
- JOIN关系:解析
JOIN ... ON 子句。
- WHERE关系:解析WHERE子句中的字段关联。
- 子查询关系:分析
IN、EXISTS 等子查询。
模块三:Neo4j写入器
将提取的关系结构化为节点和边,持久化到 Neo4j 图数据库中,便于可视化查询与分析。
三、实现细节
3.1 SQL日志读取实现
3.1.1 Binlog实时读取(推荐)
Binlog是MySQL的二进制日志,记录了所有数据变更操作。通过pymysql-replication库可以实时读取并解析。
from pymysqlreplication import BinLogStreamReader
from pymysqlreplication.event import QueryEvent
# 创建binlog流
stream = BinLogStreamReader(
connection_settings={
"host": "localhost",
"port": 3306,
"user": "root",
"passwd": "password",
},
server_id=100,
resume_stream=True # 支持断点续传
)
# 实时读取事件
for binlogevent in stream:
# 处理QueryEvent(DDL和部分DML)
if isinstance(binlogevent, QueryEvent):
sql = binlogevent.query
# 提取关系...
优势:
- 实时性强,无需等待日志文件滚动。
- 支持断点续传,可随时停止和恢复。
- 记录完整的数据变更操作。
3.2 表关系提取实现
核心是使用正则表达式解析SQL,提取表名、关联字段及关联类型(JOIN/WHERE等)。同时需要处理复杂的表别名映射问题。
3.3 Neo4j写入实现
使用 py2neo 库执行Cypher语句,将关系写入Neo4j。
MATCH (from_table:Table {name: $from_table})
MATCH (to_table:Table {name: $to_table})
MERGE (from_table)-[r:REFERENCES {
field_relation: $field_relation,
relation_type: $relation_type,
source: $source
}]->(to_table)
四、使用指南
4.1 环境准备
4.1.1 安装依赖
# 基础依赖
pip install pymysql py2neo
# Binlog实时读取(推荐)
pip install pymysql-replication
4.1.2 配置MySQL
启用Binlog(推荐):
在my.cnf中配置并重启MySQL服务:
[mysqld]
log-bin=mysql-bin
binlog-format=ROW
server-id=1
4.2 运行脚本
4.2.1 从Binlog实时读取(推荐)
通过编程方式调用,实现增量更新到Neo4j。
from sql_log_to_neo4j import SQLLogToNeo4jPipeline
pipeline = SQLLogToNeo4jPipeline()
pipeline.run_from_binlog_realtime(
log_file=None, # 从最新位置开始
stop_after_seconds=None, # 持续运行
incremental_update=True # 实时增量更新到Neo4j
)
4.3 结果查看
在Neo4j Browser中执行Cypher查询,即可直观查看自动构建出的表关系图谱。
五、方案对比与最佳实践
5.1 方案对比
| 对比项 |
MyBatis Mapper解析 |
SQL日志解析(本方案) |
| 代码权限要求 |
需要 |
不需要 |
| 数据来源 |
代码文件 |
实际执行日志 |
| 适用场景 |
有代码权限的项目 |
无代码权限/生产环境监控 |
5.2 最佳实践
- 数据源选择:生产环境强烈推荐使用
Binlog实时读取方案。
- 性能优化:可从Performance Schema读取时限制条数,或对大量SQL进行分批处理。
- 关系质量:可融合多个数据源(如Binlog+Slow Log)的结果,并对高频出现的关系赋予更高权重。
六、总结
通过从SQL执行日志(特别是Binlog)自动提取表关系,本方案有效解决了无业务代码访问权限的困境,实现了表关系图谱的自动化、实时化构建。这为Text2SQL系统提供了基于真实查询模式的高质量关系数据支撑。
当然,方案依赖于已有的SQL执行记录,对于未执行过的查询关系无法覆盖。在实践中,建议将此方案作为基础,对关键业务关系进行人工补充验证,以构建更完备的知识图谱。