Redis內(nèi)存數(shù)據(jù)庫(kù)示例分析
redies dict字典
這是 Redis 最底層的結(jié)構(gòu),比如 1個(gè)DB 下面有 16個(gè)Dict
1. 使用接口方式, 基礎(chǔ)實(shí)現(xiàn)是simple_dict ,sync_dict ,后續(xù)用戶可以根據(jù)自己的需求進(jìn)行自定義的實(shí)現(xiàn)屬于自己的Dict , 在 forEach 方法 支持匿名函數(shù)方式 type Dict interface { Get(key string) (val interface {}, exists bool) Len() // 回復(fù)上次 put 放入 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) // 傳入是方法, 返回 bool = true j繼續(xù)遍歷 Keys() []string RandomKeys(limit int) []string // 返回多個(gè)不重復(fù)的鍵 RandomDistinctKeys(limit int) []string Clear() } // Consumer is used to traversal dict, if it returns false the traversal will be break type Consumer func(key string, val interface{}) bool
package dict // SimpleDict wraps a map, it is not thread safe type SimpleDict struct { m map[string]interface{} } // MakeSimple makes a new map func MakeSimple() *SimpleDict { return &SimpleDict{ m: make(map[string]interface{}), } } // Get returns the binding value and whether the key is exist func (dict *SimpleDict) Get(key string) (val interface{}, exists bool) { val, ok := dict.m[key] return val, ok } // Len returns the number of dict func (dict *SimpleDict) Len() int { if dict.m == nil { panic("m is nil") } return len(dict.m) } // Put puts key value into dict and returns the number of new inserted key-value func (dict *SimpleDict) Put(key string, val interface{}) (result int) { _, existed := dict.m[key] dict.m[key] = val if existed { return 0 } return 1 } // PutIfAbsent puts value if the key is not exists and returns the number of updated key-value func (dict *SimpleDict) PutIfAbsent(key string, val interface{}) (result int) { _, existed := dict.m[key] if existed { return 0 } dict.m[key] = val return 1 } // PutIfExists puts value if the key is exist and returns the number of inserted key-value func (dict *SimpleDict) PutIfExists(key string, val interface{}) (result int) { _, existed := dict.m[key] if existed { dict.m[key] = val return 1 } return 0 } // Remove removes the key and return the number of deleted key-value func (dict *SimpleDict) Remove(key string) (result int) { _, existed := dict.m[key] delete(dict.m, key) if existed { return 1 } return 0 } // Keys returns all keys in dict func (dict *SimpleDict) Keys() []string { result := make([]string, len(dict.m)) i := 0 for k := range dict.m { result[i] = k } return result } // ForEach traversal the dict func (dict *SimpleDict) ForEach(consumer Consumer) { for k, v := range dict.m { if !consumer(k, v) { break } } } // RandomKeys randomly returns keys of the given number, may contain duplicated key func (dict *SimpleDict) RandomKeys(limit int) []string { result := make([]string, limit) for i := 0; i < limit; i++ { for k := range dict.m { result[i] = k break } } return result } // RandomDistinctKeys randomly returns keys of the given number, won't contain duplicated key func (dict *SimpleDict) RandomDistinctKeys(limit int) []string { size := limit if size > len(dict.m) { size = len(dict.m) } result := make([]string, size) i := 0 for k := range dict.m { if i == limit { break } result[i] = k i++ } return result } // Clear removes all keys in dict func (dict *SimpleDict) Clear() { *dict = *MakeSimple() }
Redis的DB實(shí)現(xiàn)
針對(duì)其中 Command 的實(shí)現(xiàn), ping, set ,Get 有具體的實(shí)現(xiàn)方法, 采用策略模式形式, 不同的方法 有自己的 Executor 執(zhí)行器
// 指令與結(jié)構(gòu)體之間的關(guān)系 var cmdTable = make(map[string]*command) type command struct { executor ExecFunc arity int // allow number of args, arity < 0 means len(args) >= -arity } // RegisterCommand registers a new command // arity means allowed number of cmdArgs, arity < 0 means len(args) >= -arity. // for example: the arity of `get` is 2, `mget` is -2 func RegisterCommand(name string, executor ExecFunc, arity int) { name = strings.ToLower(name) cmdTable[name] = &command{ executor: executor, arity: arity, } }
// Package database is a memory database with redis compatible interface package database import ( "go-redis/datastruct/dict" "go-redis/interface/database" "go-redis/interface/resp" "go-redis/resp/reply" "strings" ) // DB stores data and execute user's commands type DB struct { index int // key -> DataEntity data dict.Dict // 底層的實(shí)現(xiàn)可以進(jìn)行切換 } // ExecFunc is interface for command executor, redis 的 指令實(shí)現(xiàn), 所有的實(shí)現(xiàn) 協(xié)程這種形式 // args don't include cmd line type ExecFunc func(db *DB, args [][]byte) resp.Reply // CmdLine is alias for [][]byte, represents a command line type CmdLine = [][]byte // makeDB create DB instance func makeDB() *DB { db := &DB{ data: dict.MakeSyncDict(), // 使用的 sync 的 dict } return db } // Exec executes command within one database 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:]) } func validateArity(arity int, cmdArgs [][]byte) bool { argNum := len(cmdArgs) if arity >= 0 { return argNum == arity } return argNum >= -arity } /* ---- data Access ----- */ // GetEntity returns DataEntity bind to given key 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 } // PutEntity a DataEntity into DB func (db *DB) PutEntity(key string, entity *database.DataEntity) int { return db.data.Put(key, entity) } // PutIfExists edit an existing DataEntity func (db *DB) PutIfExists(key string, entity *database.DataEntity) int { return db.data.PutIfExists(key, entity) } // PutIfAbsent insert an DataEntity only if the key not exists func (db *DB) PutIfAbsent(key string, entity *database.DataEntity) int { return db.data.PutIfAbsent(key, entity) } // Remove the given key from db func (db *DB) Remove(key string) { db.data.Remove(key) } // Removes the given keys from db 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 } // Flush clean database func (db *DB) Flush() { db.data.Clear() }
具體的實(shí)現(xiàn)器
// Ping Executor 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) }
Redis持久化Aof
AOF 則以協(xié)議文本的方式,將所有對(duì)數(shù)據(jù)庫(kù)進(jìn)行過寫入的命令(及其參數(shù))記錄到 AOF 文件,以此達(dá)到記錄數(shù)據(jù)庫(kù)狀態(tài)的目的.
package aof import ( "go-redis/config" databaseface "go-redis/interface/database" "go-redis/lib/logger" "go-redis/lib/utils" "go-redis/resp/connection" "go-redis/resp/parser" "go-redis/resp/reply" "io" "os" "strconv" ) // CmdLine is alias for [][]byte, represents a command line type CmdLine = [][]byte const ( aofQueueSize = 1 << 16 ) type payload struct { cmdLine CmdLine dbIndex int } // AofHandler receive msgs from channel and write to AOF file type AofHandler struct { db databaseface.Database aofChan chan *payload aofFile *os.File aofFilename string currentDB int } // NewAOFHandler creates a new aof.AofHandler func NewAOFHandler(db databaseface.Database) (*AofHandler, error) { handler := &AofHandler{} handler.aofFilename = config.Properties.AppendFilename handler.db = db // init Load Aof to Memory handler.LoadAof() aofFile, err := os.OpenFile(handler.aofFilename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0600) if err != nil { return nil, err } handler.aofFile = aofFile handler.aofChan = make(chan *payload, aofQueueSize) go func() { handler.handleAof() }() return handler, nil } func (handler *AofHandler) AddAof(dbIndex int, cmdLine CmdLine) { if config.Properties.AppendOnly && handler.aofChan != nil { handler.aofChan <- &payload{ cmdLine: cmdLine, dbIndex: dbIndex, } } } // LoadAof read aof file 數(shù)據(jù)回放的功能 func (handler *AofHandler) LoadAof() { file, err := os.Open(handler.aofFilename) if err != nil { logger.Warn(err) return } defer func(file *os.File) { err := file.Close() if err != nil { } }(file) ch := parser.ParseStream(file) fakeConn := &connection.Connection{} // only used for save dbIndex // LoadAof recover data for p := range ch { if p.Err != nil { if p.Err == io.EOF { break } logger.Error("parse error: " + p.Err.Error()) continue } if p.Data == nil { logger.Error("empty payload") continue } r, ok := p.Data.(*reply.MultiBulkReply) if !ok { logger.Error("require multi bulk reply") continue } ret := handler.db.Exec(fakeConn, r.Args) if reply.IsErrorReply(ret) { logger.Error("exec err", err) } } } // handleAof listen aof channel and write into file aof 異步形式的落盤 func (handler *AofHandler) handleAof() { handler.currentDB = 0 for p := range handler.aofChan { if p.dbIndex != handler.currentDB { // select db data := reply.MakeMultiBulkReply(utils.ToCmdLine("SELECT", strconv.Itoa(p.dbIndex))).ToBytes() _, err := handler.aofFile.Write(data) if err != nil { logger.Warn(err) continue // skip this command } handler.currentDB = p.dbIndex } data := reply.MakeMultiBulkReply(p.cmdLine).ToBytes() _, err := handler.aofFile.Write(data) if err != nil { logger.Warn(err) } } }
到此這篇關(guān)于Redis內(nèi)存數(shù)據(jù)庫(kù)示例分析的文章就介紹到這了,更多相關(guān)Redis內(nèi)存數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
推薦一款I(lǐng)ntelliJ IDEA提示快捷鍵的Key Promoter X插件
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA提示快捷鍵的Key Promoter X插件,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳
這篇文章主要介紹了關(guān)于Java從本地文件復(fù)制到網(wǎng)絡(luò)文件上傳,File?和?IO?流其實(shí)是很相似的,都是將文件從一個(gè)地方轉(zhuǎn)移到另一個(gè)地方,這也是流的特點(diǎn)之一,需要的朋友可以參考下2023-04-04使用maven?shade插件解決項(xiàng)目版本沖突詳解
這篇文章主要為大家介紹了使用maven?shade插件解決項(xiàng)目版本沖突詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09詳解elasticsearch之metric聚合實(shí)現(xiàn)示例
這篇文章主要為大家介紹了elasticsearch之metric聚合實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01java學(xué)習(xí)之JasperReport踩坑
本篇文章介紹的是在JAVA學(xué)習(xí)中JasperReport遇到的坑以及解決辦法,有需要的朋友參考下吧。2018-01-01java代碼實(shí)現(xiàn)mysql分表操作(用戶行為記錄)
這篇文章主要介紹了java代碼實(shí)現(xiàn)mysql分表操作(用戶行為記錄),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2021-02-02多數(shù)據(jù)源@DS和@Transactional實(shí)戰(zhàn)
這篇文章主要介紹了多數(shù)據(jù)源@DS和@Transactional實(shí)戰(zhàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java跳出當(dāng)前的多重嵌套循環(huán)的五種方法
在Java編程中,跳出多重嵌套循環(huán)可以使用break語(yǔ)句、標(biāo)號(hào)與break組合、return語(yǔ)句、標(biāo)志變量和異常處理五種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10