mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 19:19:15 +08:00
feat: 完成 aof 备份恢复功能
This commit is contained in:
parent
917a11457e
commit
a111e04c65
@ -1,83 +1,37 @@
|
||||
|
||||
[client]
|
||||
port = 3306
|
||||
socket = /var/run/mysqld/mysqld.sock
|
||||
|
||||
[mysqld_safe]
|
||||
socket = /var/run/mysqld/mysqld.sock
|
||||
nice = 0
|
||||
# For advice on how to change settings please see
|
||||
# http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html
|
||||
|
||||
[mysqld]
|
||||
user = mysql
|
||||
pid-file = /var/run/mysqld/mysqld.pid
|
||||
socket = /var/run/mysqld/mysqld.sock
|
||||
port = 3306
|
||||
basedir = /usr
|
||||
datadir = /var/lib/mysql
|
||||
tmpdir = /tmp
|
||||
lc-messages-dir = /usr/share/mysql
|
||||
skip-external-locking
|
||||
skip-character-set-client-handshake
|
||||
default-storage-engine = InnoDB
|
||||
character-set-server = utf8
|
||||
transaction-isolation = READ-COMMITTED
|
||||
#
|
||||
# Remove leading # and set to the amount of RAM for the most important data
|
||||
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
|
||||
# innodb_buffer_pool_size = 128M
|
||||
#
|
||||
# Remove leading # to turn on a very important data integrity option: logging
|
||||
# changes to the binary log between backups.
|
||||
# log_bin
|
||||
#
|
||||
# Remove leading # to set options mainly useful for reporting servers.
|
||||
# The server defaults are faster for transactions and fast SELECTs.
|
||||
# Adjust sizes as needed, experiment to find the optimal values.
|
||||
# join_buffer_size = 128M
|
||||
# sort_buffer_size = 2M
|
||||
# read_rnd_buffer_size = 2M
|
||||
skip-host-cache
|
||||
skip-name-resolve
|
||||
datadir=/var/lib/mysql
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
secure-file-priv=/var/lib/mysql-files
|
||||
user=mysql
|
||||
|
||||
# Disabling symbolic-links is recommended to prevent assorted security risks
|
||||
symbolic-links=0
|
||||
|
||||
bind-address = 127.0.0.1
|
||||
key_buffer = 16M
|
||||
max_allowed_packet = 16M
|
||||
thread_stack = 192K
|
||||
thread_cache_size = 16
|
||||
myisam-recover = BACKUP
|
||||
max_connections = 300
|
||||
table_open_cache = 64
|
||||
thread_concurrency = 10
|
||||
table_open_cache = 32
|
||||
thread_concurrency = 4
|
||||
#log-error=/var/log/mysqld.log
|
||||
pid-file=/var/run/mysqld/mysqld.pid
|
||||
|
||||
query_cache_type = 1
|
||||
query_cache_limit = 1M
|
||||
query_cache_size = 8M
|
||||
general_log_file = /var/log/mysql/mysql.log
|
||||
#general_log = 1
|
||||
log_error = /var/log/mysql/error.log
|
||||
[client]
|
||||
socket=/var/run/mysqld/mysqld.sock
|
||||
|
||||
slow_query_log = 1
|
||||
slow_query_log_file = /var/log/mysql/mysql-slow.log
|
||||
long_query_time = 1
|
||||
#log-queries-not-using-indexes
|
||||
|
||||
|
||||
#server-id = 1
|
||||
#log_bin = /var/log/mysql/mysql-bin.log
|
||||
expire_logs_days = 14
|
||||
max_binlog_size = 1G
|
||||
#binlog_do_db = include_database_name
|
||||
#binlog_ignore_db = include_database_name
|
||||
|
||||
|
||||
# ssl-ca=/etc/mysql/cacert.pem
|
||||
# ssl-cert=/etc/mysql/server-cert.pem
|
||||
# ssl-key=/etc/mysql/server-key.pem
|
||||
innodb_data_file_path = ibdata1:128M:autoextend
|
||||
innodb_file_per_table = 1
|
||||
skip-innodb_doublewrite
|
||||
innodb_additional_mem_pool_size = 12M
|
||||
innodb_buffer_pool_size = 256M
|
||||
innodb_log_buffer_size = 8M
|
||||
innodb_log_file_size = 8M
|
||||
innodb_flush_log_at_trx_commit = 0
|
||||
innodb_flush_method = O_DIRECT
|
||||
innodb_support_xa = OFF
|
||||
|
||||
|
||||
[mysqldump]
|
||||
quick
|
||||
quote-names
|
||||
max_allowed_packet = 16M
|
||||
|
||||
[mysql]
|
||||
#no-auto-rehash # faster start of mysql but no tab completition
|
||||
|
||||
[isamchk]
|
||||
key_buffer = 16M
|
||||
!includedir /etc/mysql/conf.d/
|
||||
!includedir /etc/mysql/mysql.conf.d/
|
@ -48,16 +48,19 @@ func (b *BaseApi) UpdateMysql(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
|
||||
var req dto.MysqlVariablesUpdate
|
||||
var req []dto.MysqlVariablesUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
|
||||
mysqlName, ok := c.Params.Get("mysqlName")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error mysqlName in path"))
|
||||
return
|
||||
}
|
||||
if err := mysqlService.UpdateVariables(req); err != nil {
|
||||
|
||||
if err := mysqlService.UpdateVariables(mysqlName, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@ -78,26 +78,15 @@ type MysqlVariables struct {
|
||||
TableOpenCache string `json:"table_open_cache"`
|
||||
ThreadCacheSize string `json:"thread_cache_size"`
|
||||
ThreadStack string `json:"thread_stack"`
|
||||
Tmp_tableSize string `json:"tmp_table_size"`
|
||||
TmpTableSize string `json:"tmp_table_size"`
|
||||
|
||||
SlowQueryLog string `json:"slow_query_log"`
|
||||
LongQueryTime string `json:"long_query_time"`
|
||||
}
|
||||
|
||||
type MysqlVariablesUpdate struct {
|
||||
MysqlName string `json:"mysqlName" validate:"required"`
|
||||
KeyBufferSize int64 `json:"key_buffer_size" validate:"required"`
|
||||
QueryCacheSize int64 `json:"query_cache_size" validate:"required"`
|
||||
TmpTableSize int64 `json:"tmp_table_size" validate:"required"`
|
||||
InnodbBufferPoolSize int64 `json:"innodb_buffer_pool_size" validate:"required"`
|
||||
InnodbLogBufferSize int64 `json:"innodb_log_buffer_size" validate:"required"`
|
||||
SortBufferSize int64 `json:"sort_buffer_size" validate:"required"`
|
||||
ReadBufferSize int64 `json:"read_buffer_size" validate:"required"`
|
||||
|
||||
ReadRndBufferSize int64 `json:"read_rnd_buffer_size" validate:"required"`
|
||||
JoinBufferSize int64 `json:"join_buffer_size" validate:"required"`
|
||||
ThreadStack int64 `json:"thread_stack" validate:"required"`
|
||||
BinlogCachSize int64 `json:"binlog_cache_size" validate:"required"`
|
||||
ThreadCacheSize int64 `json:"thread_cache_size" validate:"required"`
|
||||
TableOpenCache int64 `json:"table_open_cache" validate:"required"`
|
||||
MaxConnections int64 `json:"max_connections" validate:"required"`
|
||||
Param string `json:"param"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
type MysqlConfUpdateByFile struct {
|
||||
MysqlName string `json:"mysqlName" validate:"required"`
|
||||
@ -112,11 +101,12 @@ type ChangeDBInfo struct {
|
||||
}
|
||||
|
||||
type DBBaseInfo struct {
|
||||
Name string `json:"name"`
|
||||
Port int64 `json:"port"`
|
||||
Password string `json:"password"`
|
||||
RemoteConn bool `json:"remoteConn"`
|
||||
MysqlKey string `json:"mysqlKey"`
|
||||
Name string `json:"name"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Port int64 `json:"port"`
|
||||
Password string `json:"password"`
|
||||
RemoteConn bool `json:"remoteConn"`
|
||||
MysqlKey string `json:"mysqlKey"`
|
||||
}
|
||||
|
||||
type SearchDBWithPage struct {
|
||||
|
@ -205,3 +205,18 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleUnTar(sourceFile, targetDir string) error {
|
||||
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("tar", "zxvfC", sourceFile, targetDir)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -14,6 +16,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
@ -27,7 +30,7 @@ type IMysqlService interface {
|
||||
SearchBackupsWithPage(search dto.SearchBackupsWithPage) (int64, interface{}, error)
|
||||
Create(mysqlDto dto.MysqlDBCreate) error
|
||||
ChangeInfo(info dto.ChangeDBInfo) error
|
||||
UpdateVariables(variables dto.MysqlVariablesUpdate) error
|
||||
UpdateVariables(mysqlName string, updatas []dto.MysqlVariablesUpdate) error
|
||||
|
||||
Backup(db dto.BackupDB) error
|
||||
Recover(db dto.RecoverDB) error
|
||||
@ -265,53 +268,41 @@ func (u *MysqlService) ChangeInfo(info dto.ChangeDBInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateVariables(variables dto.MysqlVariablesUpdate) error {
|
||||
app, err := mysqlRepo.LoadBaseInfoByName(variables.MysqlName)
|
||||
func (u *MysqlService) UpdateVariables(mysqlName string, updatas []dto.MysqlVariablesUpdate) error {
|
||||
app, err := mysqlRepo.LoadBaseInfoByName(mysqlName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var files []string
|
||||
|
||||
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, app.Key, app.Name)
|
||||
lineBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
files = strings.Split(string(lineBytes), "\n")
|
||||
}
|
||||
group := ""
|
||||
for _, info := range updatas {
|
||||
switch info.Param {
|
||||
case "key_buffer_size", "sort_buffer_size":
|
||||
group = "[myisamchk]"
|
||||
default:
|
||||
group = "[mysqld]"
|
||||
}
|
||||
files = updateMyCnf(files, group, info.Param, info.Value)
|
||||
}
|
||||
file, err := os.OpenFile(path, os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = file.WriteString(strings.Join(files, "\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL key_buffer_size=%d", variables.KeyBufferSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL query_cache_size=%d", variables.QueryCacheSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL tmp_table_size=%d", variables.TmpTableSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL innodb_buffer_pool_size=%d", variables.InnodbBufferPoolSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL innodb_log_buffer_size=%d", variables.InnodbLogBufferSize)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL sort_buffer_size=%d", variables.SortBufferSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL read_buffer_size=%d", variables.ReadBufferSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL read_rnd_buffer_size=%d", variables.ReadRndBufferSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL join_buffer_size=%d", variables.JoinBufferSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL thread_stack=%d", variables.ThreadStack)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL binlog_cache_size=%d", variables.BinlogCachSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL thread_cache_size=%d", variables.ThreadCacheSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL table_open_cache=%d", variables.TableOpenCache)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("set GLOBAL max_connections=%d", variables.MaxConnections)); err != nil {
|
||||
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, app.Key, app.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -324,6 +315,7 @@ func (u *MysqlService) LoadBaseInfo(name string) (*dto.DBBaseInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.ContainerName = app.ContainerName
|
||||
data.Name = app.Name
|
||||
data.Port = int64(app.Port)
|
||||
data.Password = app.Password
|
||||
@ -485,3 +477,40 @@ func backupMysql(backupType, baseDir, backupDir, mysqlName, dbName, fileName str
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMyCnf(oldFiles []string, group string, param string, value interface{}) []string {
|
||||
isOn := false
|
||||
hasKey := false
|
||||
regItem, _ := regexp.Compile(`\[*\]`)
|
||||
var newFiles []string
|
||||
for _, line := range oldFiles {
|
||||
if strings.HasPrefix(line, group) {
|
||||
isOn = true
|
||||
newFiles = append(newFiles, line)
|
||||
continue
|
||||
}
|
||||
if !isOn {
|
||||
newFiles = append(newFiles, line)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, param) || strings.HasPrefix(line, "# "+param) {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s=%v", param, value))
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
isDeadLine := regItem.Match([]byte(line))
|
||||
if !isDeadLine {
|
||||
newFiles = append(newFiles, line)
|
||||
continue
|
||||
}
|
||||
if !hasKey {
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s=%v\n", param, value))
|
||||
newFiles = append(newFiles, line)
|
||||
}
|
||||
}
|
||||
if !isOn {
|
||||
newFiles = append(newFiles, group+"\n")
|
||||
newFiles = append(newFiles, fmt.Sprintf("%s=%v\n", param, value))
|
||||
}
|
||||
return newFiles
|
||||
}
|
||||
|
@ -177,8 +177,6 @@ func (u *RedisService) Backup() error {
|
||||
if stdout, err := cmd.CombinedOutput(); err != nil {
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
name := fmt.Sprintf("%s.rdb", time.Now().Format("20060102150405"))
|
||||
|
||||
backupLocal, err := backupRepo.Get(commonRepo.WithByType("LOCAL"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -187,7 +185,6 @@ func (u *RedisService) Backup() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
backupDir := fmt.Sprintf("database/redis/%s/", redisInfo.Name)
|
||||
fullDir := fmt.Sprintf("%s/%s", localDir, backupDir)
|
||||
if _, err := os.Stat(fullDir); err != nil && os.IsNotExist(err) {
|
||||
@ -197,6 +194,21 @@ func (u *RedisService) Backup() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if appendonly == "yes" {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
name := fmt.Sprintf("%s.tar.gz", time.Now().Format("20060102150405"))
|
||||
if err := handleTar(redisDataDir+"/appendonlydir", fullDir, name, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("%s.rdb", time.Now().Format("20060102150405"))
|
||||
cmd2 := exec.Command("docker", "cp", fmt.Sprintf("%s:/data/dump.rdb", redisInfo.ContainerName), fmt.Sprintf("%s/%s", fullDir, name))
|
||||
if stdout, err := cmd2.CombinedOutput(); err != nil {
|
||||
return errors.New(string(stdout))
|
||||
@ -209,18 +221,29 @@ func (u *RedisService) Recover(req dto.RedisBackupRecover) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
composeDir := fmt.Sprintf("%s/redis/%s", constant.AppInstallDir, redisInfo.Name)
|
||||
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fullName := fmt.Sprintf("%s/%s", req.FileDir, req.FileName)
|
||||
input, err := ioutil.ReadFile(fullName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(composeDir+"/data/dump.rdb", input, 0640); err != nil {
|
||||
return err
|
||||
if appendonly == "yes" {
|
||||
redisDataDir := fmt.Sprintf("%s/%s/%s/data/", constant.AppInstallDir, "redis", redisInfo.Name)
|
||||
if err := handleUnTar(fullName, redisDataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
input, err := ioutil.ReadFile(fullName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(composeDir+"/data/dump.rdb", input, 0640); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := compose.Up(composeDir + "/docker-compose.yml"); err != nil {
|
||||
return err
|
||||
|
@ -1,35 +1,65 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
)
|
||||
|
||||
func TestMysql(t *testing.T) {
|
||||
cmd := exec.Command("docker", "exec", "1Panel-redis-7.0.5-zgVH-K859", "redis-cli", "config", "get", "save")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(string(stdout))
|
||||
}
|
||||
path := "/Users/slooop/go/src/github.com/1Panel/apps/mysql/5.7.39/conf/my.cnf"
|
||||
|
||||
rows := strings.Split(string(stdout), "\r\n")
|
||||
rowMap := make(map[string]string)
|
||||
for _, v := range rows {
|
||||
itemRow := strings.Split(v, "\n")
|
||||
if len(itemRow) == 3 {
|
||||
rowMap[itemRow[0]] = itemRow[1]
|
||||
var lines []string
|
||||
lineBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
lines = strings.Split(string(lineBytes), "\n")
|
||||
}
|
||||
var newLines []string
|
||||
|
||||
start := "[mysqld]"
|
||||
isOn := false
|
||||
hasKey := false
|
||||
regItem, _ := regexp.Compile(`^\[*\]`)
|
||||
i := 0
|
||||
for _, line := range lines {
|
||||
i++
|
||||
if strings.HasPrefix(line, start) {
|
||||
isOn = true
|
||||
newLines = append(newLines, line)
|
||||
continue
|
||||
}
|
||||
if !isOn {
|
||||
newLines = append(newLines, line)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "user") || strings.HasPrefix(line, "# user") {
|
||||
newLines = append(newLines, "user="+"ON")
|
||||
hasKey = true
|
||||
continue
|
||||
}
|
||||
isDeadLine := regItem.Match([]byte(line))
|
||||
if !isDeadLine {
|
||||
newLines = append(newLines, line)
|
||||
continue
|
||||
}
|
||||
if !hasKey {
|
||||
newLines = append(newLines, "user="+"ON \n")
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
}
|
||||
var info dto.RedisStatus
|
||||
arr, err := json.Marshal(rowMap)
|
||||
|
||||
file, err := os.OpenFile(path, os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = file.WriteString(strings.Join(newLines, "\n"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
_ = json.Unmarshal(arr, &info)
|
||||
fmt.Println(info)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
||||
withRecordRouter.POST("/recover", baseApi.RecoverMysql)
|
||||
withRecordRouter.POST("/backups/search", baseApi.SearchDBBackups)
|
||||
withRecordRouter.POST("/del", baseApi.DeleteMysql)
|
||||
withRecordRouter.POST("/variables/update", baseApi.UpdateMysqlVariables)
|
||||
withRecordRouter.POST("/variables/update/:mysqlName", baseApi.UpdateMysqlVariables)
|
||||
withRecordRouter.POST("/conf/update/byfile", baseApi.UpdateMysqlConfByFile)
|
||||
cmdRouter.POST("/search", baseApi.SearchMysql)
|
||||
cmdRouter.GET("/variables/:name", baseApi.LoadVariables)
|
||||
|
@ -33,6 +33,7 @@ export namespace Database {
|
||||
password: string;
|
||||
remoteConn: boolean;
|
||||
mysqlKey: string;
|
||||
containerName: string;
|
||||
}
|
||||
export interface MysqlConfUpdateByFile {
|
||||
mysqlName: string;
|
||||
@ -63,6 +64,13 @@ export namespace Database {
|
||||
thread_cache_size: number;
|
||||
thread_stack: number;
|
||||
tmp_table_size: number;
|
||||
|
||||
slow_query_log: string;
|
||||
long_query_time: number;
|
||||
}
|
||||
export interface VariablesUpdate {
|
||||
param: string;
|
||||
value: any;
|
||||
}
|
||||
export interface MysqlStatus {
|
||||
Aborted_clients: number;
|
||||
|
@ -26,8 +26,8 @@ export const addMysqlDB = (params: Database.MysqlDBCreate) => {
|
||||
export const updateMysqlDBInfo = (params: Database.ChangeInfo) => {
|
||||
return http.put(`/databases/${params.id}`, params);
|
||||
};
|
||||
export const updateMysqlVariables = (params: Database.MysqlVariables) => {
|
||||
return http.post(`/databases/variables/update`, params);
|
||||
export const updateMysqlVariables = (mysqlName: string, params: Array<Database.VariablesUpdate>) => {
|
||||
return http.post(`/databases/variables/update/${mysqlName}`, params);
|
||||
};
|
||||
export const updateMysqlConfByFile = (params: Database.MysqlConfUpdateByFile) => {
|
||||
return http.post(`/databases/conf/update/byfile`, params);
|
||||
|
60
frontend/src/components/confirm-dialog/index.vue
Normal file
60
frontend/src/components/confirm-dialog/index.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<el-dialog v-model="submitVisiable" :destroy-on-close="true" width="30%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ header }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<span style="font-size: 12px">{{ operationInfo }}</span>
|
||||
<el-input v-model="submitInput"></el-input>
|
||||
<span style="font-size: 12px">{{ $t('commons.msg.operateConfirm') }}</span>
|
||||
<span style="font-size: 12px; color: red; font-weight: 500">'{{ submitInputInfo }}'</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="submitVisiable = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="submitInput !== submitInputInfo" @click="onConfirm">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const header = ref();
|
||||
const operationInfo = ref();
|
||||
const submitInputInfo = ref();
|
||||
const submitVisiable = ref(false);
|
||||
|
||||
const submitInput = ref();
|
||||
|
||||
interface DialogProps {
|
||||
header: string;
|
||||
operationInfo: string;
|
||||
submitInputInfo: string;
|
||||
}
|
||||
|
||||
const acceptParams = (props: DialogProps): void => {
|
||||
submitVisiable.value = true;
|
||||
header.value = props.header;
|
||||
operationInfo.value = props.operationInfo;
|
||||
submitInputInfo.value = props.submitInputInfo;
|
||||
submitInput.value = '';
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'confirm'): void }>();
|
||||
|
||||
const onConfirm = async () => {
|
||||
emit('confirm');
|
||||
submitVisiable.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
111
frontend/src/components/container-log/index.vue
Normal file
111
frontend/src/components/container-log/index.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-select @change="searchLogs" style="width: 10%; float: left" v-model="logSearch.mode">
|
||||
<el-option v-for="item in timeOptions" :key="item.label" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
<div style="margin-left: 20px; float: left">
|
||||
<el-checkbox border v-model="logSearch.isWatch">{{ $t('commons.button.watch') }}</el-checkbox>
|
||||
</div>
|
||||
<el-button style="margin-left: 20px" @click="onDownload" icon="Download">
|
||||
{{ $t('file.download') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
placeholder="None data"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="margin-top: 10px; max-height: 500px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="logInfo"
|
||||
:readOnly="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { logContainer } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { dateFromatForName } from '@/utils/util';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
|
||||
const logInfo = ref();
|
||||
const logSearch = reactive({
|
||||
isWatch: false,
|
||||
container: '',
|
||||
containerID: '',
|
||||
mode: 'all',
|
||||
});
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const timeOptions = ref([
|
||||
{ label: i18n.global.t('container.all'), value: 'all' },
|
||||
{
|
||||
label: i18n.global.t('container.lastDay'),
|
||||
value: new Date(new Date().getTime() - 3600 * 1000 * 24 * 1).getTime() / 1000 + '',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.last4Hour'),
|
||||
value: new Date(new Date().getTime() - 3600 * 1000 * 4).getTime() / 1000 + '',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.lastHour'),
|
||||
value: new Date(new Date().getTime() - 3600 * 1000).getTime() / 1000 + '',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.last10Min'),
|
||||
value: new Date(new Date().getTime() - 600 * 1000).getTime() / 1000 + '',
|
||||
},
|
||||
]);
|
||||
|
||||
const onCloseLog = async () => {
|
||||
clearInterval(Number(timer));
|
||||
};
|
||||
|
||||
const searchLogs = async () => {
|
||||
const res = await logContainer(logSearch);
|
||||
logInfo.value = res.data;
|
||||
};
|
||||
|
||||
const onDownload = async () => {
|
||||
const downloadUrl = window.URL.createObjectURL(new Blob([logInfo.value]));
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = downloadUrl;
|
||||
a.download = logSearch.container + '-' + dateFromatForName(new Date()) + '.log';
|
||||
const event = new MouseEvent('click');
|
||||
a.dispatchEvent(event);
|
||||
};
|
||||
|
||||
interface DialogProps {
|
||||
containerID: string;
|
||||
}
|
||||
|
||||
const acceptParams = (props: DialogProps): void => {
|
||||
logSearch.containerID = props.containerID;
|
||||
logSearch.mode = 'all';
|
||||
logSearch.isWatch = false;
|
||||
searchLogs();
|
||||
timer = setInterval(() => {
|
||||
if (logSearch.isWatch) {
|
||||
searchLogs();
|
||||
}
|
||||
}, 1000 * 5);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
onCloseLog,
|
||||
});
|
||||
</script>
|
@ -70,6 +70,7 @@ export default {
|
||||
updateSuccess: '更新成功',
|
||||
uploadSuccess: '上传成功',
|
||||
operate: '操作',
|
||||
operateConfirm: '如果确认操作,请手动输入',
|
||||
},
|
||||
login: {
|
||||
captchaHelper: '请输入验证码',
|
||||
@ -252,12 +253,13 @@ export default {
|
||||
hit: '查找数据库键命中率',
|
||||
latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
|
||||
|
||||
recoverHelper: '即将使用 [{0}] 对数据进行覆盖,是否继续?',
|
||||
submitIt: '覆盖数据',
|
||||
|
||||
baseConf: '基础配置',
|
||||
allConf: '全部配置',
|
||||
restartNow: '立即重启',
|
||||
restartNowHelper1: '修改配置后需要',
|
||||
restartNowHelper2: '重启 redis 生效',
|
||||
restartNowHelper3: ',若您的数据需要持久化请先执行 save 操作。',
|
||||
restartNowHelper1: '修改配置后需要重启生效,若您的数据需要持久化请先执行 save 操作。',
|
||||
|
||||
persistence: '持久化',
|
||||
rdbHelper1: '秒內,插入',
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="demo-collapse" v-if="onSetting">
|
||||
<div class="demo-collapse" v-show="onSetting">
|
||||
<el-card>
|
||||
<el-collapse v-model="activeName" accordion>
|
||||
<el-collapse-item :title="$t('database.baseSetting')" name="1">
|
||||
@ -70,239 +70,18 @@
|
||||
</el-button>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('database.currentStatus')" name="3">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="6">
|
||||
<table style="width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.runTime') }}</td>
|
||||
<td>{{ mysqlStatus.run }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.connections') }}</td>
|
||||
<td>{{ mysqlStatus.connections }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.bytesSent') }}</td>
|
||||
<td>{{ mysqlStatus!.bytesSent }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.bytesReceived') }}</td>
|
||||
<td>{{ mysqlStatus!.bytesReceived }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<table style="width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.queryPerSecond }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.txPerSecond }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File</td>
|
||||
<td>{{ mysqlStatus!.file }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Position</td>
|
||||
<td>{{ mysqlStatus!.position }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="12">
|
||||
<table style="margin-top: 20px; width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.connInfo }}</td>
|
||||
<td>{{ $t('database.connInfoHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.threadCacheHit') }}</td>
|
||||
<td>{{ mysqlStatus!.threadCacheHit }}</td>
|
||||
<td>{{ $t('database.threadCacheHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.indexHit') }}</td>
|
||||
<td>{{ mysqlStatus!.indexHit }}</td>
|
||||
<td>{{ $t('database.indexHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.innodbIndexHit') }}</td>
|
||||
<td>{{ mysqlStatus!.innodbIndexHit }}</td>
|
||||
<td>{{ $t('database.innodbIndexHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.cacheHit') }}</td>
|
||||
<td>{{ mysqlStatus!.cacheHit }}</td>
|
||||
<td>{{ $t('database.cacheHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.tmpTableToDB') }}</td>
|
||||
<td>{{ mysqlStatus!.tmpTableToDB }}</td>
|
||||
<td>{{ $t('database.tmpTableToDBHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.openTables') }}</td>
|
||||
<td>{{ mysqlStatus!.openTables }}</td>
|
||||
<td>{{ $t('database.openTablesHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.selectFullJoin') }}</td>
|
||||
<td>{{ mysqlStatus!.selectFullJoin }}</td>
|
||||
<td>{{ $t('database.selectFullJoinHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.selectRangeCheck') }}</td>
|
||||
<td>{{ mysqlStatus!.selectRangeCheck }}</td>
|
||||
<td>{{ $t('database.selectRangeCheckHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.sortMergePasses') }}</td>
|
||||
<td>{{ mysqlStatus!.sortMergePasses }}</td>
|
||||
<td>{{ $t('database.sortMergePassesHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.tableLocksWaited') }}</td>
|
||||
<td>{{ mysqlStatus!.tableLocksWaited }}</td>
|
||||
<td>{{ $t('database.tableLocksWaitedHelper') }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<Status ref="statusRef" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('database.performanceTuning')" name="4">
|
||||
<el-card>
|
||||
<el-form
|
||||
:model="mysqlVariables"
|
||||
:rules="variablesRules"
|
||||
ref="variableFormRef"
|
||||
label-width="160px"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item :label="$t('database.optimizationScheme')">
|
||||
<el-select @change="changePlan" clearable v-model="plan">
|
||||
<el-option
|
||||
v-for="item in planOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="key_buffer_size" prop="key_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.key_buffer_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.keyBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="query_cache_size" prop="query_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.query_cache_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.queryCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="tmp_table_size" prop="tmp_table_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.tmp_table_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.tmpTableSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="innodb_buffer_pool_size" prop="innodb_buffer_pool_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.innodb_buffer_pool_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.innodbBufferPoolSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="innodb_log_buffer_size" prop="innodb_log_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.innodb_log_buffer_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.innodbLogBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="sort_buffer_size" prop="sort_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.sort_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.sortBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="read_buffer_size" prop="read_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.read_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.readBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
icon="Collection"
|
||||
@click="onSaveVariables(variableFormRef)"
|
||||
type="primary"
|
||||
size="default"
|
||||
>
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
<el-button icon="RefreshLeft" size="default">
|
||||
{{ $t('database.restart') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="read_rnd_buffer_size" prop="read_rnd_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.read_rnd_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.readRndBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="join_buffer_size" prop="join_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.join_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.joinBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="thread_stack" prop="thread_stack">
|
||||
<el-input clearable v-model.number="mysqlVariables.thread_stack">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.threadStackelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="binlog_cache_size" prop="binlog_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.binlog_cache_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.binlogCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="thread_cache_size" prop="thread_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.thread_cache_size" />
|
||||
<span class="input-help">{{ $t('database.threadCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="table_open_cache" prop="table_open_cache">
|
||||
<el-input clearable v-model.number="mysqlVariables.table_open_cache" />
|
||||
<span class="input-help">{{ $t('database.tableOpenCacheHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="max_connections" prop="max_connections">
|
||||
<el-input clearable v-model.number="mysqlVariables.max_connections" />
|
||||
<span class="input-help">{{ $t('database.maxConnectionsHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<Variables ref="variablesRef" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="日志" name="5">
|
||||
<ContainerLog ref="dialogContainerLogRef" />
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item title="慢日志" name="6">
|
||||
<SlowLog ref="slowLogRef" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="日志" name="5"></el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</div>
|
||||
@ -310,21 +89,16 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage, FormInstance } from 'element-plus';
|
||||
import ContainerLog from '@/components/container-log/index.vue';
|
||||
import Status from '@/views/database/mysql/setting/status/index.vue';
|
||||
import Variables from '@/views/database/mysql/setting/variables/index.vue';
|
||||
import SlowLog from '@/views/database/mysql/setting/slow-log/index.vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { LoadFile } from '@/api/modules/files';
|
||||
import { planOptions } from './helper';
|
||||
import {
|
||||
loadMysqlBaseInfo,
|
||||
loadMysqlStatus,
|
||||
loadMysqlVariables,
|
||||
updateMysqlConfByFile,
|
||||
updateMysqlDBInfo,
|
||||
updateMysqlVariables,
|
||||
} from '@/api/modules/database';
|
||||
import { computeSize } from '@/utils/util';
|
||||
import { loadMysqlBaseInfo, updateMysqlConfByFile, updateMysqlDBInfo } from '@/api/modules/database';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
|
||||
@ -337,69 +111,14 @@ const baseInfo = reactive({
|
||||
password: '',
|
||||
remoteConn: false,
|
||||
mysqlKey: '',
|
||||
containerID: '',
|
||||
});
|
||||
const panelFormRef = ref<FormInstance>();
|
||||
const mysqlConf = ref();
|
||||
|
||||
const plan = ref();
|
||||
|
||||
const variableFormRef = ref<FormInstance>();
|
||||
let mysqlVariables = reactive({
|
||||
mysqlName: '',
|
||||
key_buffer_size: 0,
|
||||
query_cache_size: 0,
|
||||
tmp_table_size: 0,
|
||||
innodb_buffer_pool_size: 0,
|
||||
innodb_log_buffer_size: 0,
|
||||
sort_buffer_size: 0,
|
||||
read_buffer_size: 0,
|
||||
read_rnd_buffer_size: 0,
|
||||
join_buffer_size: 0,
|
||||
thread_stack: 0,
|
||||
binlog_cache_size: 0,
|
||||
thread_cache_size: 0,
|
||||
table_open_cache: 0,
|
||||
max_connections: 0,
|
||||
});
|
||||
const variablesRules = reactive({
|
||||
key_buffer_size: [Rules.number],
|
||||
query_cache_size: [Rules.number],
|
||||
tmp_table_size: [Rules.number],
|
||||
innodb_buffer_pool_size: [Rules.number],
|
||||
innodb_log_buffer_size: [Rules.number],
|
||||
sort_buffer_size: [Rules.number],
|
||||
read_buffer_size: [Rules.number],
|
||||
read_rnd_buffer_size: [Rules.number],
|
||||
join_buffer_size: [Rules.number],
|
||||
thread_stack: [Rules.number],
|
||||
binlog_cache_size: [Rules.number],
|
||||
thread_cache_size: [Rules.number],
|
||||
table_open_cache: [Rules.number],
|
||||
max_connections: [Rules.number],
|
||||
});
|
||||
let mysqlStatus = reactive({
|
||||
run: 0,
|
||||
connections: 0,
|
||||
bytesSent: '',
|
||||
bytesReceived: '',
|
||||
|
||||
queryPerSecond: '',
|
||||
txPerSecond: '',
|
||||
file: '',
|
||||
position: 0,
|
||||
|
||||
connInfo: '',
|
||||
threadCacheHit: '',
|
||||
indexHit: '',
|
||||
innodbIndexHit: '',
|
||||
cacheHit: '',
|
||||
tmpTableToDB: '',
|
||||
openTables: 0,
|
||||
selectFullJoin: 0,
|
||||
selectRangeCheck: 0,
|
||||
sortMergePasses: 0,
|
||||
tableLocksWaited: 0,
|
||||
});
|
||||
const statusRef = ref();
|
||||
const variablesRef = ref();
|
||||
const slowLogRef = ref();
|
||||
|
||||
const onSetting = ref<boolean>(false);
|
||||
const mysqlName = ref();
|
||||
@ -407,12 +126,14 @@ const mysqlName = ref();
|
||||
interface DialogProps {
|
||||
mysqlName: string;
|
||||
}
|
||||
|
||||
const dialogContainerLogRef = ref();
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
onSetting.value = true;
|
||||
mysqlName.value = params.mysqlName;
|
||||
variablesRef.value!.acceptParams({ mysqlName: params.mysqlName });
|
||||
statusRef.value!.acceptParams({ mysqlName: params.mysqlName });
|
||||
loadBaseInfo();
|
||||
loadStatus();
|
||||
loadVariables();
|
||||
};
|
||||
const onClose = (): void => {
|
||||
onSetting.value = false;
|
||||
@ -453,6 +174,10 @@ const onSaveFile = async () => {
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
const loadContainerLog = async (containerID: string) => {
|
||||
dialogContainerLogRef.value!.acceptParams({ containerID: containerID });
|
||||
};
|
||||
|
||||
const loadBaseInfo = async () => {
|
||||
const res = await loadMysqlBaseInfo(mysqlName.value);
|
||||
baseInfo.name = res.data?.name;
|
||||
@ -460,7 +185,9 @@ const loadBaseInfo = async () => {
|
||||
baseInfo.password = res.data?.password;
|
||||
baseInfo.remoteConn = res.data?.remoteConn;
|
||||
baseInfo.mysqlKey = res.data?.mysqlKey;
|
||||
baseInfo.containerID = res.data?.containerName;
|
||||
loadMysqlConf(`/opt/1Panel/data/apps/${baseInfo.mysqlKey}/${baseInfo.name}/conf/my.cnf`);
|
||||
loadContainerLog(baseInfo.containerID);
|
||||
};
|
||||
|
||||
const loadMysqlConf = async (path: string) => {
|
||||
@ -468,108 +195,6 @@ const loadMysqlConf = async (path: string) => {
|
||||
mysqlConf.value = res.data;
|
||||
};
|
||||
|
||||
const loadVariables = async () => {
|
||||
const res = await loadMysqlVariables(mysqlName.value);
|
||||
mysqlVariables.key_buffer_size = Number(res.data.key_buffer_size) / 1024 / 1024;
|
||||
mysqlVariables.query_cache_size = Number(res.data.query_cache_size) / 1024 / 1024;
|
||||
mysqlVariables.tmp_table_size = Number(res.data.tmp_table_size) / 1024 / 1024;
|
||||
mysqlVariables.innodb_buffer_pool_size = Number(res.data.innodb_buffer_pool_size) / 1024 / 1024;
|
||||
mysqlVariables.innodb_log_buffer_size = Number(res.data.innodb_log_buffer_size) / 1024 / 1024;
|
||||
|
||||
mysqlVariables.sort_buffer_size = Number(res.data.sort_buffer_size) / 1024;
|
||||
mysqlVariables.read_buffer_size = Number(res.data.read_buffer_size) / 1024;
|
||||
mysqlVariables.read_rnd_buffer_size = Number(res.data.read_rnd_buffer_size) / 1024;
|
||||
mysqlVariables.join_buffer_size = Number(res.data.join_buffer_size) / 1024;
|
||||
mysqlVariables.thread_stack = Number(res.data.thread_stack) / 1024;
|
||||
mysqlVariables.binlog_cache_size = Number(res.data.binlog_cache_size) / 1024;
|
||||
mysqlVariables.thread_cache_size = Number(res.data.thread_cache_size);
|
||||
mysqlVariables.table_open_cache = Number(res.data.table_open_cache);
|
||||
mysqlVariables.max_connections = Number(res.data.max_connections);
|
||||
};
|
||||
|
||||
const changePlan = async () => {
|
||||
for (const item of planOptions) {
|
||||
if (item.id === plan.value) {
|
||||
mysqlVariables.key_buffer_size = item.data.key_buffer_size;
|
||||
mysqlVariables.query_cache_size = item.data.query_cache_size;
|
||||
mysqlVariables.tmp_table_size = item.data.tmp_table_size;
|
||||
mysqlVariables.innodb_buffer_pool_size = item.data.innodb_buffer_pool_size;
|
||||
|
||||
mysqlVariables.sort_buffer_size = item.data.sort_buffer_size;
|
||||
mysqlVariables.read_buffer_size = item.data.read_buffer_size;
|
||||
mysqlVariables.read_rnd_buffer_size = item.data.read_rnd_buffer_size;
|
||||
mysqlVariables.join_buffer_size = item.data.join_buffer_size;
|
||||
mysqlVariables.thread_stack = item.data.thread_stack;
|
||||
mysqlVariables.binlog_cache_size = item.data.binlog_cache_size;
|
||||
mysqlVariables.thread_cache_size = item.data.thread_cache_size;
|
||||
mysqlVariables.table_open_cache = item.data.table_open_cache;
|
||||
mysqlVariables.max_connections = item.data.max_connections;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSaveVariables = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let itemForm = {
|
||||
mysqlName: mysqlName.value,
|
||||
key_buffer_size: mysqlVariables.key_buffer_size * 1024 * 1024,
|
||||
query_cache_size: mysqlVariables.query_cache_size * 1024 * 1024,
|
||||
tmp_table_size: mysqlVariables.tmp_table_size * 1024 * 1024,
|
||||
innodb_buffer_pool_size: mysqlVariables.innodb_buffer_pool_size * 1024 * 1024,
|
||||
innodb_log_buffer_size: mysqlVariables.innodb_log_buffer_size * 1024 * 1024,
|
||||
|
||||
sort_buffer_size: mysqlVariables.sort_buffer_size * 1024,
|
||||
read_buffer_size: mysqlVariables.read_buffer_size * 1024,
|
||||
read_rnd_buffer_size: mysqlVariables.read_rnd_buffer_size * 1024,
|
||||
join_buffer_size: mysqlVariables.join_buffer_size * 1024,
|
||||
thread_stack: mysqlVariables.thread_stack * 1024,
|
||||
binlog_cache_size: mysqlVariables.binlog_cache_size * 1024,
|
||||
thread_cache_size: mysqlVariables.thread_cache_size,
|
||||
table_open_cache: mysqlVariables.table_open_cache,
|
||||
max_connections: mysqlVariables.max_connections,
|
||||
};
|
||||
await updateMysqlVariables(itemForm);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const loadStatus = async () => {
|
||||
const res = await loadMysqlStatus(mysqlName.value);
|
||||
let queryPerSecond = res.data.Questions / res.data.Uptime;
|
||||
let txPerSecond = (res.data!.Com_commit + res.data.Com_rollback) / res.data.Uptime;
|
||||
|
||||
let threadCacheHit = (1 - res.data.Threads_created / res.data.Connections) * 100;
|
||||
let cacheHit = (res.data.Qcache_hits / (res.data.Qcache_hits + res.data.Qcache_inserts)) * 100;
|
||||
let indexHit = (1 - res.data.Key_reads / res.data.Key_read_requests) * 100;
|
||||
let innodbIndexHit = (1 - res.data.Innodb_buffer_pool_reads / res.data.Innodb_buffer_pool_read_requests) * 100;
|
||||
let tmpTableToDB = (res.data.Created_tmp_disk_tables / res.data.Created_tmp_tables) * 100;
|
||||
|
||||
mysqlStatus.run = res.data.Run;
|
||||
mysqlStatus.connections = res.data.Connections;
|
||||
mysqlStatus.bytesSent = res.data.Bytes_sent ? computeSize(res.data.Bytes_sent) : '0';
|
||||
mysqlStatus.bytesReceived = res.data.Bytes_received ? computeSize(res.data.Bytes_received) : '0';
|
||||
|
||||
mysqlStatus.queryPerSecond = isNaN(queryPerSecond) || queryPerSecond === 0 ? '0' : queryPerSecond.toFixed(2);
|
||||
mysqlStatus.txPerSecond = isNaN(txPerSecond) || txPerSecond === 0 ? '0' : txPerSecond.toFixed(2);
|
||||
mysqlStatus.file = res.data.File;
|
||||
mysqlStatus.position = res.data.Position;
|
||||
|
||||
mysqlStatus.connInfo = res.data.Threads_running + '/' + res.data.Max_used_connections;
|
||||
mysqlStatus.threadCacheHit = isNaN(threadCacheHit) || threadCacheHit === 0 ? '0' : threadCacheHit.toFixed(2) + '%';
|
||||
mysqlStatus.indexHit = isNaN(indexHit) || indexHit === 0 ? '0' : indexHit.toFixed(2) + '%';
|
||||
mysqlStatus.innodbIndexHit = isNaN(innodbIndexHit) || innodbIndexHit === 0 ? '0' : innodbIndexHit.toFixed(2) + '%';
|
||||
mysqlStatus.cacheHit = isNaN(cacheHit) || cacheHit === 0 ? 'OFF' : cacheHit.toFixed(2) + '%';
|
||||
mysqlStatus.tmpTableToDB = isNaN(tmpTableToDB) || tmpTableToDB === 0 ? '0' : tmpTableToDB.toFixed(2) + '%';
|
||||
mysqlStatus.openTables = res.data.Open_tables;
|
||||
mysqlStatus.selectFullJoin = res.data.Select_full_join;
|
||||
mysqlStatus.selectRangeCheck = res.data.Select_range_check;
|
||||
mysqlStatus.sortMergePasses = res.data.Sort_merge_passes;
|
||||
mysqlStatus.tableLocksWaited = res.data.Table_locks_waited;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
onClose,
|
||||
|
40
frontend/src/views/database/mysql/setting/slow-log/index.vue
Normal file
40
frontend/src/views/database/mysql/setting/slow-log/index.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<span style="float: left">是否开启</span>
|
||||
<el-switch style="margin-left: 20px; float: left" v-model="form.slow_query_log" />
|
||||
<span style="margin-left: 30px; float: left">慢查询阈值</span>
|
||||
<div style="margin-left: 5px; float: left">
|
||||
<el-input v-model="form.long_query_time"></el-input>
|
||||
</div>
|
||||
<el-button style="margin-left: 20px">确认修改</el-button>
|
||||
<div v-if="form.slow_query_log === 'ON'">
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
placeholder="None data"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="margin-top: 10px; max-height: 500px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="slowLogs"
|
||||
:readOnly="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
const slowLogs = ref();
|
||||
const form = reactive({
|
||||
slow_query_log: 'OFF',
|
||||
long_query_time: 10,
|
||||
});
|
||||
</script>
|
185
frontend/src/views/database/mysql/setting/status/index.vue
Normal file
185
frontend/src/views/database/mysql/setting/status/index.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="6">
|
||||
<table style="width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.runTime') }}</td>
|
||||
<td>{{ mysqlStatus.run }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.connections') }}</td>
|
||||
<td>{{ mysqlStatus.connections }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.bytesSent') }}</td>
|
||||
<td>{{ mysqlStatus!.bytesSent }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.bytesReceived') }}</td>
|
||||
<td>{{ mysqlStatus!.bytesReceived }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<table style="width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.queryPerSecond }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.txPerSecond }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File</td>
|
||||
<td>{{ mysqlStatus!.file }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Position</td>
|
||||
<td>{{ mysqlStatus!.position }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="12">
|
||||
<table style="margin-top: 20px; width: 100%" class="myTable">
|
||||
<tr>
|
||||
<td>{{ $t('database.queryPerSecond') }}</td>
|
||||
<td>{{ mysqlStatus!.connInfo }}</td>
|
||||
<td>{{ $t('database.connInfoHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.threadCacheHit') }}</td>
|
||||
<td>{{ mysqlStatus!.threadCacheHit }}</td>
|
||||
<td>{{ $t('database.threadCacheHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.indexHit') }}</td>
|
||||
<td>{{ mysqlStatus!.indexHit }}</td>
|
||||
<td>{{ $t('database.indexHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.innodbIndexHit') }}</td>
|
||||
<td>{{ mysqlStatus!.innodbIndexHit }}</td>
|
||||
<td>{{ $t('database.innodbIndexHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.cacheHit') }}</td>
|
||||
<td>{{ mysqlStatus!.cacheHit }}</td>
|
||||
<td>{{ $t('database.cacheHitHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.tmpTableToDB') }}</td>
|
||||
<td>{{ mysqlStatus!.tmpTableToDB }}</td>
|
||||
<td>{{ $t('database.tmpTableToDBHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.openTables') }}</td>
|
||||
<td>{{ mysqlStatus!.openTables }}</td>
|
||||
<td>{{ $t('database.openTablesHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.selectFullJoin') }}</td>
|
||||
<td>{{ mysqlStatus!.selectFullJoin }}</td>
|
||||
<td>{{ $t('database.selectFullJoinHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.selectRangeCheck') }}</td>
|
||||
<td>{{ mysqlStatus!.selectRangeCheck }}</td>
|
||||
<td>{{ $t('database.selectRangeCheckHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.sortMergePasses') }}</td>
|
||||
<td>{{ mysqlStatus!.sortMergePasses }}</td>
|
||||
<td>{{ $t('database.sortMergePassesHelper') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('database.tableLocksWaited') }}</td>
|
||||
<td>{{ mysqlStatus!.tableLocksWaited }}</td>
|
||||
<td>{{ $t('database.tableLocksWaitedHelper') }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { loadMysqlStatus } from '@/api/modules/database';
|
||||
import { computeSize } from '@/utils/util';
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
let mysqlStatus = reactive({
|
||||
run: 0,
|
||||
connections: 0,
|
||||
bytesSent: '',
|
||||
bytesReceived: '',
|
||||
|
||||
queryPerSecond: '',
|
||||
txPerSecond: '',
|
||||
file: '',
|
||||
position: 0,
|
||||
|
||||
connInfo: '',
|
||||
threadCacheHit: '',
|
||||
indexHit: '',
|
||||
innodbIndexHit: '',
|
||||
cacheHit: '',
|
||||
tmpTableToDB: '',
|
||||
openTables: 0,
|
||||
selectFullJoin: 0,
|
||||
selectRangeCheck: 0,
|
||||
sortMergePasses: 0,
|
||||
tableLocksWaited: 0,
|
||||
});
|
||||
|
||||
const mysqlName = ref();
|
||||
interface DialogProps {
|
||||
mysqlName: string;
|
||||
}
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
mysqlName.value = params.mysqlName;
|
||||
loadStatus();
|
||||
};
|
||||
|
||||
const loadStatus = async () => {
|
||||
const res = await loadMysqlStatus(mysqlName.value);
|
||||
let queryPerSecond = res.data.Questions / res.data.Uptime;
|
||||
let txPerSecond = (res.data!.Com_commit + res.data.Com_rollback) / res.data.Uptime;
|
||||
|
||||
let threadCacheHit = (1 - res.data.Threads_created / res.data.Connections) * 100;
|
||||
let cacheHit = (res.data.Qcache_hits / (res.data.Qcache_hits + res.data.Qcache_inserts)) * 100;
|
||||
let indexHit = (1 - res.data.Key_reads / res.data.Key_read_requests) * 100;
|
||||
let innodbIndexHit = (1 - res.data.Innodb_buffer_pool_reads / res.data.Innodb_buffer_pool_read_requests) * 100;
|
||||
let tmpTableToDB = (res.data.Created_tmp_disk_tables / res.data.Created_tmp_tables) * 100;
|
||||
|
||||
mysqlStatus.run = res.data.Run;
|
||||
mysqlStatus.connections = res.data.Connections;
|
||||
mysqlStatus.bytesSent = res.data.Bytes_sent ? computeSize(res.data.Bytes_sent) : '0';
|
||||
mysqlStatus.bytesReceived = res.data.Bytes_received ? computeSize(res.data.Bytes_received) : '0';
|
||||
|
||||
mysqlStatus.queryPerSecond = isNaN(queryPerSecond) || queryPerSecond === 0 ? '0' : queryPerSecond.toFixed(2);
|
||||
mysqlStatus.txPerSecond = isNaN(txPerSecond) || txPerSecond === 0 ? '0' : txPerSecond.toFixed(2);
|
||||
mysqlStatus.file = res.data.File;
|
||||
mysqlStatus.position = res.data.Position;
|
||||
|
||||
mysqlStatus.connInfo = res.data.Threads_running + '/' + res.data.Max_used_connections;
|
||||
mysqlStatus.threadCacheHit = isNaN(threadCacheHit) || threadCacheHit === 0 ? '0' : threadCacheHit.toFixed(2) + '%';
|
||||
mysqlStatus.indexHit = isNaN(indexHit) || indexHit === 0 ? '0' : indexHit.toFixed(2) + '%';
|
||||
mysqlStatus.innodbIndexHit = isNaN(innodbIndexHit) || innodbIndexHit === 0 ? '0' : innodbIndexHit.toFixed(2) + '%';
|
||||
mysqlStatus.cacheHit = isNaN(cacheHit) || cacheHit === 0 ? 'OFF' : cacheHit.toFixed(2) + '%';
|
||||
mysqlStatus.tmpTableToDB = isNaN(tmpTableToDB) || tmpTableToDB === 0 ? '0' : tmpTableToDB.toFixed(2) + '%';
|
||||
mysqlStatus.openTables = res.data.Open_tables;
|
||||
mysqlStatus.selectFullJoin = res.data.Select_full_join;
|
||||
mysqlStatus.selectRangeCheck = res.data.Select_range_check;
|
||||
mysqlStatus.sortMergePasses = res.data.Sort_merge_passes;
|
||||
mysqlStatus.tableLocksWaited = res.data.Table_locks_waited;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
285
frontend/src/views/database/mysql/setting/variables/index.vue
Normal file
285
frontend/src/views/database/mysql/setting/variables/index.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form :model="mysqlVariables" :rules="variablesRules" ref="variableFormRef" label-width="160px">
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item :label="$t('database.optimizationScheme')">
|
||||
<el-select @change="changePlan" clearable v-model="plan">
|
||||
<el-option
|
||||
v-for="item in planOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="key_buffer_size" prop="key_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.key_buffer_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.keyBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="query_cache_size" prop="query_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.query_cache_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.queryCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="tmp_table_size" prop="tmp_table_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.tmp_table_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.tmpTableSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="innodb_buffer_pool_size" prop="innodb_buffer_pool_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.innodb_buffer_pool_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.innodbBufferPoolSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="innodb_log_buffer_size" prop="innodb_log_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.innodb_log_buffer_size">
|
||||
<template #append>MB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.innodbLogBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="sort_buffer_size" prop="sort_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.sort_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.sortBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="read_buffer_size" prop="read_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.read_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.readBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
icon="Collection"
|
||||
@click="onSaveVariables(variableFormRef)"
|
||||
type="primary"
|
||||
size="default"
|
||||
>
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
<el-button icon="RefreshLeft" size="default">
|
||||
{{ $t('database.restart') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2"><br /></el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="read_rnd_buffer_size" prop="read_rnd_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.read_rnd_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.readRndBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="join_buffer_size" prop="join_buffer_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.join_buffer_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.joinBufferSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="thread_stack" prop="thread_stack">
|
||||
<el-input clearable v-model.number="mysqlVariables.thread_stack">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.threadStackelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="binlog_cache_size" prop="binlog_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.binlog_cache_size">
|
||||
<template #append>KB</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('database.binlogCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="thread_cache_size" prop="thread_cache_size">
|
||||
<el-input clearable v-model.number="mysqlVariables.thread_cache_size" />
|
||||
<span class="input-help">{{ $t('database.threadCacheSizeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="table_open_cache" prop="table_open_cache">
|
||||
<el-input clearable v-model.number="mysqlVariables.table_open_cache" />
|
||||
<span class="input-help">{{ $t('database.tableOpenCacheHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="max_connections" prop="max_connections">
|
||||
<el-input clearable v-model.number="mysqlVariables.max_connections" />
|
||||
<span class="input-help">{{ $t('database.maxConnectionsHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { ElMessage, FormInstance } from 'element-plus';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import { loadMysqlVariables, updateMysqlVariables } from '@/api/modules/database';
|
||||
import i18n from '@/lang';
|
||||
import { planOptions } from './../helper';
|
||||
|
||||
const plan = ref();
|
||||
|
||||
const variableFormRef = ref<FormInstance>();
|
||||
const oldVariables = ref<Database.MysqlVariables>();
|
||||
let mysqlVariables = reactive({
|
||||
mysqlName: '',
|
||||
key_buffer_size: 0,
|
||||
query_cache_size: 0,
|
||||
tmp_table_size: 0,
|
||||
innodb_buffer_pool_size: 0,
|
||||
innodb_log_buffer_size: 0,
|
||||
sort_buffer_size: 0,
|
||||
read_buffer_size: 0,
|
||||
read_rnd_buffer_size: 0,
|
||||
join_buffer_size: 0,
|
||||
thread_stack: 0,
|
||||
binlog_cache_size: 0,
|
||||
thread_cache_size: 0,
|
||||
table_open_cache: 0,
|
||||
max_connections: 0,
|
||||
|
||||
slow_query_log: '',
|
||||
long_query_time: 0,
|
||||
});
|
||||
const variablesRules = reactive({
|
||||
key_buffer_size: [Rules.number],
|
||||
query_cache_size: [Rules.number],
|
||||
tmp_table_size: [Rules.number],
|
||||
innodb_buffer_pool_size: [Rules.number],
|
||||
innodb_log_buffer_size: [Rules.number],
|
||||
sort_buffer_size: [Rules.number],
|
||||
read_buffer_size: [Rules.number],
|
||||
read_rnd_buffer_size: [Rules.number],
|
||||
join_buffer_size: [Rules.number],
|
||||
thread_stack: [Rules.number],
|
||||
binlog_cache_size: [Rules.number],
|
||||
thread_cache_size: [Rules.number],
|
||||
table_open_cache: [Rules.number],
|
||||
max_connections: [Rules.number],
|
||||
|
||||
slow_query_log: [Rules.requiredSelect],
|
||||
long_query_time: [Rules.number],
|
||||
});
|
||||
|
||||
const mysqlName = ref();
|
||||
interface DialogProps {
|
||||
mysqlName: string;
|
||||
}
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
console.log('adad');
|
||||
mysqlName.value = params.mysqlName;
|
||||
loadVariables();
|
||||
};
|
||||
|
||||
const loadVariables = async () => {
|
||||
const res = await loadMysqlVariables(mysqlName.value);
|
||||
mysqlVariables.key_buffer_size = Number(res.data.key_buffer_size) / 1024 / 1024;
|
||||
mysqlVariables.query_cache_size = Number(res.data.query_cache_size) / 1024 / 1024;
|
||||
mysqlVariables.tmp_table_size = Number(res.data.tmp_table_size) / 1024 / 1024;
|
||||
mysqlVariables.innodb_buffer_pool_size = Number(res.data.innodb_buffer_pool_size) / 1024 / 1024;
|
||||
mysqlVariables.innodb_log_buffer_size = Number(res.data.innodb_log_buffer_size) / 1024 / 1024;
|
||||
|
||||
mysqlVariables.sort_buffer_size = Number(res.data.sort_buffer_size) / 1024;
|
||||
mysqlVariables.read_buffer_size = Number(res.data.read_buffer_size) / 1024;
|
||||
mysqlVariables.read_rnd_buffer_size = Number(res.data.read_rnd_buffer_size) / 1024;
|
||||
mysqlVariables.join_buffer_size = Number(res.data.join_buffer_size) / 1024;
|
||||
mysqlVariables.thread_stack = Number(res.data.thread_stack) / 1024;
|
||||
mysqlVariables.binlog_cache_size = Number(res.data.binlog_cache_size) / 1024;
|
||||
mysqlVariables.thread_cache_size = Number(res.data.thread_cache_size);
|
||||
mysqlVariables.table_open_cache = Number(res.data.table_open_cache);
|
||||
mysqlVariables.max_connections = Number(res.data.max_connections);
|
||||
oldVariables.value = { ...mysqlVariables };
|
||||
};
|
||||
|
||||
const changePlan = async () => {
|
||||
for (const item of planOptions) {
|
||||
if (item.id === plan.value) {
|
||||
mysqlVariables.key_buffer_size = item.data.key_buffer_size;
|
||||
mysqlVariables.query_cache_size = item.data.query_cache_size;
|
||||
mysqlVariables.tmp_table_size = item.data.tmp_table_size;
|
||||
mysqlVariables.innodb_buffer_pool_size = item.data.innodb_buffer_pool_size;
|
||||
|
||||
mysqlVariables.sort_buffer_size = item.data.sort_buffer_size;
|
||||
mysqlVariables.read_buffer_size = item.data.read_buffer_size;
|
||||
mysqlVariables.read_rnd_buffer_size = item.data.read_rnd_buffer_size;
|
||||
mysqlVariables.join_buffer_size = item.data.join_buffer_size;
|
||||
mysqlVariables.thread_stack = item.data.thread_stack;
|
||||
mysqlVariables.binlog_cache_size = item.data.binlog_cache_size;
|
||||
mysqlVariables.thread_cache_size = item.data.thread_cache_size;
|
||||
mysqlVariables.table_open_cache = item.data.table_open_cache;
|
||||
mysqlVariables.max_connections = item.data.max_connections;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
const onSaveVariables = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let param = [] as Array<Database.VariablesUpdate>;
|
||||
if (oldVariables.value?.key_buffer_size !== mysqlVariables.key_buffer_size) {
|
||||
param.push({ param: 'key_buffer_size', value: mysqlVariables.key_buffer_size * 1024 * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.query_cache_size !== mysqlVariables.query_cache_size) {
|
||||
param.push({ param: 'query_cache_size', value: mysqlVariables.query_cache_size * 1024 * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.tmp_table_size !== mysqlVariables.tmp_table_size) {
|
||||
param.push({ param: 'tmp_table_size', value: mysqlVariables.tmp_table_size * 1024 * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.innodb_buffer_pool_size !== mysqlVariables.innodb_buffer_pool_size) {
|
||||
param.push({
|
||||
param: 'innodb_buffer_pool_size',
|
||||
value: mysqlVariables.innodb_buffer_pool_size * 1024 * 1024,
|
||||
});
|
||||
}
|
||||
if (oldVariables.value?.innodb_log_buffer_size !== mysqlVariables.innodb_log_buffer_size) {
|
||||
param.push({ param: 'innodb_log_buffer_size', value: mysqlVariables.innodb_log_buffer_size * 1024 * 1024 });
|
||||
}
|
||||
|
||||
if (oldVariables.value?.sort_buffer_size !== mysqlVariables.sort_buffer_size) {
|
||||
param.push({ param: 'sort_buffer_size', value: mysqlVariables.sort_buffer_size * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.read_buffer_size !== mysqlVariables.read_buffer_size) {
|
||||
param.push({ param: 'read_buffer_size', value: mysqlVariables.read_buffer_size * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.read_rnd_buffer_size !== mysqlVariables.read_rnd_buffer_size) {
|
||||
param.push({ param: 'read_rnd_buffer_size', value: mysqlVariables.read_rnd_buffer_size * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.join_buffer_size !== mysqlVariables.join_buffer_size) {
|
||||
param.push({ param: 'join_buffer_size', value: mysqlVariables.join_buffer_size * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.thread_stack !== mysqlVariables.thread_stack) {
|
||||
param.push({ param: 'thread_stack', value: mysqlVariables.thread_stack * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.binlog_cache_size !== mysqlVariables.binlog_cache_size) {
|
||||
param.push({ param: 'binlog_cache_size', value: mysqlVariables.binlog_cache_size * 1024 });
|
||||
}
|
||||
if (oldVariables.value?.thread_cache_size !== mysqlVariables.thread_cache_size) {
|
||||
param.push({ param: 'thread_cache_size', value: mysqlVariables.thread_cache_size });
|
||||
}
|
||||
if (oldVariables.value?.table_open_cache !== mysqlVariables.table_open_cache) {
|
||||
param.push({ param: 'table_open_cache', value: mysqlVariables.table_open_cache });
|
||||
}
|
||||
if (oldVariables.value?.max_connections !== mysqlVariables.max_connections) {
|
||||
param.push({ param: 'max_connections', value: mysqlVariables.max_connections });
|
||||
}
|
||||
await updateMysqlVariables(mysqlName.value, param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -113,11 +113,14 @@
|
||||
/>
|
||||
</ComplexTable>
|
||||
</el-card>
|
||||
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onRecover"></ConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import {
|
||||
backupRedis,
|
||||
@ -161,6 +164,9 @@ const onClose = (): void => {
|
||||
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
const currentRow = ref();
|
||||
const confirmDialogRef = ref();
|
||||
const submitInput = ref();
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
@ -192,13 +198,15 @@ const onBackup = async () => {
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loadBackupRecords();
|
||||
};
|
||||
const onRecover = async (row: Database.RedisBackupRecord) => {
|
||||
let param = {
|
||||
fileName: row.fileName,
|
||||
fileDir: row.fileDir,
|
||||
};
|
||||
await recoverRedis(param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
const onRecover = async () => {
|
||||
if (submitInput.value === i18n.global.t('database.submitIt')) {
|
||||
let param = {
|
||||
fileName: currentRow.value.fileName,
|
||||
fileDir: currentRow.value.fileDir,
|
||||
};
|
||||
await recoverRedis(param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
}
|
||||
};
|
||||
|
||||
const onBatchDelete = async (row: Database.RedisBackupRecord | null) => {
|
||||
@ -220,7 +228,13 @@ const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.recover'),
|
||||
click: (row: Database.RedisBackupRecord) => {
|
||||
onRecover(row);
|
||||
currentRow.value = row;
|
||||
let params = {
|
||||
header: i18n.global.t('commons.button.recover'),
|
||||
operationInfo: i18n.global.t('database.recoverHelper', [row.fileName]),
|
||||
submitInputInfo: i18n.global.t('database.submitIt'),
|
||||
};
|
||||
confirmDialogRef.value!.acceptParams(params);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -50,40 +50,13 @@
|
||||
v-model="mysqlConf"
|
||||
:readOnly="true"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
@click="saveVisiable = true"
|
||||
style="width: 90px; margin-top: 5px"
|
||||
>
|
||||
<el-button type="primary" size="default" @click="onSaveFile" style="width: 90px; margin-top: 5px">
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="saveVisiable" :destroy-on-close="true" width="30%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ $t('database.confChange') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox v-model="restartNow" :label="$t('database.restartNow')" />
|
||||
<div>
|
||||
<span style="font-size: 12px">{{ $t('database.restartNowHelper1') }}</span>
|
||||
<span style="font-size: 12px; color: red; font-weight: 500">
|
||||
{{ $t('database.restartNowHelper2') }}
|
||||
</span>
|
||||
<span style="font-size: 12px">{{ $t('database.restartNowHelper3') }}</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="saveVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button @click="onSaveFile()">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmitSave"></ConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -94,6 +67,7 @@ import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { LoadFile } from '@/api/modules/files';
|
||||
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
|
||||
import { loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database';
|
||||
import i18n from '@/lang';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
@ -102,7 +76,6 @@ const extensions = [javascript(), oneDark];
|
||||
const confShowType = ref('base');
|
||||
|
||||
const restartNow = ref(false);
|
||||
const saveVisiable = ref(false);
|
||||
const form = reactive({
|
||||
name: '',
|
||||
port: 3306,
|
||||
@ -120,6 +93,7 @@ const rules = reactive({
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const mysqlConf = ref();
|
||||
const confirmDialogRef = ref();
|
||||
|
||||
const settingShow = ref<boolean>(false);
|
||||
|
||||
@ -145,18 +119,25 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
maxmemory: form.maxmemory + '',
|
||||
};
|
||||
await updateRedisConf(param);
|
||||
saveVisiable.value = false;
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const onSaveFile = async () => {
|
||||
let params = {
|
||||
header: i18n.global.t('database.confChange'),
|
||||
operationInfo: i18n.global.t('database.restartNowHelper1'),
|
||||
submitInputInfo: i18n.global.t('database.restartNow'),
|
||||
};
|
||||
confirmDialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const onSubmitSave = async () => {
|
||||
let param = {
|
||||
file: mysqlConf.value,
|
||||
restartNow: restartNow.value,
|
||||
};
|
||||
await updateRedisConfByFile(param);
|
||||
saveVisiable.value = false;
|
||||
restartNow.value = false;
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user