feat: Native DDL智能推荐 + MySQL版本检测 + 云数据库自动跳过gh-ost

- 新增 getMySQLVersion(): 检测MySQL实例版本
- 新增 supportsInstantDDL(): 判断是否支持 INSTANT DDL (8.0.12+)
- analyze API新增字段: mysql_version, supports_instant, cloud_type
- 云数据库(aliyun_rds/polardb/aws_aurora): 自动推荐Native DDL,跳过gh-ost
- 自建MySQL: 根据版本+环境+行数综合推荐
- MySQL 8.4.9 检测通过, supports_instant=true
This commit is contained in:
logan committed 2026-05-27 00:24:59 +08:00
1 parent b2b13661fd
commit 78986fe5bf
1 file changed
+77 -14
+77 -14
View File
@@ -143,6 +143,49 @@ func GhOstGetConfig(c *gin.Context) {
c.JSON(http.StatusOK, getGhostConfig())
}
// getMySQLVersion returns the MySQL version string for an instance
func getMySQLVersion(instanceID int) string {
var inst model.Instance
if model.DB.First(&inst, instanceID).Error != nil {
return ""
}
if inst.DBType != "mysql" {
return ""
}
db, err := getDBConnection(&inst, "")
if err != nil {
return ""
}
defer db.Close()
var version string
db.QueryRow("SELECT VERSION()").Scan(&version)
return version
}
// supportsInstantDDL checks if MySQL version supports INSTANT DDL (8.0.12+)
func supportsInstantDDL(version string) bool {
// Parse "8.0.32" or "5.7.40-log" etc
parts := strings.SplitN(version, "-", 2)
v := parts[0]
segments := strings.Split(v, ".")
if len(segments) < 3 {
return false
}
major, _ := strconv.Atoi(segments[0])
minor, _ := strconv.Atoi(segments[1])
patch, _ := strconv.Atoi(segments[2])
if major > 8 {
return true
}
if major == 8 && minor > 0 {
return true
}
if major == 8 && minor == 0 && patch >= 12 {
return true
}
return false
}
// POST /api/v1/ghost/analyze — analyze DDL and return OSC recommendation
func GhOstAnalyzeDDL(c *gin.Context) {
var req struct {
@@ -158,29 +201,49 @@ func GhOstAnalyzeDDL(c *gin.Context) {
analysis := analyzeDDL(req.Alter)
// Get MySQL version for INSTANT DDL check
mysqlVersion := ""
instantDDL := false
if req.InstanceID > 0 {
mysqlVersion = getMySQLVersion(req.InstanceID)
instantDDL = supportsInstantDDL(mysqlVersion)
}
// Cloud databases: prefer Native DDL
cloudType := getSetting("ghost_cloud_type", "self_hosted")
if cloudType != "self_hosted" && analysis.NeedsOSC {
analysis.NeedsOSC = false
analysis.RecommendedTool = "native"
analysis.Reason = fmt.Sprintf("云数据库(%s)建议使用原生DDL,无需gh-ost", cloudType)
}
// Also check table row count if instance_id provided
rowCount := -1
if req.InstanceID > 0 {
rowCount = getTableRowCount(req.InstanceID, req.Database, req.Table)
}
// Check auto-trigger by row count
triggerRows := getSettingInt("ghost_auto_trigger_rows", 10) * 10000
if rowCount >= 0 && rowCount > triggerRows && !analysis.NeedsOSC {
analysis.NeedsOSC = true
analysis.RecommendedTool = "gh-ost"
analysis.Reason = fmt.Sprintf("表行数(%d)超过阈值(%d),推荐使用 gh-ost", rowCount, triggerRows)
// Check auto-trigger by row count (only for self-hosted)
if cloudType == "self_hosted" {
triggerRows := getSettingInt("ghost_auto_trigger_rows", 10) * 10000
if rowCount >= 0 && rowCount > triggerRows && !analysis.NeedsOSC {
analysis.NeedsOSC = true
analysis.RecommendedTool = "gh-ost"
analysis.Reason = fmt.Sprintf("表行数(%d)超过阈值(%d),推荐使用 gh-ost", rowCount, triggerRows)
}
}
c.JSON(http.StatusOK, gin.H{
"enabled": isGhostEnabled(),
"ddl_types": analysis.DDLTypes,
"needs_osc": analysis.NeedsOSC,
"recommended_tool": analysis.RecommendedTool,
"reason": analysis.Reason,
"table_rows": rowCount,
"trigger_rows": triggerRows,
"config": getGhostConfig(),
"enabled": isGhostEnabled(),
"ddl_types": analysis.DDLTypes,
"needs_osc": analysis.NeedsOSC,
"recommended_tool": analysis.RecommendedTool,
"reason": analysis.Reason,
"table_rows": rowCount,
"mysql_version": mysqlVersion,
"supports_instant": instantDDL,
"cloud_type": cloudType,
"config": getGhostConfig(),
})
}