《转》读discuzx3.1 数据库层笔记
最近开始在看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 数据库层笔记的更多相关文章
- MYSQL数据库学习笔记1
MYSQL数据库学习笔记1 数据库概念 关系数据库 常见数据库软件 SQL SQL的概念 SQL语言分类 数据库操作 创建数据库 查看数据库的定义 删除数据库 修改数据库 创建表 数据类型 约束 ...
- GZFramwork数据库层《四》单据主从表增删改查
同GZFramwork数据库层<三>普通主从表增删改查 不同之处在于:实例 修改为: 直接上效果: 本系列项目源码下载地址:https://github.com/GarsonZhang/G ...
- GZFramwork数据库层《三》普通主从表增删改查
运行结果: 使用代码生成器(GZCodeGenerate)生成tb_Cusomer和tb_CusomerDetail的Model 生成器源代码下载地址: https://github.com/Gars ...
- GZFramwork数据库层《二》单据表增删改查(自动生成单据号码)
运行效果: 使用代码生成器(GZCodeGenerate)生成tb_EmpLeave的Model 生成器源代码下载地址: https://github.com/GarsonZhang/GZCodeGe ...
- GZFramwork数据库层《前言》Demo简介
本系列旨在熟悉GZFramwork数据库层操作,对数据库表进行增删改查,单据编号生成等: 详细见图: 普通单表操作: 数据库建模: 创建表脚本: from sys.sysreferences r jo ...
- GZFramwork数据库层《前言》DLL项目引用
新建项目: 1. 项目引入GZFramwork.dll NuGet地址:Install-Package GZFramwork 每个项目都引用 2.BLL层 设置数据库连接维护类:继承于:GZFramw ...
- MySQL数据库学习笔记(十二)----开源工具DbUtils的使用(数据库的增删改查)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- MySQL数据库学习笔记(九)----JDBC的ResultSet接口(查询操作)、PreparedStatement接口重构增删改查(含SQL注入的解释)
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
随机推荐
- Import MySQL Dumpfile, SQL Datafile Into My Database
How can I import a MySQL dumpfile into my database? I'm using CentOS Linux 5 server. My old hosting ...
- sql server sys.object表字段说明
列名 数据类型 说明 name sysname 对象名. object_id int 对象标识号. 在数据库中是唯一的. principal_id int 如果不是架构所有者,则为单个所有者的 ID. ...
- 【MVC】过滤器
APS.NET MVC中(以下简称“MVC”)的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理.这时候就用到了过滤器. MVC支持的过滤器 ...
- 关于lvs+keepalived只加入一台realserver问题
今天做lvs+keepalived+mysql项目实施方案,在配置lvs+keepalived时都是ok的,但是就只加入第一台realserver.本人也感到很奇怪,lvs+keepalived本人也 ...
- 下拉条的连动-ajax方式
客户端触发: <select id="category1" onchange="changecategory()"> <option v ...
- (转)解决JSP路径问题的方法(jsp文件开头path, basePath作用)
在JSP中的如果使用 "相对路径" 则有可能会出现问题. 因为 网页中的 "相对路径" , 他是相对于 "URL请求的地址" 去寻找资源. ...
- 兼容IE6,IE7和firefox可以使用的一些css hack:
.一些问题是浏览器自身的问题,遇到问题发生无法避免的情况下,那就要考虑使用一些css hack了,以下是针对IE6,IE7和firefox可以使用的一些css hack:(1) a: 针对区别IE6 ...
- Javascript 运动中Offset的bug——逐行分析代码,让你轻松了解运动的原理
我们先来看看这个bug 是怎么产生的. <style type="text/css"> #div1 { width: 200px; height: 200px; bac ...
- Python之路第五天,基础(6)-模块
模块 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个 ...
- docker数据管理2
3. 定义数据卷容器: 只是为了共享数据 docker run -itd -v /data/ --name centeos_testv centos bash /data/ 就是虚拟机内的目录,和宿主 ...