99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

海量數(shù)據(jù):可配置的分庫分表

2018-11-21 21:19 更新

2.12.1 背景

為了應(yīng)對產(chǎn)品海量用戶的愿景需求,這里將設(shè)計一個分布式的數(shù)據(jù)庫存儲方案,以便能滿足數(shù)據(jù)量的驟增、云服務(wù)的橫向擴展、后臺接口開發(fā)的兼容性,以及數(shù)據(jù)遷移等問題,避免日后因為全部數(shù)據(jù)都存放在單臺服務(wù)器上的限制。

2.12.2 主要思想

  • 1、分庫分表
  • 2、路由規(guī)則
  • 3、擴展字段
  • 4、可配置
  • 5、SQL語句自動生成

(1)分庫分表

是指將不需要進行必要關(guān)聯(lián)查詢的表分開存放,如存放事件推送的weili_event_pushto和存放標(biāo)簽的weili_tag;同時,對于 同一個表,因為存放的數(shù)據(jù)量是可預(yù)見式的暴增,如上述的weili_event_pushto,每時每刻都會產(chǎn)生大量的來自用戶發(fā)布的事件,因此為了突破 MySQL單表的限制以及其他問題,需要將此表同時創(chuàng)建N份。

(2)路由規(guī)則

在上面進行了分庫分表后,開發(fā)人員在讀取時,就需要根據(jù)相應(yīng)的規(guī)則找到對應(yīng) 的數(shù)據(jù)庫和數(shù)據(jù)庫表,這里建議每個表都需要有int(11)類型的id字段,以便作為分表的參考。

(3)擴展字段

在完成了分庫分表和制定路由規(guī)則后,考慮到日后有數(shù)據(jù)庫的DB變更,為減少DB變更對現(xiàn)有數(shù)據(jù)庫表的影響,這里建議每個表都增加text類型的extra_data字段,并且使用json格式進行轉(zhuǎn)換存儲。

(4)可配置

在有了N臺數(shù)據(jù)庫服務(wù)器以及每個表都拆分成M張表后,為減少后臺接口開發(fā)人員的壓力,有必須在后臺接口框架提供可配置 的支持。即:數(shù)據(jù)庫的變更不應(yīng)影響開發(fā)人員現(xiàn)有的開發(fā),也不需要開發(fā)人員作出代碼層面的改動,只需要稍微配置一下即可。關(guān)于這塊,請見下面的框架實現(xiàn)部 分。

(5)SQL語句自動生成

對于相同表的建表語句,可以通過腳本來自動生成,然后直接導(dǎo)入數(shù)據(jù)即可。

2.12.3 PhalApi框架的實現(xiàn)方案

PhalApi框架主要需要實現(xiàn)的是路由這一層的映射,并且通過可配置的方式進行控制,同時還應(yīng)支持生產(chǎn)環(huán)境和測試環(huán)境的異同,如在測試環(huán)境我們明顯不需要1000張數(shù)據(jù)庫的表。為此,需要提供一種 表名 + id 映射到 數(shù)據(jù)庫服務(wù)器 + 具體哪張表 的規(guī)則。 show如上圖所示,表名會統(tǒng)一加上前綴,并且將id按一定的表總數(shù)進行取模,最后再根據(jù)得到的具體表名,通過映射表查找到對應(yīng) 的數(shù)據(jù)庫服務(wù)器進行操作。其中,model層為開發(fā)實現(xiàn),數(shù)據(jù)庫表的映射由接口框架實現(xiàn)支持。

2.12.4 使用示例

(1)配置數(shù)據(jù)庫的路由配置

修改./Config/dbs.php文件,以下是參考的示例配置。其中servers為DB服務(wù)器,包括數(shù)據(jù)庫的賬號信息等,tables為數(shù)據(jù)庫表的映射關(guān)系,其中default下標(biāo)為缺省的數(shù)據(jù)庫路由。

在每個數(shù)據(jù)庫表里面,可以配置多個數(shù)據(jù)庫表,通過開始的下標(biāo)start和結(jié)束的下標(biāo)end來對表進行分布式存放,并且如果沒有start和end的,則視為不需要拆分存放,同時也是當(dāng)找不到合適時的拆分表時所采用的默認(rèn)配置。

return array(
    /**
     * avaiable db servers
     */
    'servers' => array(
        'db_demo' => array(
            'host'      => 'localhost',             //數(shù)據(jù)庫域名
            'name'      => 'test',                  //數(shù)據(jù)庫名字
            'user'      => 'root',                  //數(shù)據(jù)庫用戶名
            'password'  => '123456',                //數(shù)據(jù)庫密碼
            'port'      => '3306',                  //數(shù)據(jù)庫端口
        ),
    ),

    /**
     * custom table map
     */
    'tables' => array(
        '__default__' => array(
            'prefix' => 'tbl_',
            'key' => 'id',
            'map' => array(
                array('db' => 'db_demo'),
            ),
        ),
        'demo' => array(
            'prefix' => 'tbl_',
            'key' => 'id',
            'map' => array(
                array('db' => 'db_demo'),
                array('start' => 0, 'end' => 2, 'db' => 'db_demo'),
            ),
        ),
    ),
);

上面示例配置的意思是:

表名   DB服務(wù)器
  tbl_demo    db_demo
  tbl_demo_0      db_demo
  tbl_demo_1      db_demo
  tbl_demo_2      db_demo

(2)準(zhǔn)備需要創(chuàng)建表的基本SQL語句

這里說的基本SQL語句是指:僅是這個表所特有的字段,排除已固定公共有的自增主鍵id,和擴展字段ext_data。下面是一個示例:

`name` varchar(11) DEFAULT NULL,

(3)生成并導(dǎo)入SQL語句

由于拆分后的數(shù)據(jù)庫表數(shù)量眾多,這里提供了一個快捷的腳本工具來生成所需要創(chuàng)建的數(shù)據(jù)庫表。

$ php ./build_sqls.php
Usage: ./build_sqls.php <table> [engine=InnoDB]

執(zhí)行上面的腳本,輸入數(shù)據(jù)庫表參數(shù)后:

 php ./build_sqls.php demo

將會從配置文件 里面尋找所需要創(chuàng)建的表,并生成類似以下的SQL語句:

/**
 * DB: localhost  db_demo
 */

CREATE TABLE `demo` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(11) DEFAULT NULL,
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/**
 * DB: localhost  db_demo
 */

CREATE TABLE `tpl_demo_0` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(11) DEFAULT NULL,
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tpl_demo_1` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(11) DEFAULT NULL,
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tpl_demo_2` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(11) DEFAULT NULL,
    `ext_data` text COMMENT 'json data here',
     PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(4)使用與代碼開發(fā)

在將上面的SQL語句導(dǎo)入數(shù)據(jù)庫后,即可以像之前那樣操作數(shù)據(jù)庫。下面是一些示例:

DI()->notorm = new PhalApi_DB_NotORM(PhalApi_DI::one()->config->get('dbs'), true);

DI()->notorm->demo->where('id', '1')->fetch();

用到了拆分表的代碼示例,假設(shè)event表被拆分成了3個表,則客戶端在調(diào)用里,需要根據(jù)(id % 3 )來拼接合適的數(shù)據(jù)庫表名,其他使用不變。

DI()->notorm = new PhalApi_DB_NotORM(PhalApi_DI::one()->config->get('dbs'), true);

$row = DI()->notorm->demo_0->where('id', '3')->fetch();
$row = DI()->notorm->demo_1->where('id', '10')->fetch();
$row = DI()->notorm->demo_2->where('id', '2')->fetch();

使用Model基類的情況

更好的寫法,應(yīng)該是繼承于PhalApi_Model_NotORM,并統(tǒng)一實現(xiàn)分表的操作,如:

<?php

class Model_Demo extends PhalApi_Model_NotORM {

    protected function getTableName($id) {
        $tableName = 'demo';
        if ($id !== null) {
            $tableName .= '_' . ($id % 3);
        }
        return $tableName;
    }
}

然后,上面的查詢分別對應(yīng):

$model = new Model_Demo();

$row = $model->get('3', 'id');
$row = $model->get('10', 'id');
$row = $model->get('2', 'id');

更進一步,我們可以通過$this->getORM($id)來獲取分表的實例進行分表的操作,如:

<?php

class Model_Demo extends PhalApi_Model_NotORM {

    //... ...

    public function getNameById($id) {
        $row = $this->getORM($id)->select('name')->fetchRow(); //假設(shè)$id為3,則 $this->getORM($id) 等效于 DI()->notorm->demo_0
    return !empty($row) ? $row['name'] : '';
    }

}

2.12.5 多個數(shù)據(jù)庫的配置方式

當(dāng)需要使用多個數(shù)據(jù)庫時,可以先在servers中可以配置多組數(shù)據(jù)庫的信息,然后在tables為不同的數(shù)據(jù)庫表指定不同的數(shù)據(jù)庫服務(wù)器。

假設(shè)我們有兩臺數(shù)據(jù)庫服務(wù)器,分別叫做db_A、db_B,即:

return array(
    /**
     * DB數(shù)據(jù)庫服務(wù)器集群
     */
    'servers' => array(
        'db_A' => array(                              //db_A
            'host'      => '192.168.0.1',             //數(shù)據(jù)庫域名
            // ... ...
        ),
        'db_B' => array(                              //db_B
            'host'      => '192.168.0.2',             //數(shù)據(jù)庫域名
            // ... ...
        ),
    ),

    //... ...

若db_A服務(wù)器中的數(shù)據(jù)庫有表a_table_user、a_table_friends,而db_B服務(wù)器中的數(shù)據(jù)庫有表b_table_article、b_table_comments,則:

<?php
return array(

    //... ...

    /**
     * 自定義路由表
     */
    'tables' => array(
        //通用路由
        '__default__' => array(
            'prefix' => 'a_',  //以 a_ 為表前綴
            'key' => 'id',
            'map' => array(
                array('db' => 'db_A'),  //默認(rèn),使用db_A數(shù)據(jù)庫
            ),
        ),

        'table_article' => array(                                     //表b_table_article
            'prefix' => 'b_',                                         //表名前綴
            'key' => 'id',                                            //表主鍵名
            'map' => array(                                           //表路由配置
                array('db' => 'db_B'),                                // b_table_article表使用db_B數(shù)據(jù)庫
            ),
        ),

        'table_comments' => array(                                    //表b_table_article
            'prefix' => 'b_',                                         //表名前綴
            'key' => 'id',                                            //表主鍵名
            'map' => array(                                           //表路由配置
                array('db' => 'db_B'),                                // b_table_comments表使用db_B數(shù)據(jù)庫
            ),
        ),
    ),

如果項目存在分表的情況,可結(jié)合上述的分表的說明進行配置。
這里為了讓大家更為明了,假設(shè)db_A服務(wù)器中的數(shù)據(jù)庫有表a_table_user、a_table_friends_0到a_table_friends_9(共10張表), 而db_B服務(wù)器中的數(shù)據(jù)庫有表b_table_article、b_table_comments_0到b_table_comments_19(共20張表),則結(jié)合起來的完整配置為:

<?php
return array(
    /**
     * DB數(shù)據(jù)庫服務(wù)器集群
     */
    'servers' => array(
        'db_A' => array(                              //db_A
            'host'      => '192.168.0.1',             //數(shù)據(jù)庫域名
            // ... ...
        ),
        'db_B' => array(                              //db_B
            'host'      => '192.168.0.2',             //數(shù)據(jù)庫域名
            // ... ...
        ),
    ),

    /**
     * 自定義路由表
     */
    'tables' => array(
        //通用路由
        '__default__' => array(
            'prefix' => 'a_',  //以 a_ 為表前綴
            'key' => 'id',
            'map' => array(
                array('db' => 'db_A'),  //默認(rèn),使用db_A數(shù)據(jù)庫
            ),
        ),

        'table_friends' => array(                                     //分表配置
            'prefix' => 'a_',                                         //表名前綴
            'key' => 'id',                                            //表主鍵名
            'map' => array(                                           //表路由配置
                array('db' => 'db_A'),                                // b_table_comments表使用db_B數(shù)據(jù)庫
                array('start' => 0, 'end' => 9, 'db' => 'db_A'),      //分表配置(共10張表)
            ),
        ),

        'table_article' => array(                                     //表b_table_article
            'prefix' => 'b_',                                         //表名前綴
            'key' => 'id',                                            //表主鍵名
            'map' => array(                                           //表路由配置
                array('db' => 'db_B'),                                // b_table_article表使用db_B數(shù)據(jù)庫
            ),
        ),

        'table_comments' => array(                                    //表b_table_article
            'prefix' => 'b_',                                         //表名前綴
            'key' => 'id',                                            //表主鍵名
            'map' => array(                                           //表路由配置
                array('db' => 'db_B'),                                // b_table_comments表使用db_B數(shù)據(jù)庫
                array('start' => 0, 'end' => 19, 'db' => 'db_B'),     //分表配置(共20張表)
            ),
        ),
    ),
);

2.12.6 與主從數(shù)據(jù)庫的有機結(jié)合

雖然這是專門為海量數(shù)據(jù)設(shè)計的存儲方案,但也是可以結(jié)合主從配置來獲得更龐大強壯的方案,當(dāng)然為之付出的是復(fù)雜性的引入。

簡單地,可以將dbs.php復(fù)制一份dbs_slave.php出來給從庫使用,然后注冊一個從庫的服務(wù):

DI()->slaveNotorm = new PhalApi_DB_NotORM(DI()->config->get('slave_dbs'));

最后,在需要使用從庫來讀取時,使用slaveNotorm 服務(wù)即可。

2.12.7 不足與注意點

這樣的設(shè)計是有明顯的靈活性的,因為在后期如果需要遷移數(shù)據(jù)庫服務(wù)器,我們可以在框架支持的情況下輕松應(yīng)對,但依然需要考慮到一些問題和不足。

(1)DB變更

DB變更,這塊是必不可少的,但一旦數(shù)據(jù)庫表被拆分后,表數(shù)量的驟增導(dǎo)致變更執(zhí)行困難,所以這里暫時使用了一個折中的方案,即提供了一個ext_data 擴展字段用于存放后期可能需要的字段信息,建議采用json格式,因為通用且長度比序列化的短。但各開發(fā)可以根據(jù)自己的需要決定格式。即使如此,擴展字段 明顯做不到一些SQL的查詢及其他操作。

(2)表之間的關(guān)聯(lián)查詢

表之間的關(guān)聯(lián)查詢,這個是分拆后的最大問題。雖然這樣的代價是我們可以得到更龐大的存儲設(shè)計, 而且很多表之間不需要必須的關(guān)聯(lián)的查詢,即使我們需要,我們也可以通過其他手段如緩存和分開查詢來實現(xiàn)。這對開發(fā)人員有一定的約束,但是對于可預(yù)見性的海 量數(shù)量,這又是必須的。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號