金色时刻 · 15:48
让好资源,触手可及
AGENT SKILL

06-database-script-management

06-database-script-management 是面向数据库的 Agent Skill,来自 TencentBlueKing/bk-ci。查看技能说明、操作步骤与使用约定,并通过原项目了解详细用法。

数据库脚本管理规范,涵盖 DDL/DML 脚本编写、版本命名规则、增量更新策略、数据迁移、回滚方案。当用户编写数据库脚本、新增表结构、修改字段、进行数据迁移或管理 SQL 版本时使用。

SKILL.md

下载 MD ↓
name06-database-script-management
description数据库脚本管理规范,涵盖 DDL/DML 脚本编写、版本命名规则、增量更新策略、数据迁移、回滚方案。当用户编写数据库脚本、新增表结构、修改字段、进行数据迁移或管理 SQL 版本时使用。

Skill 06: 数据库脚本管理

概述

BK-CI 使用 MySQL/MariaDB 作为主数据库,SQL 脚本按照严格的命名规范和版本管理策略组织。

数据库支持

数据库 版本要求
MySQL 5.7.2+
MariaDB 10.x+

SQL 文件位置

support-files/sql/
├── 0001_ci_create-database_mysql.sql      # 创建所有数据库
├── 1001_ci_project_ddl_mysql.sql          # Project模块完整DDL
├── 1002_ci_process_ddl_mysql.sql          # Process模块完整DDL
├── 2003_v2.x/                             # v2.x版本增量更新
├── 2004_v3.x/                             # v3.x版本增量更新
├── 2025_v4.x/                             # v4.x版本增量更新
└── 5001_init_dml/                         # 初始化数据

文件命名规范

创建数据库脚本

0001_{系统}_create-database_{db类型}.sql

示例:0001_ci_create-database_mysql.sql

全版本 DDL 脚本

1xxx_{系统}_{模块}_ddl_{db类型}.sql

示例: - 1001_ci_project_ddl_mysql.sql - 1002_ci_process_ddl_mysql.sql - 1003_ci_repository_ddl_mysql.sql

增量版本更新脚本

2xxx_{系统}_{模块}_update_{版本号}_{db类型}.sql

示例: - 2003_ci_process_update_v2.0.0_mysql.sql - 2004_ci_auth_update_v3.0.0_mysql.sql

⚠️ 重要:双轨更新机制

当在增量脚本中添加/修改表结构时,必须同步更新对应的全版本 DDL 脚本!

初始化数据脚本

5001_{系统}_{模块}_dml_{db类型}.sql

示例:5001_ci_project_dml_mysql.sql

双轨更新机制(⚠️ 重要)

核心原则

任何表结构变更(DDL)必须在两个位置同步更新:

  1. 增量版本更新脚本(2xxx_*_update_*.sql)- 用于现有环境升级
  2. 全版本 DDL 脚本(1xxx_*_ddl_*.sql)- 用于全新安装

为什么需要双轨更新?

  • 增量脚本:负责将已部署的旧版本数据库升级到新版本
  • 全量脚本:负责全新安装时直接创建最新的完整表结构

如果只更新增量脚本,全新安装时会缺少对应的字段/索引!

标准变更流程

假设需要为 T_PROJECT 表添加索引 IDX_PROJECT_NAME:

步骤 1:更新增量脚本

文件:support-files/sql/2025_v4.x/2040_ci_project-update_v4.1_mysql.sql

USE devops_ci_project;
SET NAMES utf8mb4;

DROP PROCEDURE IF EXISTS ci_project_schema_update;

DELIMITER <CI_UBF>

CREATE PROCEDURE ci_project_schema_update()
BEGIN
    DECLARE db VARCHAR(100);
    SET AUTOCOMMIT = 0;
    SELECT DATABASE() INTO db;

    -- 添加索引
    IF NOT EXISTS(SELECT 1
                  FROM information_schema.STATISTICS
                  WHERE TABLE_SCHEMA = db
                    AND TABLE_NAME = 'T_PROJECT'
                    AND INDEX_NAME = 'IDX_PROJECT_NAME') THEN
        ALTER TABLE `T_PROJECT` ADD INDEX `IDX_PROJECT_NAME` (`PROJECT_NAME`);
    END IF;

    COMMIT;
END <CI_UBF>
DELIMITER ;

CALL ci_project_schema_update();

步骤 2:同步更新全量 DDL 脚本

文件:support-files/sql/1001_ci_project_ddl_mysql.sql

找到 T_PROJECT 表的建表语句,在索引定义区域添加:

CREATE TABLE IF NOT EXISTS `T_PROJECT` (
    `ID` bigint(20) NOT NULL AUTO_INCREMENT,
    `PROJECT_ID` varchar(64) NOT NULL,
    `PROJECT_NAME` varchar(256) NOT NULL,
    -- ... 其他字段
    PRIMARY KEY (`ID`),
    UNIQUE KEY `UNI_PROJECT_ID` (`PROJECT_ID`),
    KEY `IDX_PROJECT_NAME` (`PROJECT_NAME`)  -- ✅ 新增索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

常见变更类型示例

变更类型 增量脚本操作 全量脚本操作
添加列 ALTER TABLE ADD COLUMN 在 CREATE TABLE 中添加字段定义
添加索引 ALTER TABLE ADD INDEX 在 CREATE TABLE 的 KEY 区域添加
修改列 ALTER TABLE MODIFY COLUMN 修改 CREATE TABLE 中的字段定义
添加唯一约束 ALTER TABLE ADD UNIQUE KEY 在 CREATE TABLE 的 UNIQUE KEY 区域添加
添加外键 ALTER TABLE ADD CONSTRAINT 在 CREATE TABLE 的 CONSTRAINT 区域添加

检查清单

在提交数据库变更前,确认:

  • [ ] 增量脚本已创建(2xxx_*_update_*.sql)
  • [ ] 增量脚本具备幂等性(可重复执行)
  • [ ] 全量 DDL 脚本已同步更新(1xxx_*_ddl_*.sql)
  • [ ] 两处的字段定义、类型、默认值完全一致
  • [ ] 索引名称、类型在两处保持一致
  • [ ] 本地测试通过(全新安装 + 升级安装)

幂等性要求

所有 SQL 脚本必须支持重复执行(幂等性):

创建表

-- ✅ 正确:使用 IF NOT EXISTS
CREATE TABLE IF NOT EXISTS `T_PROJECT` (
    `ID` bigint(20) NOT NULL AUTO_INCREMENT,
    `PROJECT_ID` varchar(64) NOT NULL,
    `PROJECT_NAME` varchar(256) NOT NULL,
    PRIMARY KEY (`ID`),
    UNIQUE KEY `UNI_PROJECT_ID` (`PROJECT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ❌ 错误:直接创建
CREATE TABLE `T_PROJECT` (...);

添加列

-- ✅ 正确:使用存储过程检查
DELIMITER $$
CREATE PROCEDURE IF NOT EXISTS add_column_if_not_exists()
BEGIN
    IF NOT EXISTS (
        SELECT * FROM information_schema.COLUMNS 
        WHERE TABLE_SCHEMA = DATABASE() 
        AND TABLE_NAME = 'T_PROJECT' 
        AND COLUMN_NAME = 'NEW_COLUMN'
    ) THEN
        ALTER TABLE `T_PROJECT` ADD COLUMN `NEW_COLUMN` varchar(64) DEFAULT NULL;
    END IF;
END$$
DELIMITER ;
CALL add_column_if_not_exists();
DROP PROCEDURE IF EXISTS add_column_if_not_exists;

-- ❌ 错误:直接添加
ALTER TABLE `T_PROJECT` ADD COLUMN `NEW_COLUMN` varchar(64);

插入数据

-- ✅ 正确:使用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE
INSERT IGNORE INTO `T_CONFIG` (`KEY`, `VALUE`) VALUES ('config_key', 'config_value');

INSERT INTO `T_CONFIG` (`KEY`, `VALUE`) VALUES ('config_key', 'config_value')
ON DUPLICATE KEY UPDATE `VALUE` = VALUES(`VALUE`);

-- ❌ 错误:直接插入
INSERT INTO `T_CONFIG` (`KEY`, `VALUE`) VALUES ('config_key', 'config_value');

创建索引

-- ✅ 正确:使用存储过程检查
DELIMITER $$
CREATE PROCEDURE IF NOT EXISTS add_index_if_not_exists()
BEGIN
    IF NOT EXISTS (
        SELECT * FROM information_schema.STATISTICS 
        WHERE TABLE_SCHEMA = DATABASE() 
        AND TABLE_NAME = 'T_PROJECT' 
        AND INDEX_NAME = 'IDX_PROJECT_NAME'
    ) THEN
        ALTER TABLE `T_PROJECT` ADD INDEX `IDX_PROJECT_NAME` (`PROJECT_NAME`);
    END IF;
END$$
DELIMITER ;
CALL add_index_if_not_exists();
DROP PROCEDURE IF EXISTS add_index_if_not_exists;

⚠️ 提醒:添加索引后,别忘了同步更新 1xxx_*_ddl_mysql.sql 中的建表语句!

禁止操作

  1. ❌ 删除后重建表:可能导致数据丢失
  2. ❌ 添加非空无默认值字段:会导致现有数据插入失败
  3. ❌ 直接修改字段类型:可能导致数据截断
  4. ❌ 删除字段:需要充分评估影响

表命名规范

前缀 含义 示例
T_ 业务表 T_PROJECT, T_PIPELINE
T_AUDIT_ 审计表 T_AUDIT_RESOURCE
T_HISTORY_ 历史表 T_HISTORY_BUILD

字段命名规范

字段 类型 说明
ID bigint(20) 主键自增
PROJECT_ID varchar(64) 项目ID
PIPELINE_ID varchar(34) 流水线ID
BUILD_ID varchar(34) 构建ID
CREATE_TIME datetime(3) 创建时间
UPDATE_TIME datetime(3) 更新时间
CREATOR varchar(64) 创建人
MODIFIER varchar(64) 修改人

索引命名规范

类型 命名格式 示例
主键 PRIMARY PRIMARY KEY (ID)
唯一索引 UNI_{字段} UNIQUE KEY UNI_PROJECT_ID
普通索引 IDX_{字段} INDEX IDX_CREATE_TIME
联合索引 IDX_{字段1}_{字段2} INDEX IDX_PROJECT_PIPELINE

完整建表示例

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

CREATE TABLE IF NOT EXISTS `T_PIPELINE_INFO` (
    `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `PROJECT_ID` varchar(64) NOT NULL COMMENT '项目ID',
    `PIPELINE_ID` varchar(34) NOT NULL COMMENT '流水线ID',
    `PIPELINE_NAME` varchar(255) NOT NULL COMMENT '流水线名称',
    `PIPELINE_DESC` varchar(1024) DEFAULT NULL COMMENT '流水线描述',
    `VERSION` int(11) NOT NULL DEFAULT '1' COMMENT '版本号',
    `CREATE_TIME` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
    `UPDATE_TIME` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
    `CREATOR` varchar(64) NOT NULL DEFAULT 'system' COMMENT '创建人',
    `MODIFIER` varchar(64) NOT NULL DEFAULT 'system' COMMENT '修改人',
    `DELETE` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    PRIMARY KEY (`ID`),
    UNIQUE KEY `UNI_PROJECT_PIPELINE` (`PROJECT_ID`, `PIPELINE_ID`),
    KEY `IDX_PROJECT_ID` (`PROJECT_ID`),
    KEY `IDX_CREATE_TIME` (`CREATE_TIME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流水线信息表';

SET FOREIGN_KEY_CHECKS = 1;

JOOQ 集成

BK-CI 使用 JOOQ 作为数据库访问层,禁止手写 SQL:

// ✅ 正确:使用 JOOQ DSL
dslContext.selectFrom(T_PIPELINE_INFO)
    .where(T_PIPELINE_INFO.PROJECT_ID.eq(projectId))
    .and(T_PIPELINE_INFO.DELETE.eq(false))
    .fetch()

// ❌ 错误:手写 SQL
dslContext.fetch("SELECT * FROM T_PIPELINE_INFO WHERE PROJECT_ID = ?", projectId)

常见错误案例

❌ 错误:仅更新增量脚本

问题场景:开发者只在增量脚本中添加索引,忘记更新全量 DDL

-- 文件:2025_v4.x/2040_ci_project-update_v4.1_mysql.sql
ALTER TABLE `T_PROJECT` ADD INDEX `IDX_PROJECT_NAME` (`PROJECT_NAME`);

后果: - 旧环境升级 ✅ 成功(执行了增量脚本) - 全新安装 ❌ 失败(缺少索引,查询性能问题)

正确做法:同步更新 1001_ci_project_ddl_mysql.sql


❌ 错误:两处定义不一致

增量脚本:

ALTER TABLE `T_PROJECT` ADD COLUMN `NEW_FIELD` varchar(64) DEFAULT NULL;

全量脚本:

`NEW_FIELD` varchar(128) DEFAULT ''  -- ⚠️ 长度和默认值不一致!

后果:升级环境和全新安装的字段定义不同,可能导致数据截断或业务逻辑错误。

正确做法:保持两处完全一致。


✅ 正确示例:完整的双轨更新

需求:为 T_PIPELINE_INFO 添加 TAGS 字段和索引

步骤 1:增量脚本

文件:2025_v4.x/2040_ci_process-update_v4.1_mysql.sql

USE devops_ci_process;
SET NAMES utf8mb4;

DROP PROCEDURE IF EXISTS ci_process_schema_update;

DELIMITER <CI_UBF>

CREATE PROCEDURE ci_process_schema_update()
BEGIN
    DECLARE db VARCHAR(100);
    SET AUTOCOMMIT = 0;
    SELECT DATABASE() INTO db;

    -- 添加字段
    IF NOT EXISTS(SELECT 1
                  FROM information_schema.COLUMNS
                  WHERE TABLE_SCHEMA = db
                    AND TABLE_NAME = 'T_PIPELINE_INFO'
                    AND COLUMN_NAME = 'TAGS') THEN
        ALTER TABLE `T_PIPELINE_INFO` 
        ADD COLUMN `TAGS` varchar(1024) DEFAULT NULL COMMENT '流水线标签';
    END IF;

    -- 添加索引
    IF NOT EXISTS(SELECT 1
                  FROM information_schema.STATISTICS
                  WHERE TABLE_SCHEMA = db
                    AND TABLE_NAME = 'T_PIPELINE_INFO'
                    AND INDEX_NAME = 'IDX_TAGS') THEN
        ALTER TABLE `T_PIPELINE_INFO` ADD INDEX `IDX_TAGS` (`TAGS`(255));
    END IF;

    COMMIT;
END <CI_UBF>
DELIMITER ;

CALL ci_process_schema_update();

步骤 2:全量脚本

文件:1001_ci_process_ddl_mysql.sql

CREATE TABLE IF NOT EXISTS `T_PIPELINE_INFO` (
    `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `PROJECT_ID` varchar(64) NOT NULL COMMENT '项目ID',
    `PIPELINE_ID` varchar(34) NOT NULL COMMENT '流水线ID',
    `PIPELINE_NAME` varchar(255) NOT NULL COMMENT '流水线名称',
    `TAGS` varchar(1024) DEFAULT NULL COMMENT '流水线标签',  -- ✅ 新增字段
    -- ... 其他字段
    PRIMARY KEY (`ID`),
    UNIQUE KEY `UNI_PROJECT_PIPELINE` (`PROJECT_ID`, `PIPELINE_ID`),
    KEY `IDX_PROJECT_ID` (`PROJECT_ID`),
    KEY `IDX_TAGS` (`TAGS`(255))  -- ✅ 新增索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流水线信息表';

验证:

# 测试升级场景
docker exec -it mysql mysql -u root -p devops_ci_process < 2040_ci_process-update_v4.1_mysql.sql

# 测试全新安装
docker exec -it mysql mysql -u root -p devops_ci_process < 1001_ci_process_ddl_mysql.sql

# 对比两种方式的表结构是否一致
docker exec -it mysql mysql -u root -p -e "SHOW CREATE TABLE devops_ci_process.T_PIPELINE_INFO\G"

数据库分库

BK-CI 支持数据库分库,通过配置可以进行水平扩展:

# 分库配置示例
spring:
  datasource:
    process:
      url: jdbc:mysql://host1:3306/devops_ci_process
    project:
      url: jdbc:mysql://host2:3306/devops_ci_project

来源:TencentBlueKing/bk-ci · MIT 许可 · 许可原文 · MD 为来源页保存的历史版本