iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >基于Golang怎么实现内存数据库
  • 861
分享到

基于Golang怎么实现内存数据库

2023-07-05 17:07:37 861人浏览 八月长安
摘要

今天小编给大家分享一下基于golang怎么实现内存数据库的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。GO实现内存数据库实现

今天小编给大家分享一下基于golang怎么实现内存数据库的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

GO实现内存数据库

实现Redis的database层(核心层:处理命令并返回)

本文涉及以下文件:dict:定义字典的一些方法

  • sync_dict:实现dict

  • db:分数据库

  • command:定义指令

  • ping,keys,string:指令的具体处理逻辑

  • database:单机版数据库

datastruct/dict/dict.go

type Consumer func(key string, val interface{}) booltype 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数据结构的接口。这里我们使用sync.Map作为字典的实现,如果想用别的数据结构,换一个实现即可

Consumer:遍历字典所有的键值对,返回值是布尔,true继续遍历,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实现Dict接口

database/db.go

type DB struct {index intdata  dict.Dict}type ExecFunc func(db *DB, args [][]byte) resp.Replytype CmdLine = [][]bytefunc 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.executorreturn 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 = 0for _, key := range keys {_, exists := db.data.Get(key)if exists {db.Remove(key)deleted++}}return deleted}func (db *DB) Flush() {db.data.Clear()}

实现Redis中的分数据库

ExecFunc:所有Redis的指令都写成这样的类型

validateArity方法:

  • 定长:set k v => arity=3;

  • 变长:exists k1 k2 k3 ... => arity=-2,表示参数>=2个

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:每一个command结构体都是一个指令,例如ping,keys等等

  • arity:参数数量

  • cmdTable:记录所有指令和command结构体的关系

  • RegisterCommand:注册指令的实现,在程序

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方法:在启动程序时就会调用这个方法,用于初始化

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实现以下指令:

  • 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实现以下指令:

  • 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:执行切换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,   }}

修改实现协议层handler的database实现

架构小结

tcp层服务TCP的连接,然后将连接交给RESP协议层的handler,handler监听客户端的连接,将指令解析后发给管道,管道转给database层(database/database.go),核心层根据命令类型执行不同的方法,然后返回。

以上就是“基于Golang怎么实现内存数据库”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网GO频道。

您可能感兴趣的文档:

--结束END--

本文标题: 基于Golang怎么实现内存数据库

本文链接: https://www.lsjlt.com/news/353455.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 基于Golang怎么实现内存数据库
    今天小编给大家分享一下基于Golang怎么实现内存数据库的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。GO实现内存数据库实现...
    99+
    2023-07-05
  • golang内存数据库怎么实现
    在Golang中实现一个内存数据库可以通过以下步骤: 定义数据库结构:首先,你需要确定数据库的结构,即定义每个表的字段以及数据类型...
    99+
    2023-10-23
    golang
  • Golang如何实现事务型内存数据库
    这篇文章主要讲解了“Golang如何实现事务型内存数据库”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang如何实现事务型内存数据库”吧!特性MossDB是一个纯Golang编写、可嵌...
    99+
    2023-07-05
  • 基于内存的关系数据库memsql初探
    背景 广告系统中,算法模型预估需要根据广告的实时转化统计结果,才能做出更精准的预估;同时,支持多维度聚合查询(例如按照广告各个不同层级维度,按照时间不同粒度的维度),并跨大区合并。一开始的版本是基于mysql,但由于统计数据更新...
    99+
    2019-10-28
    基于内存的关系数据库memsql初探
  • Golang实现事务型内存数据库的方法详解
    目录特性命名由来架构快速开始具体实现AOF持久化存储引擎事务实现WatchTTL总结内存数据库经我们经常用到,例如Redis,那么如何从零实现一个内存数据库呢,本文旨在介绍如何使用G...
    99+
    2023-03-03
    Golang实现事务型内存数据库 Golang 内存数据库 Golang 数据库
  • java基于数据库怎么实现全局唯一ID
    本篇内容主要讲解“java基于数据库怎么实现全局唯一ID”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java基于数据库怎么实现全局唯一ID”吧!一、原理  在数据库中建立ID序列表记...
    99+
    2023-07-06
  • 基于Android如何实现将数据库保存到SD卡
    有时候为了需要,会将数据库保存到外部存储或者SD卡中(对于这种情况可以通过加密数据来避免数据被破解),比如一个应用支持多个数据,每个数据都需要有一个对应的数据库,并且数据库中的...
    99+
    2022-06-06
    sd sd卡 数据库 数据 Android
  • 基于Canal实现MySQL 8.0 数据库数据同步
    前言 服务器说明 主机名称操作系统说明192.168.11.82Ubuntu 22.04主库所在服务器192.168.11.28Oracle Linux Server 8.7从库所在服务器 版本说明 ...
    99+
    2023-10-25
    数据库 mysql 服务器
  • MySQL数据库基于sysbench实现OLTP基准测试
    sysbench是一款非常优秀的基准测试工具,它能够精准的模拟MySQL数据库存储引擎InnoDB的磁盘的I/O模式。因此,基于sysbench的这个特性,下面利用该工具,对MySQL数据库支撑从简单到复杂事务处理工...
    99+
    2022-05-30
    MySQL 数据库 OLTP 基准测试 sysbench
  • golang手动释放内存怎么实现
    在Go语言中,内存管理是由垃圾回收器自动完成的,不需要手动释放内存。但是,如果有特殊的需求需要手动释放内存,可以使用`runtime...
    99+
    2023-10-26
    golang
  • golang数据库自动迁移怎么实现
    在Go语言中,实现数据库自动迁移可以使用一些第三方库,如gorm、migrate等。下面以使用gorm库实现数据库自动迁移为例进行说...
    99+
    2023-10-20
    golang 数据库
  • golang并发查询数据库怎么实现
    在Go语言中实现并发查询数据库可以使用goroutine和channel来实现。以下是一个简单的示例代码: package main...
    99+
    2023-10-25
    golang 数据库
  • 基于C++实现Mysql数据库连接池实例
    目录项目技术点项目意义项目实现Connection设计ConnectionPool设计项目复杂接口细节刨析项目技术点 C语言进行mysql数据库编程无锁单例基于STL队列加C++11新特性保证线程安全实现的生产者消费者模...
    99+
    2022-12-07
    C++数据库连接池 C++mysql连接池
  • 基于percona xtrabackup的innobackupex如何实现基于时间点数据库恢复
    基于percona xtrabackup的innobackupex如何实现基于时间点数据库恢复,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望...
    99+
    2022-10-18
  • 使用golang怎么实现mysql数据库备份
    这期内容当中小编将会给大家带来有关使用golang怎么实现mysql数据库备份,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。map & json,在处理主键与外键...
    99+
    2022-10-18
  • 基于Java怎么实现Redis多级缓存
    这篇文章主要介绍“基于Java怎么实现Redis多级缓存”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于Java怎么实现Redis多级缓存”文章能帮助大家解决问题。一、多级缓存1. 传统缓存方案请...
    99+
    2023-06-29
  • Jmeter基于JDBC请求实现MySQL数据库测试
    理论知识部分: 一、简单总结几点数据库测试点: 检查接口返回的数据是否与预期一致 传递数据类型错误时能否处理,比如数据类型要求是整数,传递小数时能否处理 接口参数的边界值 接口处理数据的时间 接口的安全性 二、Jem...
    99+
    2022-05-19
    Jmeter JDBC请求 MySQL 数据库 测试
  • Spring boot基于JPA访问MySQL数据库的实现
    本文展示如何通过JPA访问MySQL数据库。 JPA全称Java Persistence API,即Java持久化API,它为Java开发人员提供了一种对象/关系映射工具来管理Jav...
    99+
    2022-11-12
  • 数据库内存共享实现原理是什么
    这篇文章主要讲解了“数据库内存共享实现原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“数据库内存共享实现原理是什么”吧!共享内存是进程间通讯的一种方...
    99+
    2022-10-18
  • 基于Redis缓存怎么实现分布式锁
    本篇内容介绍了“基于Redis缓存怎么实现分布式锁”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是分布式锁首先我们先来简单了解一下什么是...
    99+
    2023-06-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作