最近开始在看discuzx3.1的代码,看到数据库层的实现,discuzx的数据库层能够支撑数据库分库,分布式部署,主要水平分表,也可以很方便的支持其他数据库。性能上,可以做读写分离,支持数据缓存。可以说,是一个很完善的数据库层的解决方案了。

数据库层分为三层,业务逻辑层封装,抽象层,和驱动层。如图:

其中,数据抽象层封装定义数据库操作,负责解析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';

可根据数据表进行分布式部署

@example 将 common_member 部署到第二服务器, common_session 部署在第三服务器, 则设置为
$_config['db']['map']['common_member'] = 2;
$_config['db']['map']['common_session'] = 3;

先看在抽象层(DB 类)初始化。在discuz_application的init时,调用_init_db()方法

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的init

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

《转》读discuzx3.1 数据库层笔记的更多相关文章

  1. MYSQL数据库学习笔记1

      MYSQL数据库学习笔记1 数据库概念 关系数据库 常见数据库软件 SQL SQL的概念 SQL语言分类 数据库操作 创建数据库 查看数据库的定义 删除数据库 修改数据库 创建表 数据类型 约束 ...

  2. GZFramwork数据库层《四》单据主从表增删改查

    同GZFramwork数据库层<三>普通主从表增删改查 不同之处在于:实例 修改为: 直接上效果: 本系列项目源码下载地址:https://github.com/GarsonZhang/G ...

  3. GZFramwork数据库层《三》普通主从表增删改查

    运行结果: 使用代码生成器(GZCodeGenerate)生成tb_Cusomer和tb_CusomerDetail的Model 生成器源代码下载地址: https://github.com/Gars ...

  4. GZFramwork数据库层《二》单据表增删改查(自动生成单据号码)

    运行效果: 使用代码生成器(GZCodeGenerate)生成tb_EmpLeave的Model 生成器源代码下载地址: https://github.com/GarsonZhang/GZCodeGe ...

  5. GZFramwork数据库层《前言》Demo简介

    本系列旨在熟悉GZFramwork数据库层操作,对数据库表进行增删改查,单据编号生成等: 详细见图: 普通单表操作: 数据库建模: 创建表脚本: from sys.sysreferences r jo ...

  6. GZFramwork数据库层《前言》DLL项目引用

    新建项目: 1. 项目引入GZFramwork.dll NuGet地址:Install-Package GZFramwork 每个项目都引用 2.BLL层 设置数据库连接维护类:继承于:GZFramw ...

  7. MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  9. MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

随机推荐

  1. OpenGL鼠标旋转图像

    (鼠标旋转功能) #include <iostream> using namespace std; #include<gl/glut.h> GLfloat transx,tra ...

  2. 关于sql 外键的讨论。

    外键是否采用看业务应用场景,以及开发成本的,大致列下什么时候适合,什么时候不适合使用: 1. 互联网行业应用不推荐使用外键: 用户量大,并发度高,为此数据库服务器很容易成为性能瓶颈,尤其受IO能力限制 ...

  3. Javascript 拖拽的一些简单的应用——逐行分析代码,让你轻松了解拖拽的原理

    今天我们来看看如何让拖拽的物体不能拖出某个div之外和拖拽的吸附功能 上次讲到我们的拖拽是不可拖出可视区范围的,在这基础上我们加个父级的div,不让他拖出父级.原理和之前的一样,简单吧. <di ...

  4. AlarmManager与PendingIntent

    1.AlarmManager的作用与PendingIntent的关系 顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent.简单的说就是 ...

  5. Datagridview控件实现分页功能

    可以进行sql语句进行设置:      1.先新建一个窗体,一个DataGridView控件.两个label控件.两个Button控件   2.代码如下: using System; using Sy ...

  6. Spring中常用的hql查询方法(getHibernateTemplate())

    一.find(String queryString); 示例:getHibernateTemplate().find("from bean.User"); 返回所有User对象 二 ...

  7. 【转】浅析terminal创建时ptmx和pts关系

      我们打开一个terminal,那么将会在devpts文件系统/dev/pts下创建一个对应的pts字符文件,该pts字符文件节点直接由/dev/ptmx节点的驱动函数ptmx_open()调用de ...

  8. Ajax学习教程在线阅读

      1.什么是AJAX ?(1) 2.什么是AJAX ?(2) 3.什么是AJAX ?(3) 4.什么是AJAX ?(4) 5.Ajax基础教程(1)-Ajax简介 1.1 Web应用简史 6.Aja ...

  9. [转] Mysql 数据库引擎

    你能用的数据库引擎取决于mysql在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型INNO ...

  10. 全国计算机等级考试二级教程-C语言程序设计_第9章_数组

    四维数组,可用于航天卫星,三维+时间 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> ...