package business import ( "fmt" "strings" "gorm.io/gorm" ) type businessQueryIndex struct { table, name string columns []string } var businessQueryIndexes = []businessQueryIndex{ {"tab_promote", "idx_sw_promote_created", []string{"source_db", "create_time", "id"}}, {"tab_spend", "idx_sw_spend_time_summary", []string{"pay_status", "pay_time", "source_db", "pay_amount"}}, {"tab_spend", "idx_sw_spend_promote_summary", []string{"promote_id", "pay_status", "source_db", "pay_amount"}}, {"tab_sw_report_day", "idx_sw_report_pay_scan", []string{"dedup_mode", "stat_date", "source_db", "promote_id", "game_id", "total_pay"}}, {"tab_sw_report_day_key", "idx_sw_report_key_scan", []string{"dedup_mode", "stat_date", "source_db", "promote_id", "game_id", "metric", "identity_key"}}, {"tab_sw_report_server_day", "idx_sw_server_pay_game_scan", []string{"game_name", "dedup_mode", "stat_date", "source_db", "game_id", "server_num", "total_pay"}}, {"tab_sw_report_server_day_key", "idx_sw_server_key_game_scan", []string{"game_name", "dedup_mode", "stat_date", "source_db", "game_id", "server_num", "metric", "identity_key"}}, } var reportBuildQueryIndexes = []businessQueryIndex{ {"tab_user", "idx_sw_user_report_identity", []string{"id", "source_db", "puid", "promote_id", "idcard"}}, {"tab_user_play_info", "idx_sw_play_report_active", []string{"play_time", "user_id", "source_db", "promote_id", "game_id", "server_id"}}, } // 只为已经接入的业务表增加索引,不创建平台表、不改写记录。 // 按实际筛选前缀排列,并覆盖聚合读取列,避免大范围回表。 func migrateBusinessQueryIndexes(db *gorm.DB) error { return migrateQueryIndexes(db, businessQueryIndexes) } func migrateReportBuildQueryIndexes(db *gorm.DB) error { return migrateQueryIndexes(db, reportBuildQueryIndexes) } func migrateQueryIndexes(db *gorm.DB, indexes []businessQueryIndex) error { if db.Dialector.Name() != "mysql" { return nil } groups := make(map[string][]businessQueryIndex) tables := []string{} for _, index := range indexes { if _, exists := groups[index.table]; !exists { tables = append(tables, index.table) } groups[index.table] = append(groups[index.table], index) } for _, table := range tables { available, err := businessTableAvailable(db, table) if err != nil { return err } if !available { continue } additions := []string{} for _, index := range groups[table] { var existing []string if err := db.Raw("SELECT column_name FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name=? AND index_name=? ORDER BY seq_in_index", index.table, index.name).Scan(&existing).Error; err != nil { return err } if len(existing) > 0 { if strings.Join(existing, ",") != strings.Join(index.columns, ",") { return fmt.Errorf("%s.%s 的现有索引列不符", index.table, index.name) } continue } columns := "`" + strings.Join(index.columns, "`,`") + "`" additions = append(additions, "ADD INDEX `"+index.name+"` ("+columns+")") } // 同一大表的多个索引合并构建,减少重复扫描。 if len(additions) > 0 { build := func(conn *gorm.DB) error { return conn.Exec("ALTER TABLE `" + table + "` " + strings.Join(additions, ",") + ", ALGORITHM=INPLACE, LOCK=NONE").Error } // Later indexes also evaluate the historical generated time column. // Apply the same connection-local compatibility rule as its first index. if table == "tab_user" && db.Migrator().HasColumn(table, "sw_register_time_unix") { err = withLegacyRegistrationCast(db, build) } else { err = build(db) } if err != nil { return fmt.Errorf("add query indexes on %s: %w", table, err) } } } return nil }