diff --git a/agent/app/model/backup.go b/agent/app/model/backup.go
index 392bd7e21..e8d21bd18 100644
--- a/agent/app/model/backup.go
+++ b/agent/app/model/backup.go
@@ -31,4 +31,5 @@ type BackupRecord struct {
Status string `json:"status"`
Message string `json:"message"`
Description string `json:"description"`
+ Args string `gorm:"not null;default:''" json:"args"`
}
diff --git a/agent/app/service/backup_mysql.go b/agent/app/service/backup_mysql.go
index 04aa67ab4..187c5c245 100644
--- a/agent/app/service/backup_mysql.go
+++ b/agent/app/service/backup_mysql.go
@@ -2,6 +2,7 @@ package service
import (
"context"
+ "encoding/json"
"fmt"
"os"
"path"
@@ -39,6 +40,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
+ Args: encodeBackupArgs(req.Args),
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
@@ -143,6 +145,14 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
+ var rollbackArgs []string
+ if req.BackupRecordID != 0 {
+ record, err := backupRepo.GetRecord(repo.WithByID(req.BackupRecordID))
+ if err != nil {
+ return err
+ }
+ rollbackArgs = decodeBackupArgs(record.Args)
+ }
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
Type: req.Type,
@@ -150,6 +160,7 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
Format: dbInfo.Format,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
+ Args: rollbackArgs,
}); err != nil {
return fmt.Errorf("backup mysql db %s for rollback before recover failed, err: %v", req.DetailName, err)
}
@@ -242,6 +253,36 @@ func doMysqlBackup(db DatabaseHelper, targetDir, fileName, secret string) error
return nil
}
+func encodeBackupArgs(args []string) string {
+ var items []string
+ for _, arg := range args {
+ if len(arg) != 0 {
+ items = append(items, arg)
+ }
+ }
+ if len(items) == 0 {
+ return ""
+ }
+ data, err := json.Marshal(items)
+ if err != nil {
+ global.LOG.Warnf("marshal backup args failed: %v", err)
+ return ""
+ }
+ return string(data)
+}
+
+func decodeBackupArgs(value string) []string {
+ if len(value) == 0 {
+ return nil
+ }
+ var args []string
+ if err := json.Unmarshal([]byte(value), &args); err != nil {
+ global.LOG.Warnf("unmarshal backup args failed: %v", err)
+ return nil
+ }
+ return args
+}
+
func loadSqlFile(file string) (string, error) {
if !strings.HasSuffix(file, ".tar.gz") && !strings.HasSuffix(file, ".zip") {
return file, nil
diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go
index 9ee51258b..1cd542d15 100644
--- a/agent/app/service/cronjob_backup.go
+++ b/agent/app/service/cronjob_backup.go
@@ -163,6 +163,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
record.Name = dbInfo.Database
record.DetailName = dbInfo.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
+ record.Args = encodeBackupArgs(dbInfo.Args)
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
switch dbInfo.DBType {
diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go
index e6d934dc6..2685d60c2 100644
--- a/agent/init/migration/migrate.go
+++ b/agent/init/migration/migrate.go
@@ -93,6 +93,7 @@ func InitAgentDB() {
migrations.AddMcpServerGatewayArgs,
migrations.InitFirewallPortWhiteList,
migrations.AddDatabaseUserTable,
+ migrations.AddBackupRecordArgs,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go
index f31bda168..874387e7b 100644
--- a/agent/init/migration/migrations/init.go
+++ b/agent/init/migration/migrations/init.go
@@ -1676,3 +1676,10 @@ var AddMcpServerGatewayArgs = &gormigrate.Migration{
return tx.AutoMigrate(&model.McpServer{})
},
}
+
+var AddBackupRecordArgs = &gormigrate.Migration{
+ ID: "20260729-add-backup-record-args",
+ Migrate: func(tx *gorm.DB) error {
+ return tx.AutoMigrate(&model.BackupRecord{})
+ },
+}
diff --git a/frontend/src/components/backup/index.vue b/frontend/src/components/backup/index.vue
index 30d048f3b..cbe2acea4 100644
--- a/frontend/src/components/backup/index.vue
+++ b/frontend/src/components/backup/index.vue
@@ -129,9 +129,9 @@
{{ $t('database.mongodbRecoverDropAllCollectionsHelper') }}
-
+
-
+
{{ item.arg }}
{{ item.description }}
@@ -199,7 +199,7 @@ import { MsgSuccess } from '@/utils/message';
import TaskLog from '@/components/log/task/index.vue';
import { routerToFileWithPath } from '@/utils/router';
import { useGlobalStore } from '@/composables/useGlobalStore';
-import { mysqlArgs } from '@/views/cronjob/cronjob/helper';
+import { loadMysqlArgs } from '@/views/cronjob/cronjob/helper';
const { currentNode } = useGlobalStore();
const emit = defineEmits(['close']);
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 85b3fbe3d..a5ff75a94 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1821,6 +1821,8 @@ const message = {
singleTransaction: 'Backup InnoDB tables using a single transaction, suitable for large-volume data backups',
quick: 'Read data row by row instead of loading the entire table into memory, suitable for large-volume data and low-memory machine backups',
skipLockTables: 'Backup without locking all tables, suitable for highly concurrent databases',
+ setGtidPurgedOff:
+ 'Do not export GTID information, suitable for database recovery in Group Replication environments',
missBackupAccount: 'The backup account could not be found',
syncDate: 'Synchronization time ',
clean: 'Cache clean',
diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts
index ad3bf0958..b63f98211 100644
--- a/frontend/src/lang/modules/es-es.ts
+++ b/frontend/src/lang/modules/es-es.ts
@@ -1855,6 +1855,8 @@ const message = {
quick: 'Lee datos fila por fila en lugar de cargar la tabla completa en memoria, adecuado para copias de seguridad de datos de gran volumen y máquinas con poca memoria',
skipLockTables:
'Copia de seguridad sin bloquear todas las tablas, adecuada para bases de datos altamente concurrentes',
+ setGtidPurgedOff:
+ 'No exportar información GTID, adecuado para restaurar bases de datos en entornos de Group Replication',
missBackupAccount: 'No se pudo encontrar la cuenta de respaldo',
syncDate: 'Hora de sincronización',
clean: 'Limpiar caché',
diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts
index f9b8e6df4..edb07c590 100644
--- a/frontend/src/lang/modules/fa.ts
+++ b/frontend/src/lang/modules/fa.ts
@@ -1810,6 +1810,7 @@ const message = {
'پشتیبانگیری از جداول InnoDB با استفاده از یک تراکنش واحد، مناسب برای پشتیبانگیری دادههای حجیم',
quick: 'خواندن دادهها سطر به سطر به جای بارگذاری کل جدول در حافظه، مناسب برای پشتیبانگیری دادههای حجیم و ماشینهای با حافظه کم',
skipLockTables: 'پشتیبانگیری بدون قفل کردن همه جداول، مناسب برای پایگاههای داده با همروندی بالا',
+ setGtidPurgedOff: 'اطلاعات GTID صادر نشود؛ مناسب برای بازیابی پایگاه داده در محیطهای Group Replication',
missBackupAccount: 'حساب پشتیبان پیدا نشد',
syncDate: 'زمان همگامسازی',
clean: 'پاکسازی کش',
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index 9826fdecd..03dde3e6e 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -1820,6 +1820,8 @@ const message = {
'単一トランザクションを使用して InnoDB テーブルをバックアップします。大容量データのバックアップに適しています',
quick: 'テーブル全体をメモリにロードする代わりに、データを行単位で読み取ります。大容量データや低メモリマシンのバックアップに適しています',
skipLockTables: 'すべてのテーブルをロックせずにバックアップします。高並列データベースに適しています',
+ setGtidPurgedOff:
+ 'バックアップ時に GTID 情報をエクスポートしません。グループレプリケーション環境でのデータベース復元に適しています',
missBackupAccount: 'バックアップアカウントは見つかりませんでした',
syncDate: '同期時間',
clean: 'キャッシュクリーン',
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index a3cf4f732..fe2fc2092 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -1791,6 +1791,7 @@ const message = {
singleTransaction: '단일 트랜잭션을 사용하여 InnoDB 테이블을 백업하며, 대용량 데이터 백업에 적합합니다',
quick: '전체 테이블을 메모리에 로드하는 대신 데이터를 행별로 읽습니다. 대용량 데이터 및 저메모리 시스템 백업에 적합합니다',
skipLockTables: '모든 테이블을 잠그지 않고 백업합니다. 높은 동시성 데이터베이스에 적합합니다',
+ setGtidPurgedOff: '백업 시 GTID 정보를 내보내지 않으며 그룹 복제 환경에서 데이터베이스를 복구할 때 적합합니다',
missBackupAccount: '백업 계정을 찾을 수 없습니다',
syncDate: '동기화 시간',
clean: '캐시 정리',
diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts
index 4cfec0cd9..306ab5bca 100644
--- a/frontend/src/lang/modules/lo.ts
+++ b/frontend/src/lang/modules/lo.ts
@@ -1789,6 +1789,7 @@ const message = {
singleTransaction: 'ສຳຮອງຕາຕະລາງ InnoDB ໂດຍໃຊ້ single transaction, ເໝາະສຳລັບຂໍ້ມູນຂະໜາດໃຫຍ່',
quick: 'ອ່ານຂໍ້ມູນເທື່ອລະແຖວແທນການໂຫຼດທັງໝົດລົງ memory, ເໝາະສຳລັບຂໍ້ມູນໃຫຍ່ ແລະ ເຄື່ອງທີ່ມີ memory ຕ່ຳ',
skipLockTables: 'ສຳຮອງໂດຍບໍ່ລັອກຕາຕະລາງ, ເໝາະສຳລັບຖານຂໍ້ມູນທີ່ມີການໃຊ້ງານສູງ',
+ setGtidPurgedOff: 'ບໍ່ສົ່ງອອກຂໍ້ມູນ GTID, ເໝາະສຳລັບການກູ້ຄືນຖານຂໍ້ມູນໃນສະພາບແວດລ້ອມ Group Replication',
missBackupAccount: 'ກວດບໍ່ພບບັນຊີສຳຮອງຂໍ້ມູນ',
syncDate: 'ເວລາຊິງໂຄໄນ ',
clean: 'ລ້າງ Cache',
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index 92b4fe802..f9a66c423 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -1838,6 +1838,8 @@ const message = {
'Sandaran jadual InnoDB menggunakan transaksi tunggal, sesuai untuk sandaran data isipadu besar',
quick: 'Baca data baris demi baris daripada memuatkan keseluruhan jadual ke dalam ingatan, sesuai untuk sandaran data isipadu besar dan mesin ingatan rendah',
skipLockTables: 'Sandaran tanpa mengunci semua jadual, sesuai untuk pangkalan data konkuren tinggi',
+ setGtidPurgedOff:
+ 'Jangan eksport maklumat GTID, sesuai untuk pemulihan pangkalan data dalam persekitaran Group Replication',
missBackupAccount: 'Akaun sandaran tidak dijumpai',
syncDate: 'Waktu penyelarasan',
clean: 'Bersihkan cache',
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index 1878a1e00..30a91f35e 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -1840,6 +1840,8 @@ const message = {
'Faz backup de tabelas InnoDB usando uma única transação, adequado para backups de dados de grande volume',
quick: 'Lê dados linha por linha em vez de carregar toda a tabela na memória, adequado para backups de dados de grande volume e máquinas com pouca memória',
skipLockTables: 'Backup sem bloquear todas as tabelas, adequado para bancos de dados altamente concorrentes',
+ setGtidPurgedOff:
+ 'Não exportar informações de GTID, adequado para restaurar bancos de dados em ambientes de Group Replication',
missBackupAccount: 'A conta de backup não foi encontrada',
syncDate: 'Data de sincronização',
clean: 'Limpeza de cache',
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index b4e771271..9d5287f0e 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -1827,6 +1827,8 @@ const message = {
'Резервное копирование таблиц InnoDB с использованием одной транзакции, подходит для резервного копирования данных большого объема',
quick: 'Чтение данных построчно вместо загрузки всей таблицы в память, подходит для резервного копирования данных большого объема и машин с низкой памятью',
skipLockTables: 'Резервное копирование без блокировки всех таблиц, подходит для высококонкурентных баз данных',
+ setGtidPurgedOff:
+ 'Не экспортировать информацию GTID, подходит для восстановления базы данных в среде Group Replication',
missBackupAccount: 'Не удалось найти учетную запись резервного копирования',
syncDate: 'Время синхронизации',
clean: 'Очистка кэша',
diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts
index 6c269eff4..f459938b5 100644
--- a/frontend/src/lang/modules/tr.ts
+++ b/frontend/src/lang/modules/tr.ts
@@ -1839,6 +1839,8 @@ const message = {
'InnoDB tablolarını tek bir işlem kullanarak yedekler, büyük hacimli veri yedeklemeleri için uygundur',
quick: 'Tüm tabloyu belleğe yüklemek yerine verileri satır satır okur, büyük hacimli veri ve düşük bellekli makine yedeklemeleri için uygundur',
skipLockTables: 'Tüm tabloları kilitlemeden yedekleme, yüksek eşzamanlılığa sahip veritabanları için uygundur',
+ setGtidPurgedOff:
+ 'GTID bilgilerini dışa aktarmayın; Group Replication ortamlarında veritabanı kurtarma için uygundur',
missBackupAccount: 'Yedekleme hesabı bulunamadı',
syncDate: 'Senkronizasyon zamanı ',
clean: 'Önbellek temizleme',
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index fe4a34bea..0a9ef0bf1 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -1713,6 +1713,7 @@ const message = {
singleTransaction: '使用單一事務備份 InnoDB 表,適用於大資料量的備份',
quick: '逐行讀取資料,而不是將整個表載入到記憶體中,適用於大資料量和低記憶體機器的備份',
skipLockTables: '不鎖定所有表進行備份,適用於高併發的資料庫',
+ setGtidPurgedOff: '備份時不匯出 GTID 資訊,適用於群組複寫環境中的資料庫還原',
missBackupAccount: '未能找到備份帳號',
syncDate: '同步時間 ',
clean: '快取清理',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index f461171e0..4ea2e1959 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -1723,6 +1723,7 @@ const message = {
singleTransaction: '使用单一事务备份 InnoDB 表,适用于大数据量的备份',
quick: '逐行读取数据,而不是将整个表加载到内存中,适用于大数据量和低内存机器的备份',
skipLockTables: '不锁定所有表进行备份,适用于高并发的数据库',
+ setGtidPurgedOff: '备份时不导出 GTID 信息,适用于组复制环境中的数据库恢复',
missBackupAccount: '未能找到备份账号',
syncDate: '同步时间 ',
clean: '缓存清理',
diff --git a/frontend/src/views/cronjob/cronjob/helper.ts b/frontend/src/views/cronjob/cronjob/helper.ts
index 46ae342fa..85465e4f8 100644
--- a/frontend/src/views/cronjob/cronjob/helper.ts
+++ b/frontend/src/views/cronjob/cronjob/helper.ts
@@ -24,7 +24,15 @@ export const mysqlArgs = [
{ arg: '--single-transaction', description: i18n.global.t('cronjob.singleTransaction') },
{ arg: '--quick', description: i18n.global.t('cronjob.quick') },
{ arg: '--skip-lock-tables', description: i18n.global.t('cronjob.skipLockTables') },
+ {
+ arg: '--set-gtid-purged=OFF',
+ description: i18n.global.t('cronjob.setGtidPurgedOff'),
+ supportedTypes: ['mysql', 'mysql-cluster'],
+ },
];
+export const loadMysqlArgs = (dbType: string) => {
+ return mysqlArgs.filter((item) => !item.supportedTypes || item.supportedTypes.includes(dbType));
+};
function loadWeek(i: number) {
for (const week of weekOptions) {
if (week.value === i) {
diff --git a/frontend/src/views/cronjob/cronjob/operate/index.vue b/frontend/src/views/cronjob/cronjob/operate/index.vue
index c45946e99..131f25f84 100644
--- a/frontend/src/views/cronjob/cronjob/operate/index.vue
+++ b/frontend/src/views/cronjob/cronjob/operate/index.vue
@@ -356,7 +356,7 @@