Bootstrap

Golang代码案例:基于slowquery梳理MySQL慢查询SQL并发送邮件

config.ini

[email]
to = [email protected]@email.com

main.go

package main

import (
	"database/sql"
	"crypto/tls"
	"fmt"
	"net/smtp"
	"strings"
	"github.com/go-ini/ini"
	_ "github.com/go-sql-driver/mysql"
)

type QueryResult struct {
	Checksum      string
	Fingerprint   string
	DBMax         string
	UserMax       string
	LastSeen      string
	TSCnt         int
	QueryTimeMin  float64
	QueryTimeMax  float64
	QueryTimeAvg  float64
	Sample        string
}

func main() {
	// 连接数据库
	db, err := connectDB()
	if err != nil {
		panic(err)
	}
	defer db.Close()

	// 执行SQL查询
	results, err := executeQuery(db)
	if err != nil {
		panic(err)
	}

	// 格式化邮件内容
	emailContent := formatEmailContent(results)

	// 发送邮件
	err = sendEmail(emailContent)
	if err != nil {
		panic(err)
	}

	fmt.Println("Email sent successfully")
}

func connectDB() (*sql.DB, error) {
	dsn := "slowquery:1qazxsw2@tcp(192.168.0.100:3307)/slowquery?charset=utf8&parseTime=True&loc=Local"
	return sql.Open("mysql", dsn)
}

func executeQuery(db *sql.DB) ([]QueryResult, error) {
	rows, err := db.Query(`
		SELECT r.sample, h.db_max, h.user_max, r.last_seen, SUM(h.ts_cnt) AS ts_cnt,
		       ROUND(SUM(h.Query_time_sum)/SUM(h.ts_cnt), 3) AS Query_time_avg
		FROM mysql_slow_query_review AS r
		JOIN mysql_slow_query_review_history AS h ON r.checksum = h.checksum
		WHERE r.last_seen >= SUBDATE(NOW(), INTERVAL 1 DAY)
		GROUP BY r.checksum
		ORDER BY r.last_seen DESC, ts_cnt DESC
		LIMIT 100
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var results []QueryResult
	for rows.Next() {
		var result QueryResult
		err := rows.Scan(&result.Sample, &result.DBMax, &result.UserMax, &result.LastSeen, &result.TSCnt, &result.QueryTimeAvg)
		if err != nil {
			return nil, err
		}
		results = append(results, result)
	}
	return results, nil
}

func formatEmailContent(results []QueryResult) string {
	var emailContent strings.Builder
	emailContent.WriteString("<html><head><meta charset=\"UTF-8\"><style>table {width:100%;border-collapse:collapse;background-color:white;} th,td{border:1px solid #ccc;} th{background-color:#00bfff;color:white;text-align:center;cursor:pointer;}</style></head><body>")
	emailContent.WriteString("<table>")
	emailContent.WriteString("<tr><th>SQL语句</th><th>数据库</th><th>用户</th><th>最近时间</th><th>次数</th><th>平均时间</th></tr>")

	for _, r := range results {
		emailContent.WriteString(fmt.Sprintf(
			"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%.3f</td></tr>",
			r.Sample, r.DBMax, r.UserMax, r.LastSeen, r.TSCnt, r.QueryTimeAvg, 
		))
	}

	emailContent.WriteString("</table>")
	emailContent.WriteString("</body></html>")
	return emailContent.String()
}

func sendEmail(content string) error {
	from := "[email protected]"
	// to := []string{"[email protected]", "[email protected]"}
	// 获取邮件收件人
	// 读取配置文件
	cfg, err := ini.Load("config.ini")
	if err != nil {
		panic(err)
	}
	toStr := cfg.Section("email").Key("to").String()
	to := strings.Split(toStr, ",")

	auth := smtp.PlainAuth("", from, "密码", "192.168.100.2")

	// 创建 SMTP 客户端
	smtpServer := "192.168.100.2:25"
	client, err := smtp.Dial(smtpServer)
	if err != nil {
		return err
	}
	defer client.Close()

	// 启动 TLS 加密
	tlsConfig := &tls.Config{
		InsecureSkipVerify: true, // 禁用证书验证
	}
	if err := client.StartTLS(tlsConfig); err != nil {
		return fmt.Errorf("failed to start TLS: %v", err)
	}

	if err := client.Auth(auth); err != nil {
		return err
	}

	if err := client.Mail(from); err != nil {
		return err
	}

	for _, addr := range to {
		if err := client.Rcpt(addr); err != nil {
			return err
		}
	}

	w, err := client.Data()
	if err != nil {
		return err
	}

	// 构建邮件内容,确保 From 字段正确设置
	msg := []byte(fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: mysql慢查询Top 100\r\nMIME-Version: 1.0;\r\nContent-Type: text/html; charset=UTF-8;\r\n\r\n%s",
		from, strings.Join(to, ","), content))

	_, err = w.Write(msg)
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	return nil
}
;