291 lines
6.3 KiB
Go
291 lines
6.3 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm/logger"
|
|
"rear/internal/config"
|
|
"sync"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// WriteTask 写任务接口
|
|
type WriteTask interface {
|
|
Execute(db *gorm.DB) error
|
|
GetPriority() int // 优先级,用于任务排序
|
|
GetID() string // 任务ID,用于去重或追踪
|
|
}
|
|
|
|
// WriteTaskResult 写任务结果
|
|
type WriteTaskResult struct {
|
|
TaskID string
|
|
Error error
|
|
}
|
|
|
|
// TaskCallback 任务回调
|
|
type TaskCallback struct {
|
|
OnSuccess func(taskID string)
|
|
OnError func(taskID string, err error)
|
|
}
|
|
|
|
// DatabaseManager 数据库管理器
|
|
type DatabaseManager struct {
|
|
db *gorm.DB
|
|
dbType config.DatabaseType
|
|
|
|
// 写任务队列
|
|
writeQueue chan WriteTaskWithCallback
|
|
resultQueue chan WriteTaskResult
|
|
stopCh chan struct{}
|
|
wg sync.WaitGroup
|
|
|
|
// 读写分离配置
|
|
maxWriteWorkers int
|
|
maxRetries int
|
|
}
|
|
|
|
// WriteTaskWithCallback 带回调的写任务
|
|
type WriteTaskWithCallback struct {
|
|
Task WriteTask
|
|
Callback TaskCallback
|
|
}
|
|
|
|
// NewDatabaseManager 创建数据库管理器
|
|
func NewDatabaseManager(db *gorm.DB, dbType config.DatabaseType) *DatabaseManager {
|
|
maxWorkers := 1 // SQLite默认单线程
|
|
if dbType == config.MySQL {
|
|
maxWorkers = 10 // MySQL可以多线程
|
|
}
|
|
|
|
dm := &DatabaseManager{
|
|
db: db,
|
|
dbType: dbType,
|
|
writeQueue: make(chan WriteTaskWithCallback, 1000), // 缓冲队列
|
|
resultQueue: make(chan WriteTaskResult, 1000),
|
|
stopCh: make(chan struct{}),
|
|
maxWriteWorkers: maxWorkers,
|
|
maxRetries: 3,
|
|
}
|
|
|
|
dm.start()
|
|
return dm
|
|
}
|
|
|
|
// start 启动数据库管理器
|
|
func (dm *DatabaseManager) start() {
|
|
// 启动写任务处理器
|
|
for i := 0; i < dm.maxWriteWorkers; i++ {
|
|
dm.wg.Add(1)
|
|
go dm.writeWorker(i)
|
|
}
|
|
|
|
// 启动结果处理器
|
|
dm.wg.Add(1)
|
|
go dm.resultProcessor()
|
|
}
|
|
|
|
// writeWorker 写任务工作协程
|
|
func (dm *DatabaseManager) writeWorker(workerID int) {
|
|
defer dm.wg.Done()
|
|
|
|
for {
|
|
select {
|
|
case taskWithCallback := <-dm.writeQueue:
|
|
dm.executeWriteTask(taskWithCallback)
|
|
case <-dm.stopCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// executeWriteTask 执行写任务
|
|
func (dm *DatabaseManager) executeWriteTask(taskWithCallback WriteTaskWithCallback) {
|
|
task := taskWithCallback.Task
|
|
callback := taskWithCallback.Callback
|
|
|
|
var err error
|
|
for retry := 0; retry < dm.maxRetries; retry++ {
|
|
// 对于SQLite,可以使用事务来优化性能
|
|
if dm.dbType == config.SQLite {
|
|
err = dm.db.Transaction(func(tx *gorm.DB) error {
|
|
return task.Execute(tx)
|
|
})
|
|
} else {
|
|
err = task.Execute(dm.db)
|
|
}
|
|
|
|
if err == nil {
|
|
break
|
|
}
|
|
|
|
// 重试延迟
|
|
if retry < dm.maxRetries-1 {
|
|
time.Sleep(time.Millisecond * time.Duration(100*(retry+1)))
|
|
}
|
|
}
|
|
|
|
// 发送结果
|
|
result := WriteTaskResult{
|
|
TaskID: task.GetID(),
|
|
Error: err,
|
|
}
|
|
|
|
select {
|
|
case dm.resultQueue <- result:
|
|
default:
|
|
// 结果队列满了,直接执行回调
|
|
if err != nil && callback.OnError != nil {
|
|
callback.OnError(task.GetID(), err)
|
|
} else if err == nil && callback.OnSuccess != nil {
|
|
callback.OnSuccess(task.GetID())
|
|
}
|
|
}
|
|
}
|
|
|
|
// resultProcessor 结果处理器
|
|
func (dm *DatabaseManager) resultProcessor() {
|
|
defer dm.wg.Done()
|
|
|
|
for {
|
|
select {
|
|
case result := <-dm.resultQueue:
|
|
// 这里可以进行结果处理,比如记录日志、监控等
|
|
fmt.Printf("Task %s completed with error: %v\n", result.TaskID, result.Error)
|
|
case <-dm.stopCh:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// SubmitWriteTask 提交写任务
|
|
func (dm *DatabaseManager) SubmitWriteTask(task WriteTask, callback TaskCallback) error {
|
|
taskWithCallback := WriteTaskWithCallback{
|
|
Task: task,
|
|
Callback: callback,
|
|
}
|
|
|
|
select {
|
|
case dm.writeQueue <- taskWithCallback:
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("write queue is full")
|
|
}
|
|
}
|
|
|
|
// SubmitWriteTaskSync 同步提交写任务
|
|
func (dm *DatabaseManager) SubmitWriteTaskSync(ctx context.Context, task WriteTask) error {
|
|
done := make(chan error, 1)
|
|
|
|
callback := TaskCallback{
|
|
OnSuccess: func(taskID string) {
|
|
done <- nil
|
|
},
|
|
OnError: func(taskID string, err error) {
|
|
done <- err
|
|
},
|
|
}
|
|
|
|
if err := dm.SubmitWriteTask(task, callback); err != nil {
|
|
return err
|
|
}
|
|
|
|
select {
|
|
case err := <-done:
|
|
return err
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
// ReadDirect 直接读取(不经过队列)
|
|
func (dm *DatabaseManager) ReadDirect() *gorm.DB {
|
|
return dm.db
|
|
}
|
|
|
|
// GetDB 获取数据库连接(仅用于读操作)
|
|
func (dm *DatabaseManager) GetDB() *gorm.DB {
|
|
return dm.db
|
|
}
|
|
|
|
// Stop 停止数据库管理器
|
|
func (dm *DatabaseManager) Stop() {
|
|
close(dm.stopCh)
|
|
dm.wg.Wait()
|
|
}
|
|
|
|
func InitDatabase() error {
|
|
databaseConfig := getDatabaseConfig()
|
|
|
|
var db *gorm.DB
|
|
var err error
|
|
|
|
switch databaseConfig.Type {
|
|
case config.MySQL:
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
|
databaseConfig.Username, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Database)
|
|
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Info),
|
|
})
|
|
case config.SQLite:
|
|
// SQLite特殊配置
|
|
db, err = gorm.Open(sqlite.Open(databaseConfig.Database), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Info),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// SQLite优化设置
|
|
sqlDB, _ := db.DB()
|
|
sqlDB.SetMaxIdleConns(1) // SQLite单连接
|
|
sqlDB.SetMaxOpenConns(1) // SQLite单连接
|
|
sqlDB.SetConnMaxLifetime(0) // 永不关闭
|
|
|
|
// SQLite WAL模式优化
|
|
db.Exec("PRAGMA journal_mode=WAL;")
|
|
db.Exec("PRAGMA synchronous=NORMAL;")
|
|
db.Exec("PRAGMA cache_size=1000000;")
|
|
db.Exec("PRAGMA foreign_keys=true;")
|
|
db.Exec("PRAGMA temp_store=memory;")
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported database type: %s", databaseConfig.Type)
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect database: %w", err)
|
|
}
|
|
DB = db
|
|
Manger = NewDatabaseManager(db, databaseConfig.Type)
|
|
return nil
|
|
}
|
|
|
|
func getDatabaseConfig() config.DatabaseConfig {
|
|
// 初始化数据库
|
|
return config.DatabaseConfig{
|
|
Type: config.SQLite,
|
|
Database: "test.db",
|
|
MaxIdleConns: 1,
|
|
MaxOpenConns: 1,
|
|
MaxLifetime: 0,
|
|
}
|
|
}
|
|
|
|
var DB *gorm.DB
|
|
var Manger *DatabaseManager
|
|
|
|
func GetDB() *gorm.DB {
|
|
return DB
|
|
}
|
|
func GetManger() *DatabaseManager {
|
|
return Manger
|
|
}
|
|
func IsSQLite() bool {
|
|
cfg := getDatabaseConfig()
|
|
return cfg.Type == config.SQLite
|
|
}
|