<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ##2.12.1 背景 為了應對產品海量用戶的愿景需求,這里將設計一個分布式的數據庫存儲方案,以便能滿足數據量的驟增、云服務的橫向擴展、后臺接口開發的兼容性,以及數據遷移等問題,避免日后因為全部數據都存放在單臺服務器上的限制。 ##2.12.2 主要思想 + 1、分庫分表 + 2、路由規則 + 3、擴展字段 + 4、可配置 + 5、SQL語句自動生成 ###(1)分庫分表 是指將不需要進行必要關聯查詢的表分開存放,如存放事件推送的weili_event_pushto和存放標簽的weili_tag;同時,對于 同一個表,因為存放的數據量是可預見式的暴增,如上述的weili_event_pushto,每時每刻都會產生大量的來自用戶發布的事件,因此為了突破 MySQL單表的限制以及其他問題,需要將此表同時創建N份。 ###(2)路由規則 在上面進行了分庫分表后,開發人員在讀取時,就需要根據相應的規則找到對應 的數據庫和數據庫表,這里建議每個表都需要有int(11)類型的id字段,以便作為分表的參考。 ###(3)擴展字段 在完成了分庫分表和制定路由規則后,考慮到日后有數據庫的DB變更,為減少DB變更對現有數據庫表的影響,這里建議每個表都增加text類型的extra_data字段,并且使用json格式進行轉換存儲。 ###(4)可配置 在有了N臺數據庫服務器以及每個表都拆分成M張表后,為減少后臺接口開發人員的壓力,有必須在后臺接口框架提供可配置 的支持。即:數據庫的變更不應影響開發人員現有的開發,也不需要開發人員作出代碼層面的改動,只需要稍微配置一下即可。關于這塊,請見下面的框架實現部 分。 ###(5)SQL語句自動生成 對于相同表的建表語句,可以通過腳本來自動生成,然后直接導入數據即可。 ##2.12.3 PhalApi框架的實現方案 PhalApi框架主要需要實現的是路由這一層的映射,并且通過可配置的方式進行控制,同時還應支持生產環境和測試環境的異同,如在測試環境我們明顯不需要1000張數據庫的表。為此,需要提供一種 **表名 + id** 映射到 **數據庫服務器 + 具體哪張表** 的規則。 ![show](http://webtools.qiniudn.com/20150411005257_8ea80ab617023e2f377bf1ba8fdff6c5) 如上圖所示,表名會統一加上前綴,并且將id按一定的表總數進行取模,最后再根據得到的具體表名,通過映射表查找到對應 的數據庫服務器進行操作。其中,model層為開發實現,數據庫表的映射由接口框架實現支持。 ##2.12.4 使用示例 ###(1)配置數據庫的路由配置 修改./Config/dbs.php文件,以下是參考的示例配置。其中servers為DB服務器,包括數據庫的賬號信息等,tables為數據庫表的映射關系,其中__default__下標為缺省的數據庫路由。 在每個數據庫表里面,可以配置多個數據庫表,通過開始的下標start和結束的下標end來對表進行分布式存放,并且如果沒有start和end的,則視為不需要拆分存放,同時也是當找不到合適時的拆分表時所采用的默認配置。 ```javascript return array( /** * avaiable db servers */ 'servers' => array( 'db_demo' => array( 'host' => 'localhost', //數據庫域名 'name' => 'test', //數據庫名字 'user' => 'root', //數據庫用戶名 'password' => '123456', //數據庫密碼 'port' => '3306', //數據庫端口 ), ), /** * 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'), ), ), ), ); ``` 上面示例配置的意思是: ```javascript 表名 DB服務器 tbl_demo db_demo tbl_demo_0 db_demo tbl_demo_1 db_demo tbl_demo_2 db_demo ``` ###(2)準備需要創建表的基本SQL語句 這里說的基本SQL語句是指:僅是這個表所特有的字段,排除已固定公共有的自增主鍵id,和擴展字段ext_data。下面是一個示例: ```javascript `name` varchar(11) DEFAULT NULL, ``` ###(3)生成并導入SQL語句 由于拆分后的數據庫表數量眾多,這里提供了一個快捷的腳本工具來生成所需要創建的數據庫表。 ```javascript $ php ./build_sqls.php Usage: ./build_sqls.php <table> [engine=InnoDB] ``` 執行上面的腳本,輸入數據庫表參數后: ```javascript php ./build_sqls.php demo ``` 將會從配置文件 里面尋找所需要創建的表,并生成類似以下的SQL語句: ```javascript /** * 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)使用與代碼開發 在將上面的SQL語句導入數據庫后,即可以像之前那樣操作數據庫。下面是一些示例: ```javascript DI()->notorm = new PhalApi_DB_NotORM(PhalApi_DI::one()->config->get('dbs'), true); DI()->notorm->demo->where('id', '1')->fetch(); ``` 用到了拆分表的代碼示例,假設event表被拆分成了3個表,則客戶端在調用里,需要根據(id % 3 )來拼接合適的數據庫表名,其他使用不變。 ```javascript 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基類的情況 更好的寫法,應該是繼承于PhalApi_Model_NotORM,并統一實現分表的操作,如: ```javascript <?php class Model_Demo extends PhalApi_Model_NotORM { protected function getTableName($id) { $tableName = 'demo'; if ($id !== null) { $tableName .= '_' . ($id % 3); } return $tableName; } } ``` 然后,上面的查詢分別對應: ```javascript $model = new Model_Demo(); $row = $model->get('3', 'id'); $row = $model->get('10', 'id'); $row = $model->get('2', 'id'); ``` 更進一步,我們可以通過$this->getORM($id)來獲取分表的實例進行分表的操作,如: ```javascript <?php class Model_Demo extends PhalApi_Model_NotORM { //... ... public function getNameById($id) { $row = $this->getORM($id)->select('name')->fetchRow(); //假設$id為3,則 $this->getORM($id) 等效于 DI()->notorm->demo_0 return !empty($row) ? $row['name'] : ''; } } ``` ##2.12.5 多個數據庫的配置方式 當需要使用多個數據庫時,可以先在servers中可以配置多組數據庫的信息,然后在tables為不同的數據庫表指定不同的數據庫服務器。 假設我們有兩臺數據庫服務器,分別叫做db_A、db_B,即: ```javascript return array( /** * DB數據庫服務器集群 */ 'servers' => array( 'db_A' => array( //db_A 'host' => '192.168.0.1', //數據庫域名 // ... ... ), 'db_B' => array( //db_B 'host' => '192.168.0.2', //數據庫域名 // ... ... ), ), //... ... ``` 若db_A服務器中的數據庫有表a_table_user、a_table_friends,而db_B服務器中的數據庫有表b_table_article、b_table_comments,則: ```javascript <?php return array( //... ... /** * 自定義路由表 */ 'tables' => array( //通用路由 '__default__' => array( 'prefix' => 'a_', //以 a_ 為表前綴 'key' => 'id', 'map' => array( array('db' => 'db_A'), //默認,使用db_A數據庫 ), ), 'table_article' => array( //表b_table_article 'prefix' => 'b_', //表名前綴 'key' => 'id', //表主鍵名 'map' => array( //表路由配置 array('db' => 'db_B'), // b_table_article表使用db_B數據庫 ), ), 'table_comments' => array( //表b_table_article 'prefix' => 'b_', //表名前綴 'key' => 'id', //表主鍵名 'map' => array( //表路由配置 array('db' => 'db_B'), // b_table_comments表使用db_B數據庫 ), ), ), ``` 如果項目存在分表的情況,可結合上述的分表的說明進行配置。 這里為了讓大家更為明了,假設db_A服務器中的數據庫有表a_table_user、a_table_friends_0到a_table_friends_9(共10張表), 而db_B服務器中的數據庫有表b_table_article、b_table_comments_0到b_table_comments_19(共20張表),則結合起來的完整配置為: ```javascript <?php return array( /** * DB數據庫服務器集群 */ 'servers' => array( 'db_A' => array( //db_A 'host' => '192.168.0.1', //數據庫域名 // ... ... ), 'db_B' => array( //db_B 'host' => '192.168.0.2', //數據庫域名 // ... ... ), ), /** * 自定義路由表 */ 'tables' => array( //通用路由 '__default__' => array( 'prefix' => 'a_', //以 a_ 為表前綴 'key' => 'id', 'map' => array( array('db' => 'db_A'), //默認,使用db_A數據庫 ), ), 'table_friends' => array( //分表配置 'prefix' => 'a_', //表名前綴 'key' => 'id', //表主鍵名 'map' => array( //表路由配置 array('db' => 'db_A'), // b_table_comments表使用db_B數據庫 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數據庫 ), ), 'table_comments' => array( //表b_table_article 'prefix' => 'b_', //表名前綴 'key' => 'id', //表主鍵名 'map' => array( //表路由配置 array('db' => 'db_B'), // b_table_comments表使用db_B數據庫 array('start' => 0, 'end' => 19, 'db' => 'db_B'), //分表配置(共20張表) ), ), ), ); ``` ##2.12.6 與主從數據庫的有機結合 雖然這是專門為海量數據設計的存儲方案,但也是可以結合主從配置來獲得更龐大強壯的方案,當然為之付出的是復雜性的引入。 簡單地,可以將dbs.php復制一份dbs_slave.php出來給從庫使用,然后注冊一個從庫的服務: ```javascript DI()->slaveNotorm = new PhalApi_DB_NotORM(DI()->config->get('slave_dbs')); ``` 最后,在需要使用從庫來讀取時,使用slaveNotorm 服務即可。 ##2.12.7 不足與注意點 這樣的設計是有明顯的靈活性的,因為在后期如果需要遷移數據庫服務器,我們可以在框架支持的情況下輕松應對,但依然需要考慮到一些問題和不足。 ###(1)DB變更 DB變更,這塊是必不可少的,但一旦數據庫表被拆分后,表數量的驟增導致變更執行困難,所以這里暫時使用了一個折中的方案,即提供了一個ext_data 擴展字段用于存放后期可能需要的字段信息,建議采用json格式,因為通用且長度比序列化的短。但各開發可以根據自己的需要決定格式。即使如此,擴展字段 明顯做不到一些SQL的查詢及其他操作。 ###(2)表之間的關聯查詢 表之間的關聯查詢,這個是分拆后的最大問題。雖然這樣的代價是我們可以得到更龐大的存儲設計, 而且很多表之間不需要必須的關聯的查詢,即使我們需要,我們也可以通過其他手段如緩存和分開查詢來實現。這對開發人員有一定的約束,但是對于可預見性的海 量數量,這又是必須的。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看