-
Notifications
You must be signed in to change notification settings - Fork 0
hidb.md
hidb 套件為 HypGo 框架提供資料庫抽象層與連線管理器,支援多資料庫引擎、讀寫分離(Master-Replica)、連線池、交易管理、Redis 快取,以及可擴展的插件系統。
-
多資料庫支援: 透過
Dialect介面抽象 MySQL / PostgreSQL 的驅動差異,使用 Bun ORM 作為查詢建構層。 -
讀寫分離: 寫入操作始終路由至 Master,讀取操作透過
ReplicaPool以 Round-Robin 分配至 Replica,無 Replica 時自動回退至 Master。 -
Lock-Free Round-Robin: 使用
atomic.Pointer+atomic.Uint64實現完全無鎖讀路徑(copy-on-write),100k+ QPS 下零 mutex 競爭。 -
連線池管理: 支援
MaxIdleConns/MaxOpenConns/ConnMaxLifetime(預設 30 分鐘)配置,Master 與每個 Replica 獨立管理連線池。 -
交易管理: 同時提供原生
sql.Tx與 Bun ORMbun.Tx兩種交易介面。 -
Redis 整合: 內建
go-redis/v9客戶端,與 SQL 資料庫統一管理生命週期。 -
插件系統: 透過
DatabasePlugin介面支援非 SQL 資料庫(如 Cassandra)的動態註冊與載入。 - 優雅關閉: 按順序關閉 Replica → Master → Redis → Plugins,累積錯誤統一報告。
-
向後兼容:
HypDB()/SQL()始終返回 Master,舊有程式碼無需修改。
package main import ( "context" "log" "github.com/maoxiaoyue/hypgo/pkg/hidb" "github.com/maoxiaoyue/hypgo/pkg/hidb/pg" ) func main() { // 1. 建立資料庫管理器(PostgreSQL) db, err := hidb.NewWithInterface( appConfig.Database, // 實作 config.DatabaseConfigInterface hidb.WithDialect(pg.New()), // 指定 SQL 方言 ) if err != nil { log.Fatal(err) } defer db.Close() // 2. 健康檢查 if err := db.HealthCheck(context.Background()); err != nil { log.Fatal(err) } // 3. 讀取操作 → Replica(無 Replica 時回退至 Master) readDB := db.ReadHypDB() var users []User readDB.NewSelect().Model(&users).Scan(context.Background()) // 4. 寫入操作 → 始終使用 Master writeDB := db.WriteHypDB() writeDB.NewInsert().Model(&newUser).Exec(context.Background()) }
透過實作 config.ReplicaConfigProvider 介面啟用讀寫分離:
type ReplicaConfigProvider interface { GetReplicas() []ReplicaConfig } type ReplicaConfig struct { DSN string // Replica 連線字串 MaxIdleConns int // 最大閒置連線數 MaxOpenConns int // 最大連線數 }
| 方法 | 目標 | 說明 |
|---|---|---|
ReadHypDB() |
Replica (ORM) | Round-Robin 分配,無 Replica 回退 Master |
ReadSQL() |
Replica (Raw SQL) | Round-Robin 分配,無 Replica 回退 Master |
WriteHypDB() |
Master (ORM) | 始終使用 Master |
WriteSQL() |
Master (Raw SQL) | 始終使用 Master |
HypDB() |
Master (ORM) | 向後兼容,等同 WriteHypDB()
|
SQL() |
Master (Raw SQL) | 向後兼容,等同 WriteSQL()
|
// 讀路徑完全無鎖:atomic.Pointer + atomic.Uint64 // 寫路徑 copy-on-write:Mutex 保護 + 建立新 slice + atomic.Store replicas := *rp.replicas.Load() // 零鎖競爭 idx := rp.counter.Add(1) - 1 replica := replicas[idx % uint64(len(replicas))]
Master 和所有 Replica 統一設定 ConnMaxLifetime = 30 分鐘,防止長連線持有過期狀態(如 DNS 變更、密碼輪換)。
db.HasReplicas() // bool — 是否配置了 Replica db.ReplicaCount() // int — Replica 數量
err := db.Transaction(ctx, func(tx *sql.Tx) error { _, err := tx.ExecContext(ctx, "INSERT INTO users (name) VALUES (?)", "Alice") if err != nil { return err // 自動 Rollback } return nil // 自動 Commit })
err := db.HypDBTransaction(ctx, func(ctx context.Context, tx bun.Tx) error { _, err := tx.NewInsert().Model(&user).Exec(ctx) if err != nil { return err // 自動 Rollback } return nil // 自動 Commit })
HiDB 內建完整的 Redis 封裝,同時相容 KeyDB(Redis 協議相容)。除了原始 go-redis/v9 client,還提供高階方法覆蓋所有常用資料結構。
database: driver: "redis" # 或搭配 SQL 一起使用 redis: addr: "localhost:6379" # KeyDB 也是同一個設定 password: "your_password" db: 0
type RedisConfigInterface interface { GetAddr() string GetPassword() string GetDB() int }
ctx := context.Background() // 設定(含 TTL,0 = 不過期) db.RedisSet(ctx, "user:1:name", "Alice", 10*time.Minute) // 取得 name, err := db.RedisGet(ctx, "user:1:name") if hidb.RedisIsNil(err) { // key 不存在 } // 刪除 db.RedisDel(ctx, "user:1:name", "user:2:name") // 檢查存在 count, _ := db.RedisExists(ctx, "user:1:name") // TTL 管理 db.RedisExpire(ctx, "session:abc", 30*time.Minute) ttl, _ := db.RedisTTL(ctx, "session:abc")
直接存取 Go struct,自動 JSON 序列化/反序列化:
type GameState struct { Players []string `json:"players"` Round int `json:"round"` Status string `json:"status"` } // 存入 state := GameState{Players: []string{"p1", "p2"}, Round: 1, Status: "playing"} db.RedisSetJSON(ctx, "game:123:state", state, time.Hour) // 取出 var loaded GameState db.RedisGetJSON(ctx, "game:123:state", &loaded)
// 設定多個欄位 db.RedisHSet(ctx, "user:1", "name", "Alice", "email", "alice@test.com") // 取得單一欄位 name, _ := db.RedisHGet(ctx, "user:1", "name") // 取得所有欄位 all, _ := db.RedisHGetAll(ctx, "user:1") // all = map[string]string{"name": "Alice", "email": "alice@test.com"} // 刪除欄位 db.RedisHDel(ctx, "user:1", "email")
// 推入 db.RedisLPush(ctx, "queue:tasks", "task1", "task2") db.RedisRPush(ctx, "queue:tasks", "task3") // 取出 task, _ := db.RedisLPop(ctx, "queue:tasks") // 範圍查詢 tasks, _ := db.RedisLRange(ctx, "queue:tasks", 0, -1) // 長度 length, _ := db.RedisLLen(ctx, "queue:tasks")
// 新增成員 db.RedisSAdd(ctx, "room:1:players", "player1", "player2") // 檢查成員 isMember, _ := db.RedisSIsMember(ctx, "room:1:players", "player1") // 取得所有成員 members, _ := db.RedisSMembers(ctx, "room:1:players") // 移除成員 db.RedisSRem(ctx, "room:1:players", "player1")
import "github.com/redis/go-redis/v9" // 新增分數 db.RedisZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "player1"}) // 取得排行(含分數) results, _ := db.RedisZRangeWithScores(ctx, "leaderboard", 0, 9) // Top 10 // 查詢分數 score, _ := db.RedisZScore(ctx, "leaderboard", "player1") // 移除 db.RedisZRem(ctx, "leaderboard", "player1")
// 計數器 db.RedisIncr(ctx, "api:requests:count") db.RedisDecr(ctx, "stock:item:42") db.RedisIncrBy(ctx, "user:1:score", 50)
// 嘗試取得鎖(30 秒自動釋放) acquired, _ := db.RedisSetNX(ctx, "lock:game:123", "owner-uuid", 30*time.Second) if acquired { defer db.RedisDel(ctx, "lock:game:123") // 執行臨界區操作... }
// 發布 db.RedisPublish(ctx, "game:events", "player_joined") // 訂閱(呼叫端需 Close) sub, _ := db.RedisSubscribe(ctx, "game:events") defer sub.Close() for msg := range sub.Channel() { fmt.Printf("Channel: %s, Payload: %s\n", msg.Channel, msg.Payload) }
// 一般 pipeline(非交易) pipe, _ := db.RedisPipeline() pipe.Set(ctx, "key1", "val1", 0) pipe.Set(ctx, "key2", "val2", 0) pipe.Incr(ctx, "counter") cmds, err := pipe.Exec(ctx) // 交易式 pipeline(MULTI/EXEC) txPipe, _ := db.RedisTxPipeline() txPipe.Set(ctx, "key1", "val1", 0) txPipe.Set(ctx, "key2", "val2", 0) cmds, err = txPipe.Exec(ctx)
// 不阻塞伺服器的 key 掃描 var cursor uint64 for { keys, nextCursor, _ := db.RedisScan(ctx, cursor, "user:*", 100) for _, key := range keys { fmt.Println(key) } cursor = nextCursor if cursor == 0 { break } }
需要使用未封裝的 go-redis 功能時:
client := db.Redis() // *redis.Client // 直接使用 go-redis/v9 全部 API
KeyDB 與 Redis 使用相同協議,只需更改連線地址即可:
database: driver: "redis" redis: addr: "keydb-server:6379" # 指向 KeyDB password: "password" db: 0
KeyDB 特有優勢:多執行緒架構、FLASH 儲存支援、Active Replication — 全部透明運作,無需程式碼變更。
type Dialect interface { DriverName() string // "mysql" 或 "postgres" BunDialect() schema.Dialect // Bun ORM 方言實例 }
MySQL / TiDB:
import "github.com/maoxiaoyue/hypgo/pkg/hidb/mysql" db, err := hidb.NewWithInterface(cfg, hidb.WithDialect(mysql.New()))
PostgreSQL:
import "github.com/maoxiaoyue/hypgo/pkg/hidb/pg" db, err := hidb.NewWithInterface(cfg, hidb.WithDialect(pg.New()))
type DatabasePlugin interface { Name() string Init(config map[string]interface{}) error Connect() error Close() error Ping(ctx context.Context) error }
// 註冊插件 cassandraPlugin := cassandra.NewPlugin() db.RegisterPlugin(cassandraPlugin) // 動態載入(Init + Connect) db.LoadPlugin("cassandra", map[string]interface{}{ "hosts": []string{"127.0.0.1"}, "keyspace": "my_keyspace", }) // 取得插件 if plugin, ok := db.GetPlugin("cassandra"); ok { plugin.Ping(ctx) }
pkg/hidb/cassandra 提供內建的 Cassandra 5.0+ / ScyllaDB 完整驅動封裝,包含 Fluent DDL builder、Model 對映、CRUD builder、Vector/ANN 查詢、Migration、RBAC、Schema Introspection 等功能。
詳細文檔請參閱 hidb.cassandra 。
// 連線狀態 db.IsConnected() // bool // 完整健康檢查(Master + Replicas + Redis + Plugins) err := db.HealthCheck(ctx) // 資料庫驅動類型 db.Type() // "mysql", "postgres", "redis", etc. // 優雅關閉(按順序:Replicas → Master → Redis → Plugins) err := db.Close()
當配置物件未實作 config.DatabaseConfigInterface 時,New() 透過反射自動適配:
// 傳統結構體(使用反射適配) db, err := hidb.New(legacyConfig, hidb.WithDialect(pg.New())) // 推薦:實作 DatabaseConfigInterface db, err := hidb.NewWithInterface(modernConfig, hidb.WithDialect(pg.New()))
DatabaseConfigAdapter 透過反射提取 Driver、DSN、MaxIdleConns、MaxOpenConns、Redis 等欄位,確保舊有程式碼無需修改。
pkg/hidb/
├── hidb.go # 核心:Database, Dialect, Option, Plugin 系統,
│ # 交易管理, 健康檢查, 優雅關閉
├── redis.go # Redis/KeyDB 高階封裝(KV、Hash、List、Set、ZSet、Pub/Sub、Pipeline)
├── redis_test.go # Redis nil guard + RedisIsNil 測試
├── readwrite.go # ReplicaPool: Round-Robin 負載均衡, 讀寫分離
├── readwrite_test.go # 11 項測試:Round-Robin, 併發, 回退, 關閉
├── mysql/
│ └── mysql.go # MySQL / TiDB 方言實作
├── pg/
│ └── pg.go # PostgreSQL 方言實作
└── cassandra/
└── cassandra.go # Cassandra 插件實作
| 套件 | 用途 |
|---|---|
database/sql |
Go 標準 SQL 介面 |
github.com/uptrace/bun |
ORM 查詢建構 |
github.com/redis/go-redis/v9 |
Redis 客戶端 |
github.com/go-sql-driver/mysql |
MySQL 驅動 |
github.com/lib/pq |
PostgreSQL 驅動 |
github.com/gocql/gocql |
Cassandra 驅動 |
設計文件
套件
- config — 設定
- context — 請求上下文
- router — 路由器
- server — 伺服器
- middleware — 中介層
- websocket — WebSocket
- hidb — 資料庫 ORM
- hidb/cassandra — Cassandra
- logger — 日誌
- json — JSON 處理
- grpc — gRPC
AI 協作工具鏈
- schema — Schema-first 路由
- manifest — 專案 Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — 智慧 Scaffold
- airules — AI Rules
CLI 命令
- hyp 總覽
- hyp new
- hyp api
- hyp run
- hyp restart
- hyp generate
- hyp migrate
- hyp context
- hyp ai-rules
- hyp chkcomment
- hyp impact
- hyp docker
- hyp health
- hyp version
- hyp difflog
Design Docs
Packages
- config — Configuration
- context — Request Context
- router — Router
- server — Server
- middleware — Middleware
- websocket — WebSocket
- hidb — Database ORM
- hidb/cassandra - Cassandra 5.0
- logger — Logger
- json — JSON
- grpc — gRPC
AI Collaboration Toolchain
- schema — Schema-first Routing
- manifest — Project Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — Smart Scaffold
- airules — AI Rules
CLI Commands