Agent 优化索引的边界:只做建议,不直连主库执行 DDL
Agent 优化索引的边界:只做建议,不直连主库执行 DDL
说明:表规模、锁等待和查询分布均为示例。任何索引建议都应经过执行计划、影子验证与 DBA 审批,不能由 Agent 直接写主库。
复现重点:保存原 SQL、参数分布、执行计划、表统计信息和写入基线。Agent 只能在只读或虚拟索引环境生成建议;DDL 是否执行,仍由 DBA 根据锁影响、复制延迟和回退方案决定。
Agent 直跑 ALTER TABLE,为什么会把风险放大
考虑一个“AI 自动治理慢查询”的方案:Agent 读到 SQL 后直接连接主库执行 DDL。这个设计把建议、审批与执行混成了一个权限边界。
他们编写了一个数据库治理 Agent,让 Agent 实时监听 MySQL 慢查询日志(Slow Log)。一旦发现执行时间超出 2 秒的 SQL,Agent 便自动调用 LLM 推理出优化建议,并直接通过数据库连接池对主库发起 ALTER TABLE ADD INDEX 操作。
当天下午,Agent 捕捉到了订单明细表的一条慢查询,随即自动生成并执行了一行 DDL:
-- Agent 自动执行的指令(没有加上无锁参数)
ALTER TABLE order_items ADD INDEX idx_user_status(user_id, status);
对于拥有 4500 万行记录的 order_items 表,由于缺乏 ALGORITHM=INPLACE, LOCK=NONE 的显式约束,MySQL 触发了全表 Copy 操作。大表锁死很快导致后端服务的 Connection Pool 快速吃满,全站订单写入直接卡死 40 分钟。
试图让非确定性的 LLM Agent 直接握有线上数据库的物理写权限,毫无工程安全边界可言。
flowchart TD
A[Slow Log 捕捉到慢查询 SQL] --> B[LLM Agent 提取特征并生成索引方案]
B --> C{物理隔离防线: 安全拦截闸门}
C -- 试图直连主库物理 DDL --> D[硬性拦截并报警: 严禁自动 ALTER TABLE]
C -- 进入虚拟评估机制 --> E[PostgreSQL: HypoPG / MySQL: INVISIBLE Index]
E --> F[执行 EXPLAIN COST 验证, 比对 Cost 降幅]
F -- Cost 降低 < 50% 或 增加写放大风险 --> G[标记为高风险方案并终止]
F -- Cost 降低 > 50% 且 满足无锁规范 --> H[生成 Schema 变更单并提交 gh-ost / pt-osc 灰度]
Agent 治理慢查询的三大工程死穴
如果不为 Agent 设定物理边界,Agent 往往会在以下三个方向将数据库推向崩溃边缘。
1. 盲目推荐索引,引发写放大与 Buffer Pool 污染
Agent 通常只关注单条 SQL 的查询速度。在看到 WHERE status = 1 AND is_deleted = 0 时,它极易推荐建立组合索引。但在实际生产中,status 这种低区分度(Cardinality)字段会严重浪费 B+ 树的内存页。更严重的是,高频写的表每多一个索引,所有的 INSERT/UPDATE 都需要同步维护 B+ 树的分裂与页面写入,直接将磁盘 IOPS 拉爆。
2. 忽略动态数据倾斜与 Explain 欺骗
Agent 依赖静态的 SQL 文本进行分析,无法感知数据库的动态统计信息(Histogram)。比如某个租户的数据量占了 90%,优化器在面对这个大租户时即使有索引也会选择全表扫描;而 Agent 给出的索引不仅没派上用场,反而干涉了优化器对其他 10% 小租户的索引选择。
3. 缺乏“无锁变更”的物理认知
在现代数据库运维中,大表的 DDL 操作绝不能直接跑 ALTER TABLE。需要使用 gh-ost 或 pt-online-schema-change 这种通过 Binlog 同步建立影子表的方式进行。LLM 无法天然感知这类底层运维工具的约束,直接放权后果不堪设想。
Agent 治理的正确边界与分工
Agent 不是数据库管理员(DBA),它的定位应当是高级分析助理,而非决策与执行一体的超级管理员。
- 允许 Agent 做的(工具调用边界):
- 解析慢查询 Log,提取 SQL 模板与指纹。
- 调用 PostgreSQL 的
HypoPG或 MySQL 的INVISIBLE特性生成虚拟索引。 - 模拟运行
EXPLAIN ANALYZE,比对成本(Cost)变化。
- 绝对禁止 Agent 做的(物理边界):
- 直接连接主库或写库执行任何 DDL 命令。
- 避开 DBA 的审核规则链条直接更新数据库物理配置。
示例 Python 虚拟索引评估与 DDL 拦截硬防线实现
下面的 Python 代码展示了如何为数据库 Agent 搭建一层物理隔离闸门。它强制所有索引建议需要通过 PostgreSQL 的 HypoPG 虚拟索引工具进行 Cost 比对,并严禁任何未经批准的物理 DDL 执行。
import psycopg2
import re
import json
from typing import Dict, Any
class DBAgentSafetyGate:
def __init__(self, db_config: Dict[str, Any]):
self.db_config = db_config
# 匹配物理 DDL 的危险正则表达式
self.dangerous_ddl_pattern = re.compile(
r'^\s*(DROP|ALTER|CREATE|TRUNCATE)\s+', re.IGNORECASE
)
def validate_agent_sql(self, sql_statement: str) -> bool:
"""物理硬拦截:检查 Agent 是否试图裸跑 DDL"""
if self.dangerous_ddl_pattern.search(sql_statement):
print(f"[SECURITY INTERCEPT] 拦截到 Agent 的高危 DDL 操作: {sql_statement}")
return False
return True
def evaluate_with_hypopg(self, raw_sql: str, proposed_index_ddl: str) -> Dict[str, Any]:
"""
使用 PostgreSQL 的 HypoPG 虚拟索引功能测试开销降幅
不真正创建物理 B+ 树,零磁盘开销安全评估
"""
if not self.validate_agent_sql(proposed_index_ddl):
return {"status": "BLOCKED", "reason": "试图直接执行物理 DDL"}
# 连接测试/影子数据库
conn = psycopg2.connect(**self.db_config)
cursor = conn.cursor()
try:
# 1. 开启 HypoPG 扩展
cursor.execute("CREATE EXTENSION IF NOT EXISTS hypopg;")
# 2. 获取原始 EXPLAIN Cost
cursor.execute(f"EXPLAIN (FORMAT JSON) {raw_sql}")
base_plan = cursor.fetchone()[0]
base_cost = base_plan[0]["Plan"]["Total Cost"]
# 3. 创建虚拟索引(仅在内存中注册,不触碰磁盘数据)
# 从 CREATE INDEX STATEMENT 提取表和列
cursor.execute(f"SELECT * FROM hypopg_create_index('{proposed_index_ddl}');")
hypo_res = cursor.fetchone()
index_relid, index_name = hypo_res[0], hypo_res[1]
# 4. 再次获取带有虚拟索引的 EXPLAIN Cost
cursor.execute(f"EXPLAIN (FORMAT JSON) {raw_sql}")
new_plan = cursor.fetchone()[0]
new_cost = new_plan[0]["Plan"]["Total Cost"]
# 5. 清理虚拟索引
cursor.execute(f"SELECT hypopg_remove_index({index_relid});")
cost_reduction = (base_cost - new_cost) / base_cost
return {
"status": "SUCCESS",
"base_cost": base_cost,
"new_cost": new_cost,
"reduction_percent": round(cost_reduction * 100, 2),
"virtual_index": index_name
}
except Exception as e:
conn.rollback()
return {"status": "ERROR", "reason": str(e)}
finally:
cursor.close()
conn.close()
if __name__ == "__main__":
# 配置只读/影子库连接
db_conf = {
"dbname": "order_db",
"user": "agent_readonly",
"password": "safe_password",
"host": "127.0.0.1",
"port": 5432
}
gate = DBAgentSafetyGate(db_conf)
# 场景 A: Agent 试图直接在生产库裸跑 DDL 语句
agent_bad_action = "ALTER TABLE orders ADD INDEX idx_created(created_at);"
gate.validate_agent_sql(agent_bad_action)
# 场景 B: Agent 提交建议,安全闸门使用 HypoPG 进行物理隔离验证
slow_sql = "SELECT * FROM orders WHERE user_id = 99823 AND status = 'PAID';"
index_suggestion = "CREATE INDEX ON orders (user_id, status);"
eval_result = gate.evaluate_with_hypopg(slow_sql, index_suggestion)
print(f"[VIRTUAL INDEX EVALUATION] 评估结果: {json.dumps(eval_result, indent=2)}")
数据库 AI 治理的铁律
不要把 Agent 当成能拯救 DB 性能的万能灵药。在接入 Agent 协助慢查询治理时,建议烙印这三条铁律:
- 零写权限(Zero Write Access)。Agent 拥有的数据库账户只能赋予
SELECT权限,物理上剥夺其ALTER、DROP、CREATE的能力。 - 强制使用虚拟索引(HypoPG / INVISIBLE)进行数据说话。任何索引提议,需要出具权威的 Cost 降幅数据(如 Cost 降低至少 60% 以上),否则直接否决。
- DDL 变更需要交由专门的无锁变更工具(如 gh-ost)执行。即便 Agent 的建议获得了 DBA 的批准,最后的上线动作也需要跑在严格的工具流与低峰期维护窗口中。
执行前最后核对
这里的重点是把假设、观测和改动分开记录。先在隔离环境复现,再带着基线和回滚条件逐步验证;没有对应数据时,只把结论当作排查方向。
更多推荐



所有评论(0)