Go 简单实现多租户数据库隔离

 更新时间:2023年05月14日 08:48:51   作者:小雄Ya  
本文主要介绍了Go 简单实现多租户数据库隔离,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

多租户

在 SaaS 系统中多租户是一个很重要的架构,在服务上仅需运行一个软件实例就能够支持每个租户的功能,它们之间的数据互相隔离。就比如 Gitee ,每个用户或组织都拥有自己的空间。它们自己的数据仅在自己的空间内流通,彼此之间互不影响。多租户其实就是一种软件设计结构。
所以。租户模型其实就是分离数据的方式:

  • 单租户:一个应用一个数据库。其实就是应用分离,数据分离。
  • 多租户:一个应用多个数据库。就是同一个应用不同数据库。

为什么需要多租户?

刚毕业第一家公司,接触的第一个项目的内容主要帮助客户提高生产力。每次有一个很大的问题就是,合作一个客户,项目复制一份出来,然后改成该客户定制的功能需求。这样就导致相同功能要更新多份代码,还要注意可能会出现因不同客户的不同需求问题。整个过程就导致了一个很大的维护成本的支出。

多租户应用程序的维护更容易,只需根据每个租户专属标志,然后提供不同的功能,且公共的功能可以集中更新升级。

数据隔离方式

在 SaaS 系统中一般分离租户的数据有两种模型:

  • 数据的逻辑分离:所有租户只使用一个数据库。它们的数据通过为每个租户使用一个唯一标识符来分隔。
  • 数据的物理分离:一个租户分配一个数据库。就是一个应用对应多个数据库。每个数据库就是一个租户。这种方式在客户的增长时扩展了应用的功能,也方便扩展数据库

物理分离详细实现

目标:每个请求过来,应用程序能够识别租户,并从该租户数据库中进行提供数据。

  • 公共数据库:存储所有租户相关全局配置和所有租户数据库信息。
  • 租户数据库:每个租户独立的的数据库,根据租户需要保存数据。

启动服务

创建 main 文件,并使用 Gin 框架。初始化一个简单的应用程序并创建基本的路由。

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
        })
    })
    r.Run()
}

数据库配置

在公共数据库创建一个保存租户数据库信息表并添加上租户的数据库。

Create Table db_tenant (
    id int primary key,
    db_name varchar(100) unique not null,
    db_domain  varchar(100) unique not null,
    conn_str varchar(200) not null,
    remark varchar(1000) ,
)

在配置文件中加入一个公共数据库的连接配置,这样就可以在服务启动时第一次连接的是公共数据库。接着需要实现根据请求连接到正确的租户数据库。主要使用 gorm 的 DBResolver ,多个数据库的连接支持

func allDbConnect(db *db.gorm) (allDbs map[string]) {
     db.Model(&db_tenant).Find(&allDbs)
    var gconn []gorm.Dialector
      for _,connStr := range allDbs {
       db :=mysql.Open(connStr)
                gconn = append(gconn,db)
    }
db.Use(dbresolver.Register(dbresolver.Config{
  Sources:  gconn,
  // sources/replicas 负载均衡策略
  Policy: dbresolver.RandomPolicy{},
})
   return allDbs
}

通过使用中间件来解析每个请求连接,从而确定整个请求是由哪个租户数据库来提供数据读写。中间件中主要的处理方式是得到租户对应的数据库连接名,然后从连接池中拿到该连接并设置在全局变量中。在业务逻辑处理中从全局拿到数据库连接,就可以进行数据库读写。

sr.Use(middlerware.dbResolve)
func dbResolve(ctx *gin.Context) {
    dbName := ctx.Query("dbName")
    setGlobalDb(dbName)
}
func dbOperater(){
    db := getDb()
  var users []User
  db.Clauses(dbresolver.Use(dbName)).First(&users)
}

这样就可以启动项目,然后访问 localhost:8003?dbName=db1 ,这样就可以正常进行数据读写。

其实可以从域名上解析对应的数据库连接。这里最重要就是对数据库的解析。这里的例子只是实验使用,其中还有很多可优化的点。比如可以考虑使用连接池,这样就不用在项目启动时都全部创建数据库连接,造成浪费。

到此这篇关于Go 简单实现多租户数据库隔离的文章就介绍到这了,更多相关Go  多租户隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言使用goroutine及通道实现并发详解

    Go语言使用goroutine及通道实现并发详解

    这篇文章主要为大家介绍了Go语言使用goroutine及通道实现并发详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Swaggo零基础入门教程

    Swaggo零基础入门教程

    swagger是一套基于OpenAPI规范构建的开源工具,使用RestApi。swagger-ui呈现出来的是一份可交互式的API文档,可以直接在文档页面尝试API的调用
    2023-01-01
  • Go 通过结构struct实现接口interface的问题

    Go 通过结构struct实现接口interface的问题

    这篇文章主要介绍了Go 通过结构struct实现接口interface的问题,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • Golang中的crypto/ecdh包使用详解

    Golang中的crypto/ecdh包使用详解

    这篇文章主要给大家详细介绍了Golang 中的 crypto/ecdh 包,主要包括什么是ECDH 算法和crypto/ecdh 包的使用方法,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • Golang 流水线设计模式实践示例详解

    Golang 流水线设计模式实践示例详解

    这篇文章主要为大家介绍了Golang 流水线设计模式实践示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 在Gin框架中解决跨域问题的多种方法

    在Gin框架中解决跨域问题的多种方法

    在使用Go语言进行Web开发时,Gin框架因其简洁、高效的特点而被广泛使用,然而,在实际开发中,跨域问题(CORS, Cross-Origin Resource Sharing)是一个常见的挑战,本文将结合实际案例,详细介绍在Gin框架中解决跨域问题的多种方法,需要的朋友可以参考下
    2024-10-10
  • 基于go语言实现图片验证码的代码示例

    基于go语言实现图片验证码的代码示例

    这篇文章主要为大家详细介绍了基于go语言实现图片验证码的代码示例,文中的示例代码简洁易懂,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • Go语言Zap日志库使用教程

    Go语言Zap日志库使用教程

    在项目开发中,经常需要把程序运行过程中各种信息记录下来,有了详细的日志有助于问题排查和功能优化;但如何选择和使用性能好功能强大的日志库,这个就需要我们从多角度考虑
    2023-02-02
  • 使用Golang实现Sm2加解密的代码详解

    使用Golang实现Sm2加解密的代码详解

    本文主要介绍了Go语言实现Sm2加解密的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • Go指针数组和数组指针的具体使用

    Go指针数组和数组指针的具体使用

    go语言跟c语言一样,指针数组和数组指针概念容易搞混,本文主要介绍了Go指针数组和数组指针的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03

最新评论