讀discuzx3.1 數據庫層筆記(轉帖)

最近開始在看discuzx3.1的代碼,看到數據庫層的實現,discuzx的數據庫層可以支撐數據庫分庫,分佈式部署,主要水平分表,也能夠很方便的支持其餘數據庫。性能上,能夠作讀寫分離,支持數據緩存。能夠說,是一個很完善的數據庫層的解決方案了。php

數據庫層分爲三層,業務邏輯層封裝,抽象層,和驅動層。如圖:mysql

其中,數據抽象層封裝定義數據庫操做,負責解析sql語句,鏈接底層驅動執行sql,並數據安全過濾。sql

數據庫抽象層由discuzx_database類實現,該類全部的成員變量和方法都是靜態的,能夠直接調用。本類中,init 方法用來初始化底層驅動,設置數據庫配置,而且創建默認的數據庫鏈接。table方法,設置當前操做的數據表,這個方法很關鍵,數據庫的分佈式部署,讀寫分離,都是由表名來肯定的。這個在驅動層具體實現。其餘如insert,delete,update,fetch_all等等方法是封裝了數據表的基本操做,checkquery方法負責數據的安全檢查,防注入。query方法負責解析sql語句,調用驅動層,執行sql語句,quote,field_quote,field,format等方法負責sql語句的格式化。數據庫

數據庫驅動層選擇數據庫,直接鏈接數據庫,跟數據庫交互。目前discuzx默認提供兩種底層驅動,mysql和mysqli。以mysql爲例,整個驅動層其實由db_dirver_mysql和db_driver_mysql_slave兩個類外加數據庫配置文件實現,db_driver_mysql_salve繼承於db_driver_mysql。緩存

配置文件中(./config/config_global.php )可配置數據表的讀寫分離,包括多臺從服務器,分佈式部署。安全

 $_config['db']['1']['slave']['1']['dbhost'] = 'localhost';
 $_config['db']['1']['slave']['1']['dbuser'] = 'root';
 $_config['db']['1']['slave']['1']['dbpw'] = 'root';
 $_config['db']['1']['slave']['1']['dbcharset'] = 'gbk';

可配置 禁用從數據庫的數據表, 表名字之間使用逗號分割服務器

 * @example common_session, common_member 這兩個表僅從主服務器讀寫, 不使用從服務器
 * $_config['db']['common']['slave_except_table'] = 'common_session, common_member';

 

可根據數據表進行分佈式部署session

 

@example 將 common_member 部署到第二服務器, common_session 部署在第三服務器, 則設置爲
 $_config['db']['map']['common_member'] = 2;
 $_config['db']['map']['common_session'] = 3;

 

先看在抽象層(DB 類)初始化。在discuz_application的init時,調用_init_db()方法app

複製代碼
private function _init_db() {
if($this->init_db) {
$driver = function_exists('mysql_connect') ? 'db_driver_mysql' : 'db_driver_mysqli';
if(getglobal('config/db/slave')) {
$driver = function_exists('mysql_connect') ? 'db_driver_mysql_slave' : 'db_driver_mysqli_slave';
}
DB::init($driver, $this->config['db']);
}
}
複製代碼

抽象層DB的initcurl

 

複製代碼
public static function init($driver, $config) {
self::$driver = $driver;
self::$db = new $driver;
self::$db->set_config($config);
self::$db->connect();
}
複製代碼

 

根據配置文件中的讀寫分離來肯定底層dirver,若是支持從庫,則初始化db_dirver_mysql_salve。

db_driver_mysql中的table_name表是實現分庫的鑰匙,根據表名來肯定當前須要鏈接的數據庫服務器編號,並鏈接數據庫,存入鏈接池中,同時設置爲當前鏈接。

複製代碼
function table_name($tablename) {
if(!empty($this->map) && !empty($this->map[$tablename])) {
$id = $this->map[$tablename];
if(!$this->link[$id]) {
$this->connect($id);
}
$this->curlink = $this->link[$id];
} else {
$this->curlink = $this->link[1];
}
return $this->tablepre.$tablename;
}
複製代碼

 

這裏提一下,$this->link能夠實現數據庫只須要鏈接一次,不須要重複鏈接。

在寫入數據時,調用主庫,而讀取數據時調用從庫,見db_driver_mysql_salve, 該類覆蓋了table_name表,判斷該表是否須要讀寫分離,而且肯定該表的數據庫服務器組編號

複製代碼
public function table_name($tablename) {
$this->tablename = $tablename;
if(!$this->slaveexcept && $this->excepttables) {
$this->slaveexcept = in_array($tablename, $this->excepttables, true);
}
$this->serverid = isset($this->map[$this->tablename]) ? $this->map[$this->tablename] : 1;
return $this->tablepre.$tablename;
}
複製代碼

 

重寫了query方法,判斷若是當前須要讀從庫則選擇並鏈接從庫。

複製代碼
public function query($sql, $silent = false, $unbuffered = false) {
if(!(!$this->slaveexcept && strtoupper(substr($sql, 0 , 6)) === 'SELECT' && $this->_slave_connect())) {
$this->_master_connect();
}
$this->tablename = '';
$this->slaveexcept = false;
return parent::query($sql, $silent, $unbuffered);
}
複製代碼

 

db_dirver_mysql中其餘方法的用途。set_config方法,用於加載配置信息。content方法,根據服務器號鏈接相應數據庫,存入鏈接池,並設置爲當前鏈接。query使用當前鏈接執行sql語句。fetch_all,fetch_first,result_first定義經常使用操做。

db_dirver_mysql_slave中的其餘方法的用途。set_config執行父類方法,設置不須要讀寫分離的數據表。_choose_slave方法,在多臺從服務器的狀況下,根據權重選擇一臺從庫。

至此,數據庫的抽象層和底層基本清楚了。

業務邏輯層實際上是對抽象層的封裝。邏輯層實際上是全部的業務邏輯中涉及數據庫操做的部分。都能直接經過抽象層的DB::query或者DB::insert等方法來讀寫數據。不過一般數據表的操做都再次進行了封裝。數據表業務類在source/class/table目錄中。數據表操做類都繼承於discuz_table類,該在在source/calss/discuz目錄下。數據庫的數據緩存也在本層中實現。

__coustruct方法,設置表名和主鍵,設置是否緩存和緩存時間。該類主要封裝了一些經常使用數據庫操做,count,insert,delete,fetch,fetch_all等。數據緩存相關的方法:獲取指定鍵值的數據緩存fetch_cache,存儲單條記錄緩存store_cache,清除指定記錄緩存clear_cache,update_cache 更新指定單條記錄緩存,update_batch_cache 批量更新指定記錄緩存,reset_cache 重置指定數據的緩存,increase_cache 在原有緩存上追加更新緩存。緩存操做的方法由實際數據庫操做的方法調用,如fetch方法:

複製代碼
public function fetch($id, $force_from_db = false){
$data = array();
if(!empty($id)) {
if($force_from_db || ($data = $this->fetch_cache($id)) === false) {
$data = DB::fetch_first('SELECT * FROM '.DB::table($this->_table).' WHERE '.DB::field($this->_pk, $id));
if(!empty($data)) $this->store_cache($id, $data);
}
}
return $data;
}

public function store_cache($id, $data, $cache_ttl = null, $pre_cache_key = null) {
$ret = false;
if($this->_allowmem) {
if($pre_cache_key === null) $pre_cache_key = $this->_pre_cache_key;
if($cache_ttl === null) $cache_ttl = $this->_cache_ttl;
$ret = memory('set', $id, $data, $cache_ttl, $pre_cache_key);
}
return $ret;
}
複製代碼

 

判斷是否緩存和緩存中是否存,若是是則直接返回緩存,而不讀數據庫。從數據庫中讀出數據後,存入緩存中。而是否從緩存中讀取時由外部參數來定義的,默認是可緩存。本方法中的緩存讀寫用經過memory來實現的,memory方法定義在function_core文件中,至於緩存的具體實現,須要在另一篇文章中說明了。

 

至此,discuzx3.1的數據庫層都有了,分析的有點凌亂。

原地址:

http://blog.csdn.net/jetxt/article/details/17242581

相關文章
相關標籤/搜索