您好,登錄后才能下訂單哦!
今天小編給大家分享一下基于Golang怎么實(shí)現(xiàn)內(nèi)存數(shù)據(jù)庫(kù)的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
GO實(shí)現(xiàn)內(nèi)存數(shù)據(jù)庫(kù)
實(shí)現(xiàn)Redis的database層(核心層:處理命令并返回)
本文涉及以下文件:dict:定義字典的一些方法
sync_dict:實(shí)現(xiàn)dict
db:分?jǐn)?shù)據(jù)庫(kù)
command:定義指令
ping,keys,string:指令的具體處理邏輯
database:?jiǎn)螜C(jī)版數(shù)據(jù)庫(kù)
datastruct/dict/dict.go
type Consumer func(key string, val interface{}) bool type Dict interface { Get(key string) (val interface{}, exists bool) Len() int Put(key string, val interface{}) (result int) PutIfAbsent(key string, val interface{}) (result int) PutIfExists(key string, val interface{}) (result int) Remove(key string) (result int) ForEach(consumer Consumer) Keys() []string RandomKeys(limit int) []string RandomDistinctKeys(limit int) []string Clear() }
Dict接口:Redis數(shù)據(jù)結(jié)構(gòu)的接口。這里我們使用sync.Map作為字典的實(shí)現(xiàn),如果想用別的數(shù)據(jù)結(jié)構(gòu),換一個(gè)實(shí)現(xiàn)即可
Consumer:遍歷字典所有的鍵值對(duì),返回值是布爾,true繼續(xù)遍歷,false停止遍歷
datastruct/dict/sync_dict.go
type SyncDict struct { m sync.Map } func MakeSyncDict() *SyncDict { return &SyncDict{} } func (dict *SyncDict) Get(key string) (val interface{}, exists bool) { val, ok := dict.m.Load(key) return val, ok } func (dict *SyncDict) Len() int { length := 0 dict.m.Range(func(k, v interface{}) bool { length++ return true }) return length } func (dict *SyncDict) Put(key string, val interface{}) (result int) { _, existed := dict.m.Load(key) dict.m.Store(key, val) if existed { return 0 } return 1 } func (dict *SyncDict) PutIfAbsent(key string, val interface{}) (result int) { _, existed := dict.m.Load(key) if existed { return 0 } dict.m.Store(key, val) return 1 } func (dict *SyncDict) PutIfExists(key string, val interface{}) (result int) { _, existed := dict.m.Load(key) if existed { dict.m.Store(key, val) return 1 } return 0 } func (dict *SyncDict) Remove(key string) (result int) { _, existed := dict.m.Load(key) dict.m.Delete(key) if existed { return 1 } return 0 } func (dict *SyncDict) ForEach(consumer Consumer) { dict.m.Range(func(key, value interface{}) bool { consumer(key.(string), value) return true }) } func (dict *SyncDict) Keys() []string { result := make([]string, dict.Len()) i := 0 dict.m.Range(func(key, value interface{}) bool { result[i] = key.(string) i++ return true }) return result } func (dict *SyncDict) RandomKeys(limit int) []string { result := make([]string, limit) for i := 0; i < limit; i++ { dict.m.Range(func(key, value interface{}) bool { result[i] = key.(string) return false }) } return result } func (dict *SyncDict) RandomDistinctKeys(limit int) []string { result := make([]string, limit) i := 0 dict.m.Range(func(key, value interface{}) bool { result[i] = key.(string) i++ if i == limit { return false } return true }) return result } func (dict *SyncDict) Clear() { *dict = *MakeSyncDict() }
使用sync.Map實(shí)現(xiàn)Dict接口
database/db.go
type DB struct { index int data dict.Dict } type ExecFunc func(db *DB, args [][]byte) resp.Reply type CmdLine = [][]byte func makeDB() *DB { db := &DB{ data: dict.MakeSyncDict(), } return db } func (db *DB) Exec(c resp.Connection, cmdLine [][]byte) resp.Reply { cmdName := strings.ToLower(string(cmdLine[0])) cmd, ok := cmdTable[cmdName] if !ok { return reply.MakeErrReply("ERR unknown command '" + cmdName + "'") } if !validateArity(cmd.arity, cmdLine) { return reply.MakeArgNumErrReply(cmdName) } fun := cmd.executor return fun(db, cmdLine[1:]) // 把 set k v 中的set切掉 } func validateArity(arity int, cmdArgs [][]byte) bool { argNum := len(cmdArgs) if arity >= 0 { return argNum == arity } return argNum >= -arity } func (db *DB) GetEntity(key string) (*database.DataEntity, bool) { raw, ok := db.data.Get(key) if !ok { return nil, false } entity, _ := raw.(*database.DataEntity) return entity, true } func (db *DB) PutEntity(key string, entity *database.DataEntity) int { return db.data.Put(key, entity) } func (db *DB) PutIfExists(key string, entity *database.DataEntity) int { return db.data.PutIfExists(key, entity) } func (db *DB) PutIfAbsent(key string, entity *database.DataEntity) int { return db.data.PutIfAbsent(key, entity) } func (db *DB) Remove(key string) { db.data.Remove(key) } func (db *DB) Removes(keys ...string) (deleted int) { deleted = 0 for _, key := range keys { _, exists := db.data.Get(key) if exists { db.Remove(key) deleted++ } } return deleted } func (db *DB) Flush() { db.data.Clear() }
實(shí)現(xiàn)Redis中的分?jǐn)?shù)據(jù)庫(kù)
ExecFunc:所有Redis的指令都寫成這樣的類型
validateArity方法:
定長(zhǎng):set k v => arity=3;
變長(zhǎng):exists k1 k2 k3 ... => arity=-2,表示參數(shù)>=2個(gè)
database/command.go
var cmdTable = make(map[string]*command) type command struct { executor ExecFunc arity int } func RegisterCommand(name string, executor ExecFunc, arity int) { name = strings.ToLower(name) cmdTable[name] = &command{ executor: executor, arity: arity, } }
command:每一個(gè)command結(jié)構(gòu)體都是一個(gè)指令,例如ping,keys等等
arity:參數(shù)數(shù)量
cmdTable:記錄所有指令和command結(jié)構(gòu)體的關(guān)系
RegisterCommand:注冊(cè)指令的實(shí)現(xiàn),在程序
database/ping.go
func Ping(db *DB, args [][]byte) resp.Reply { if len(args) == 0 { return &reply.PongReply{} } else if len(args) == 1 { return reply.MakeStatusReply(string(args[0])) } else { return reply.MakeErrReply("ERR wrong number of arguments for 'ping' command") } } func init() { RegisterCommand("ping", Ping, 1) }
init方法:在啟動(dòng)程序時(shí)就會(huì)調(diào)用這個(gè)方法,用于初始化
database/keys.go
func execDel(db *DB, args [][]byte) resp.Reply { keys := make([]string, len(args)) for i, v := range args { keys[i] = string(v) } deleted := db.Removes(keys...) return reply.MakeIntReply(int64(deleted)) } func execExists(db *DB, args [][]byte) resp.Reply { result := int64(0) for _, arg := range args { key := string(arg) _, exists := db.GetEntity(key) if exists { result++ } } return reply.MakeIntReply(result) } func execFlushDB(db *DB, args [][]byte) resp.Reply { db.Flush() return &reply.OkReply{} } func execType(db *DB, args [][]byte) resp.Reply { key := string(args[0]) entity, exists := db.GetEntity(key) if !exists { return reply.MakeStatusReply("none") } switch entity.Data.(type) { case []byte: return reply.MakeStatusReply("string") } return &reply.UnknownErrReply{} } func execRename(db *DB, args [][]byte) resp.Reply { if len(args) != 2 { return reply.MakeErrReply("ERR wrong number of arguments for 'rename' command") } src := string(args[0]) dest := string(args[1]) entity, ok := db.GetEntity(src) if !ok { return reply.MakeErrReply("no such key") } db.PutEntity(dest, entity) db.Remove(src) return &reply.OkReply{} } func execRenameNx(db *DB, args [][]byte) resp.Reply { src := string(args[0]) dest := string(args[1]) _, exist := db.GetEntity(dest) if exist { return reply.MakeIntReply(0) } entity, ok := db.GetEntity(src) if !ok { return reply.MakeErrReply("no such key") } db.Removes(src, dest) db.PutEntity(dest, entity) return reply.MakeIntReply(1) } func execKeys(db *DB, args [][]byte) resp.Reply { pattern := wildcard.CompilePattern(string(args[0])) result := make([][]byte, 0) db.data.ForEach(func(key string, val interface{}) bool { if pattern.IsMatch(key) { result = append(result, []byte(key)) } return true }) return reply.MakeMultiBulkReply(result) } func init() { RegisterCommand("Del", execDel, -2) RegisterCommand("Exists", execExists, -2) RegisterCommand("Keys", execKeys, 2) RegisterCommand("FlushDB", execFlushDB, -1) RegisterCommand("Type", execType, 2) RegisterCommand("Rename", execRename, 3) RegisterCommand("RenameNx", execRenameNx, 3) }
keys.go實(shí)現(xiàn)以下指令:
execDel:del k1 k2 k3 ...
execExists:exist k1 k2 k3 ...
execFlushDB:flushdb
execType:type k1
execRename:rename k1 k2
execRenameNx:renamenx k1 k2
execKeys:keys(依賴lib包的工具類wildcard.go)
database/string.go
func execGet(db *DB, args [][]byte) resp.Reply { key := string(args[0]) bytes, err := db.getAsString(key) if err != nil { return err } if bytes == nil { return &reply.NullBulkReply{} } return reply.MakeBulkReply(bytes) } func (db *DB) getAsString(key string) ([]byte, reply.ErrorReply) { entity, ok := db.GetEntity(key) if !ok { return nil, nil } bytes, ok := entity.Data.([]byte) if !ok { return nil, &reply.WrongTypeErrReply{} } return bytes, nil } func execSet(db *DB, args [][]byte) resp.Reply { key := string(args[0]) value := args[1] entity := &database.DataEntity{ Data: value, } db.PutEntity(key, entity) return &reply.OkReply{} } func execSetNX(db *DB, args [][]byte) resp.Reply { key := string(args[0]) value := args[1] entity := &database.DataEntity{ Data: value, } result := db.PutIfAbsent(key, entity) return reply.MakeIntReply(int64(result)) } func execGetSet(db *DB, args [][]byte) resp.Reply { key := string(args[0]) value := args[1] entity, exists := db.GetEntity(key) db.PutEntity(key, &database.DataEntity{Data: value}) if !exists { return reply.MakeNullBulkReply() } old := entity.Data.([]byte) return reply.MakeBulkReply(old) } func execStrLen(db *DB, args [][]byte) resp.Reply { key := string(args[0]) entity, exists := db.GetEntity(key) if !exists { return reply.MakeNullBulkReply() } old := entity.Data.([]byte) return reply.MakeIntReply(int64(len(old))) } func init() { RegisterCommand("Get", execGet, 2) RegisterCommand("Set", execSet, -3) RegisterCommand("SetNx", execSetNX, 3) RegisterCommand("GetSet", execGetSet, 3) RegisterCommand("StrLen", execStrLen, 2) }
string.go實(shí)現(xiàn)以下指令:
execGet:get k1
execSet:set k v
execSetNX:setnex k v
execGetSet:getset k v 返回舊值
execStrLen:strlen k
database/database.go
type Database struct { dbSet []*DB } func NewDatabase() *Database { mdb := &Database{} if config.Properties.Databases == 0 { config.Properties.Databases = 16 } mdb.dbSet = make([]*DB, config.Properties.Databases) for i := range mdb.dbSet { singleDB := makeDB() singleDB.index = i mdb.dbSet[i] = singleDB } return mdb } func (mdb *Database) Exec(c resp.Connection, cmdLine [][]byte) (result resp.Reply) { defer func() { if err := recover(); err != nil { logger.Warn(fmt.Sprintf("error occurs: %v\n%s", err, string(debug.Stack()))) } }() cmdName := strings.ToLower(string(cmdLine[0])) if cmdName == "select" { if len(cmdLine) != 2 { return reply.MakeArgNumErrReply("select") } return execSelect(c, mdb, cmdLine[1:]) } dbIndex := c.GetDBIndex() selectedDB := mdb.dbSet[dbIndex] return selectedDB.Exec(c, cmdLine) } func execSelect(c resp.Connection, mdb *Database, args [][]byte) resp.Reply { dbIndex, err := strconv.Atoi(string(args[0])) if err != nil { return reply.MakeErrReply("ERR invalid DB index") } if dbIndex >= len(mdb.dbSet) { return reply.MakeErrReply("ERR DB index is out of range") } c.SelectDB(dbIndex) return reply.MakeOkReply() } func (mdb *Database) Close() { } func (mdb *Database) AfterClientClose(c resp.Connection) { }
Database:一組db的集合
Exec:執(zhí)行切換db指令或者其他指令
execSelect方法:選擇db(指令:select 2)
resp/handler/handler.go
import ( database2 "go-redis/database" ) func MakeHandler() *RespHandler { var db database.Database db = database2.NewDatabase() return &RespHandler{ db: db, } }
修改實(shí)現(xiàn)協(xié)議層handler的database實(shí)現(xiàn)
架構(gòu)小結(jié)
TCP層服務(wù)TCP的連接,然后將連接交給RESP協(xié)議層的handler,handler監(jiān)聽(tīng)客戶端的連接,將指令解析后發(fā)給管道,管道轉(zhuǎn)給database層(database/database.go),核心層根據(jù)命令類型執(zhí)行不同的方法,然后返回。
以上就是“基于Golang怎么實(shí)現(xiàn)內(nèi)存數(shù)據(jù)庫(kù)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。