自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SaaS多租戶架構(gòu)數(shù)據(jù)源動(dòng)態(tài)切換解決方案

開(kāi)發(fā) 架構(gòu)
在實(shí)際應(yīng)用中,數(shù)據(jù)同步操作可能涉及到復(fù)雜的數(shù)據(jù)映射和處理邏輯,需要根據(jù)具體的業(yè)務(wù)需求進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。同時(shí),為了保障系統(tǒng)的穩(wěn)定性和性能,可能還需要考慮引入事務(wù)管理、批量處理和異步處理等機(jī)制。

概述

隨著云計(jì)算和SaaS(Software as a Service)模型的興起,多租戶系統(tǒng)成為了構(gòu)建靈活、高效應(yīng)用的重要架構(gòu)。在構(gòu)建多租戶SaaS平臺(tái)時(shí),數(shù)據(jù)庫(kù)方案的選擇直接關(guān)系到數(shù)據(jù)隔離、性能和可擴(kuò)展性。

在SaaS平臺(tái)項(xiàng)目中,根據(jù)前端不同的域名查詢不同的數(shù)據(jù)庫(kù),通常涉及到多租戶架構(gòu)的實(shí)現(xiàn)。在這種架構(gòu)中,一個(gè)應(yīng)用實(shí)例可以服務(wù)多個(gè)客戶(租戶)【數(shù)據(jù)庫(kù)】,每個(gè)租戶的數(shù)據(jù)需要隔離存儲(chǔ)。實(shí)現(xiàn)這一目標(biāo)的關(guān)鍵技術(shù)之一就是動(dòng)態(tài)切換數(shù)據(jù)庫(kù)連接。

設(shè)計(jì)多租戶數(shù)據(jù)模型

在數(shù)據(jù)庫(kù)設(shè)計(jì)階段,你需要決定數(shù)據(jù)隔離的級(jí)別。通常有以下幾種隔離級(jí)別:

  • 獨(dú)立數(shù)據(jù)庫(kù):每個(gè)租戶擁有一個(gè)獨(dú)立的數(shù)據(jù)庫(kù)實(shí)例。
  • 共享數(shù)據(jù)庫(kù),獨(dú)立Schema:所有租戶共享同一個(gè)數(shù)據(jù)庫(kù),但每個(gè)租戶有獨(dú)立的Schema。
  • 共享數(shù)據(jù)庫(kù),共享Schema,共享數(shù)據(jù)表:所有租戶共享數(shù)據(jù)庫(kù)、Schema和數(shù)據(jù)表,但通過(guò)租戶ID字段進(jìn)行數(shù)據(jù)隔離。

共享數(shù)據(jù)庫(kù),獨(dú)立Schema

"共享數(shù)據(jù)庫(kù),獨(dú)立Schema" 是一種在SaaS平臺(tái)中實(shí)現(xiàn)多租戶架構(gòu)的策略,它在數(shù)據(jù)庫(kù)層面上提供了一種折中的數(shù)據(jù)隔離方法。

Oracle數(shù)據(jù)庫(kù):在Oracle中一個(gè)數(shù)據(jù)庫(kù)可以具有多個(gè)用戶,那么一個(gè)用戶一般對(duì)應(yīng)一個(gè)Schema,表都是建立在Schema中的,(可以簡(jiǎn)單的理解:在Oracle中一個(gè)用戶一套數(shù)據(jù)庫(kù)表)

圖片圖片

在 MySQL 中,Schema 和 Database 可以認(rèn)為是相同的概念。在 SQL 語(yǔ)句中,CREATE DATABASE 和 CREATE SCHEMA 基本上是等效的。所以,當(dāng)你創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)時(shí),你也在事實(shí)上創(chuàng)建了一個(gè)模式。模式是一個(gè)邏輯上的容器,用于組織和管理數(shù)據(jù)庫(kù)對(duì)象,如表、視圖、存儲(chǔ)過(guò)程等。在 MySQL 中,模式和數(shù)據(jù)庫(kù)可以互換使用。

共享數(shù)據(jù)庫(kù)

在這種模式下,所有的租戶(即SaaS平臺(tái)的客戶)共享同一個(gè)物理數(shù)據(jù)庫(kù)服務(wù)器或數(shù)據(jù)庫(kù)實(shí)例。這意味著,盡管每個(gè)租戶都有自己的數(shù)據(jù),但這些數(shù)據(jù)都存儲(chǔ)在同一個(gè)數(shù)據(jù)庫(kù)文件或數(shù)據(jù)庫(kù)集群中。這樣做的好處是可以減少硬件資源和維護(hù)成本,因?yàn)椴恍枰獮槊總€(gè)租戶單獨(dú)設(shè)置和維護(hù)數(shù)據(jù)庫(kù)實(shí)例。

獨(dú)立Schema

盡管數(shù)據(jù)庫(kù)是共享的,但每個(gè)租戶都有自己獨(dú)立的Schema。Schema是數(shù)據(jù)庫(kù)中的一種邏輯分組,它包含了一系列的數(shù)據(jù)庫(kù)對(duì)象,如表、視圖、索引、存儲(chǔ)過(guò)程等。在這個(gè)模式下,每個(gè)租戶的數(shù)據(jù)都存儲(chǔ)在自己的Schema中,這樣可以保證租戶之間的數(shù)據(jù)邏輯上是隔離的。

例如,假設(shè)有兩個(gè)租戶A和B,他們共享同一個(gè)數(shù)據(jù)庫(kù)"SaaSDB"。在"SaaSDB"中,可以分別為租戶A和租戶B創(chuàng)建兩個(gè)Schema(數(shù)據(jù)庫(kù)),分別是"SchemaA"和"SchemaB"。租戶A的所有數(shù)據(jù)都存儲(chǔ)在"SchemaA"中,而租戶B的數(shù)據(jù)存儲(chǔ)在"SchemaB"中。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)
  1. 資源利用率高:由于所有租戶共享同一個(gè)數(shù)據(jù)庫(kù),硬件資源和數(shù)據(jù)庫(kù)維護(hù)成本較低。
  2. 易于管理:數(shù)據(jù)庫(kù)管理員只需要管理一個(gè)數(shù)據(jù)庫(kù)實(shí)例,簡(jiǎn)化了維護(hù)和升級(jí)的工作。
  3. 隔離性:每個(gè)租戶的數(shù)據(jù)存儲(chǔ)在獨(dú)立的Schema中,邏輯上實(shí)現(xiàn)了數(shù)據(jù)隔離,減少了數(shù)據(jù)交叉污染的風(fēng)險(xiǎn)。
缺點(diǎn)
  1. 隔離性不如獨(dú)立數(shù)據(jù)庫(kù):雖然Schema提供了一定程度的隔離,但如果Schema之間存在依賴關(guān)系或需要進(jìn)行復(fù)雜的數(shù)據(jù)操作,隔離性可能不如完全獨(dú)立的數(shù)據(jù)庫(kù)。
  2. 性能問(wèn)題:如果租戶數(shù)量增多,可能會(huì)導(dǎo)致數(shù)據(jù)庫(kù)性能問(wèn)題,因?yàn)樗凶鈶舳荚诟?jìng)爭(zhēng)同一個(gè)數(shù)據(jù)庫(kù)資源。

總體來(lái)說(shuō),"共享數(shù)據(jù)庫(kù),獨(dú)立Schema" 的模式在SaaS平臺(tái)中是一種常見(jiàn)的多租戶數(shù)據(jù)隔離策略,它在資源利用率和數(shù)據(jù)隔離性之間取得了平衡。開(kāi)發(fā)者需要根據(jù)具體的業(yè)務(wù)需求和預(yù)期的租戶規(guī)模來(lái)決定是否采用這種模式。

SaaS多租戶架構(gòu)數(shù)據(jù)庫(kù)設(shè)計(jì)

重點(diǎn):在 SQL 語(yǔ)句中,CREATE DATABASE 和 CREATE SCHEMA 基本上是等效的。所以,當(dāng)你創(chuàng)建一個(gè)SCHEMA時(shí),就是在一個(gè)RDS實(shí)例下創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)DATABASE。

圖片圖片

以newtrain.tinywan.com、hz_newtrain.tinywan.com、bj_newtrain.tinywan.com三個(gè)域名為例,每個(gè)域名對(duì)應(yīng)一個(gè)租戶平臺(tái)站點(diǎn),分別對(duì)應(yīng)各自的數(shù)據(jù)源數(shù)據(jù)庫(kù)newtrain.tinywan.com、hangzhou.tinywan.com、beijing.tinywan.com。

實(shí)施方案

域名解析與路由

  • 在DNS系統(tǒng)中為每個(gè)域名配置A記錄,指向SaaS平臺(tái)的服務(wù)器
  • 在服務(wù)器上部署Web應(yīng)用,并根據(jù)請(qǐng)求的Host頭部信息,確定租戶身份。

數(shù)據(jù)源配置

  • 在應(yīng)用程序的配置文件中,定義每個(gè)租戶的數(shù)據(jù)源配置,包括數(shù)據(jù)庫(kù)URL、用戶名和密碼
  • 可以使用環(huán)境變量或配置中心來(lái)動(dòng)態(tài)加載這些配置。

動(dòng)態(tài)數(shù)據(jù)源切換

根據(jù)請(qǐng)求的域名或其他標(biāo)識(shí)符,動(dòng)態(tài)確定使用哪個(gè)數(shù)據(jù)庫(kù)連接。這通常通過(guò)中間件、攔截器或全局函數(shù)來(lái)實(shí)現(xiàn)。

示例:使用PHP實(shí)現(xiàn)域名路由中間件

<?php
/**
 * @desc 域名路由中間件
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/20 18:14
 */
declare(strict_types=1);

namespace app\middleware;

use app\common\model\SaasModel;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;

class ConnectionMiddleware implements MiddlewareInterface
{
    /**
     * @param Request $request
     * @param callable $handler
     * @return Response
     */
    public function process(Request $request, callable $handler): Response
    {
        $domain = $request->header()['x-site-domain']?? 'https://newtrain.tinywan.com';
        $platform = SaasModel::where('domain', $domain)->field('id, domain, website')->findOrEmpty();
        if (!$platform->isEmpty()) {
            $request->website = $platform['website'];
        }
        return $handler($request);
    }
}

以上根據(jù)前端請(qǐng)求的域名標(biāo)識(shí)符x-site-domain,動(dòng)態(tài)確定使用哪個(gè)數(shù)據(jù)庫(kù)連接,通過(guò)中間件動(dòng)態(tài)賦予全局請(qǐng)求對(duì)象$request->website,后續(xù)就可以使用。

項(xiàng)目應(yīng)用

項(xiàng)目架構(gòu)

項(xiàng)目使用超高性能可擴(kuò)展PHP框架webman。webman是一款基于workerman開(kāi)發(fā)的高性能HTTP服務(wù)框架。webman用于替代傳統(tǒng)的php-fpm架構(gòu),提供超高性能可擴(kuò)展的HTTP服務(wù)。你可以用webman開(kāi)發(fā)網(wǎng)站,也可以開(kāi)發(fā)HTTP接口或者微服務(wù)。

數(shù)據(jù)庫(kù)連接使用ThinkORM。ThinkORM是一個(gè)基于PHP和PDO的數(shù)據(jù)庫(kù)中間層和ORM類(lèi)庫(kù),之前一直作為T(mén)hinkPHP5.*系列的內(nèi)置ORM類(lèi),以優(yōu)異的功能和突出的性能著稱,現(xiàn)已經(jīng)支持獨(dú)立使用,并作了升級(jí)改進(jìn),提供了更優(yōu)秀的性能和開(kāi)發(fā)體驗(yàn),最新版本要求PHP7.1+。

數(shù)據(jù)庫(kù)連接中間

示例:域名路由中間件

<?php
/**
 * @desc 域名路由中間件
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/20 18:14
 */
declare(strict_types=1);

namespace app\middleware;

use app\common\model\SaasModel;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;

class ConnectionMiddleware implements MiddlewareInterface
{
    /**
     * @param Request $request
     * @param callable $handler
     * @return Response
     */
    public function process(Request $request, callable $handler): Response
    {
        $domain = $request->header()['x-site-domain']?? 'https://newtrain.tinywan.com';
        $platform = SaasModel::where('domain', $domain)->field('id, domain, website')->findOrEmpty();
        if (!$platform->isEmpty()) {
            $request->website = $platform['website'];
        }
        return $handler($request);
    }
}

數(shù)據(jù)庫(kù)配置

ThinkORM配置文件:config/thinkorm.php

<?php
/**
 * @desc ThinkORM配置文件
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/14 15:14
 */
declare(strict_types=1);

return [
    'default' => 'train',
    'connections' => [
        'train' => [
            'type' => 'mysql',
            'hostname' => '127.0.0.1',
            'database' => 'newtrain.tinywan.com',
            'username' => 'root',
            'password' => '123456'
        ],
        'hangzhou' => [
            'type' => 'mysql',
            'hostname' => '127.0.0.1',
            'database' => 'hangzhou.tinywan.com',
            'username' => 'root',
            'password' => '123456'
        ],
        'beijing' => [
            'type' => 'mysql',
            'hostname' => '127.0.0.1',
            'database' => 'beijing.tinywan.com',
            'username' => 'root',
            'password' => '123456'
        ]
    ],
];

Model模型使用

BaseModel.php 基礎(chǔ)模型

<?php
/**
 * @desc 基礎(chǔ)模型
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/2 15:09
 */
declare(strict_types=1);

namespace app\common\model;

use think\Model;

class BaseModel extends Model
{
    /**
     * 設(shè)置當(dāng)前模型的數(shù)據(jù)庫(kù)連接
     * @var string
     */
    protected $connection;

    /**
     * BaseModel constructor.
     * @param array $data
     */
    public function __construct(array $data = [])
    {
        $this->connection = \request()->website ?? 'train';
        parent::__construct($data);
    }
}

CityModel.php 公共模型,對(duì)應(yīng)數(shù)據(jù)庫(kù)表名common_city。

<?php
/**
 * @desc 市模型
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/12/13 14:59
 */
declare(strict_types=1);

namespace app\common\model;

use think\Model;

class CityModel extends Model
{
    /** 數(shù)據(jù)庫(kù)配置 */
    protected $connection = 'train';

    /** 設(shè)置當(dāng)前模型對(duì)應(yīng)的完整數(shù)據(jù)表名稱 */
    protected $table = 'common_city';
}

公共模型CityModel類(lèi)里面定義了connection屬性,則該模型操作的時(shí)候會(huì)自動(dòng)按照給定的數(shù)據(jù)庫(kù)配置進(jìn)行連接,而不是配置文件中設(shè)置的默認(rèn)連接信息.

業(yè)務(wù) MeetingModel.php 會(huì)議模型類(lèi)。對(duì)應(yīng)數(shù)據(jù)庫(kù)表名resty_meeting

<?php
/**
 * @desc 會(huì)議模型類(lèi)
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/17 11:55
 */
declare(strict_types=1);

namespace app\common\model;

class MeetingModel extends BaseModel
{
    /** 設(shè)置當(dāng)前模型對(duì)應(yīng)的完整數(shù)據(jù)表名稱 */
    protected $table = 'resty_meeting';
}

業(yè)務(wù)控制器或者服務(wù)使用

<?php
/**
 * @desc 會(huì)議
 * @author Tinywan(ShaoBo Wan)
 * @date 2023/11/9 16:57
 */
declare(strict_types=1);

public function meetingList(\support\Request $request, int $organizationId) : \support\Response
{
    $meetingList = \app\common\model\MeetingModel::where([
        'organization_id' => $organizationId,
        'create_user_id' => $this->userId
    ])->select();
    return json($meetingList->toArray());
}

Db類(lèi)使用

可以調(diào)用Db::connect方法動(dòng)態(tài)配置數(shù)據(jù)庫(kù)連接信息

<?php
/**
 * @desc 會(huì)議
 * @author Tinywan(ShaoBo Wan)
 * @date 2023/11/9 16:57
 */
declare(strict_types=1);

public function datasetList(\support\Request $request) : \support\Response
{
    $res = \think\facade\Db::connect(\request()->website)
        ->table('resty_meeting')
        ->field('id,name')
        ->select();
    return json($res->toArray());
}

connect方法必須在查詢的最開(kāi)始調(diào)用,而且必須緊跟著調(diào)用查詢方法,否則可能會(huì)導(dǎo)致部分查詢失效或者依然使用默認(rèn)的數(shù)據(jù)庫(kù)連接。動(dòng)態(tài)連接數(shù)據(jù)庫(kù)的connect方法僅對(duì)當(dāng)次查詢有效。這種方式的動(dòng)態(tài)連接和切換數(shù)據(jù)庫(kù)比較方便,經(jīng)常用于多數(shù)據(jù)庫(kù)連接的應(yīng)用需求。

動(dòng)態(tài)連接到目標(biāo)數(shù)據(jù)庫(kù)

在SaaS平臺(tái)中,如果需要根據(jù)前端傳遞的配置信息動(dòng)態(tài)連接到目標(biāo)數(shù)據(jù)庫(kù)并將數(shù)據(jù)拉取到本地?cái)?shù)據(jù)庫(kù),可以采用以下步驟實(shí)現(xiàn)

  • 前端傳遞配置信息。前端在用戶操作時(shí),將目標(biāo)數(shù)據(jù)庫(kù)的連接信息作為請(qǐng)求參數(shù)發(fā)送到后端。這些配置信息通常包括數(shù)據(jù)庫(kù)類(lèi)型、主機(jī)地址、端口、數(shù)據(jù)庫(kù)名、用戶名和密碼等。
  • 驗(yàn)證和解析配置信息。后端接收到配置信息后,首先進(jìn)行驗(yàn)證,確保其合法性和安全性。解析配置信息,并準(zhǔn)備用于數(shù)據(jù)庫(kù)連接的參數(shù)。
  • 動(dòng)態(tài)數(shù)據(jù)源管理。創(chuàng)建一個(gè)動(dòng)態(tài)數(shù)據(jù)源管理器,它可以根據(jù)傳入的配置信息動(dòng)態(tài)創(chuàng)建數(shù)據(jù)庫(kù)連接。
  • 數(shù)據(jù)同步。根據(jù)目標(biāo)數(shù)據(jù)庫(kù)的連接信息,建立連接并執(zhí)行數(shù)據(jù)查詢操作。然后將查詢結(jié)果同步到本地?cái)?shù)據(jù)庫(kù)。這可能涉及到以下步驟:

建立連接:使用動(dòng)態(tài)數(shù)據(jù)源管理器創(chuàng)建的目標(biāo)數(shù)據(jù)庫(kù)連接。

執(zhí)行查詢:在目標(biāo)數(shù)據(jù)庫(kù)上執(zhí)行SQL查詢,獲取所需數(shù)據(jù)。

映射數(shù)據(jù):將查詢結(jié)果映射到本地?cái)?shù)據(jù)庫(kù)的表結(jié)構(gòu)中。

寫(xiě)入本地?cái)?shù)據(jù)庫(kù):將映射后的數(shù)據(jù)插入到本地?cái)?shù)據(jù)庫(kù)中。

  • 異常處理和日志記錄。在整個(gè)數(shù)據(jù)同步過(guò)程中,需要妥善處理可能出現(xiàn)的異常情況,并記錄相關(guān)操作日志,以便于問(wèn)題追蹤和系統(tǒng)維護(hù)。
  • 安全性考慮
  • 加密敏感信息:確保所有的數(shù)據(jù)庫(kù)憑證信息在存儲(chǔ)和傳輸過(guò)程中都是加密的。
  • 權(quán)限控制:確保只有授權(quán)的用戶或服務(wù)才能訪問(wèn)數(shù)據(jù)同步功能。
  • SQL注入防護(hù):對(duì)動(dòng)態(tài)執(zhí)行的SQL進(jìn)行嚴(yán)格的安全檢查,避免SQL注入攻擊。

自定義函數(shù)

函數(shù)配置文件app/functions.php新增函數(shù)dynamic_connect_db()

/**
 * @desc: 動(dòng)態(tài)切換數(shù)據(jù)庫(kù)
 * @param string $name
 * @param array $connection
 * @return \think\db\ConnectionInterface
 * @author Tinywan(ShaoBo Wan)
 */
function dynamic_connect_db(string $name, array $connection): \think\db\ConnectionInterface
{
    try {
        $connect = \think\facade\Db::connect($name);
    } catch (\Throwable $e) {
        // 獲取配置參數(shù)
        $config  = \think\facade\Db::getConfig();

        // 配置具體的數(shù)據(jù)庫(kù)連接信息
        $config['connections'][$name] = $connection;

        // 初始化配置參數(shù)
        \think\facade\Db::setConfig($config);

        // 創(chuàng)建/切換數(shù)據(jù)庫(kù)連接查詢
        $connect = \think\facade\Db::connect($name);
    }
    return $connect;
}

動(dòng)態(tài)使用

調(diào)用自定義函數(shù)dynamic_connect_db()方法動(dòng)態(tài)數(shù)據(jù)庫(kù)連接查詢,這里查詢一個(gè)不存在的配置數(shù)據(jù)庫(kù)zhejiang 浙江站點(diǎn)。

/**
 * @desc: 動(dòng)態(tài)切換數(shù)據(jù)庫(kù)
 * @param Request $request
 * @return Response
 * @throws DataNotFoundException
 * @throws DbException
 * @throws ModelNotFoundException
 * @author Tinywan(ShaoBo Wan)
 */
public function dynamicConnectDb(Request $request): Response
{
    $connection = [
        'type' => 'mysql',
        'hostname' => '127.0.0.1',
        'database' => 'zhejiang.tinywan.com',
        'username' => 'root',
        'password' => '123456'
    ];
    $connect = dynamic_connect_db('zhejiang', $connection);
    $result = $connect->table('resty_meeting')->where('id', 1)->find();
    var_dump($result);
    return json($result->toArray());
}

在實(shí)際應(yīng)用中,數(shù)據(jù)同步操作可能涉及到復(fù)雜的數(shù)據(jù)映射和處理邏輯,需要根據(jù)具體的業(yè)務(wù)需求進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。同時(shí),為了保障系統(tǒng)的穩(wěn)定性和性能,可能還需要考慮引入事務(wù)管理、批量處理和異步處理等機(jī)制。

責(zé)任編輯:武曉燕 來(lái)源: 開(kāi)源技術(shù)小棧
相關(guān)推薦

2015-08-12 15:46:02

SaaS多租戶數(shù)據(jù)存儲(chǔ)

2023-11-29 08:35:28

群多租戶ES運(yùn)維

2023-06-07 13:50:00

SaaS多租戶系統(tǒng)

2023-12-14 12:26:16

SaaS數(shù)據(jù)庫(kù)方案

2022-01-12 17:39:16

Spring多租戶數(shù)據(jù)

2020-09-15 07:00:00

SaaS架構(gòu)架構(gòu)

2025-01-09 14:39:40

2022-05-10 10:43:35

數(shù)據(jù)源動(dòng)態(tài)切換Spring

2025-01-17 09:11:51

2018-12-03 12:07:54

南京新動(dòng)態(tài)解決方案

2015-04-02 11:04:27

云應(yīng)用SaaSOFBIZ

2009-09-22 11:56:58

ibmdwSaaS

2023-02-06 14:44:00

嚴(yán)選數(shù)據(jù)源DB

2016-12-28 18:08:11

RiverbedSaaS數(shù)字化轉(zhuǎn)型

2022-09-13 07:14:29

云計(jì)算SaaS多租戶

2024-05-28 08:17:54

2011-07-29 10:21:03

iPad 橫豎屏 切換

2023-01-04 09:33:31

SpringBootMybatis

2023-12-13 12:20:36

SpringMySQL數(shù)據(jù)源
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)