《转》读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 ...
随机推荐
- Android学习自定义Dialog
Dialog是Android提供的各种对话框的基类,和上篇的DialogFragment类似.为什么还要介绍Dialog呢,因为DialogFragment只能运行在Android3.0以上的系统中. ...
- dom操作例子
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- JS判断终端浏览器类型
根据终端浏览器类型不懂加载不同的JS或CSS文件 <script> var browser = { versions: function () { var u = navigator.us ...
- JavaScript中的this引用
在JavaScript的学习当中,this关键字的出现频率可不低,所以想想有必要对this关键字做一个总结.在总结过程中,参考的资料来源于书本及网上. 一.定义 1.this是函数内部的一个特殊对象( ...
- [string]字符串中几个比较难的算法和容易搞混的题目
一.两个难点算法 1.Manacher算法,线性时间求最长回文子串 2.KMP算法,字符串匹配问题,c语言中的strStr 二.几个题目 1.最长回文子串 方法:暴力,动态规划,中心扩展,manach ...
- [string]Valid Parentheses
Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...
- js常用DOM操作
在博客园看到了苏夏写的常用DOM整理文章,地址:http://www.cnblogs.com/cabbagen/p/4579412.html,然后抽时间都试了一下这些常用的DOM操作.在这里记录一下. ...
- Android 软键盘小知识点
chatText = (EditText) findViewById(R.id.chatText); chatText.setOnKeyListener(new OnKeyListener() { p ...
- NoSQL的价值到底在哪里?
关系型数据库的价值 持久化数据:通过数据库来保存数据 处理并发:通过事务方式处理并发 集成:共享数据库集成,多个应用程序可以同时访问同一份数据 标准模型:前几种功能已经成标准,开发人员学习成本低,虽然 ...
- 【学习笔记01】:hover为DIV添加鼠标悬停时改变颜色的效果
:hover所有主流浏览器都支持(IE6.0以下支持不好,以后再学习用Javascript来实现悬停效果) 这是一个绿色底白色Icon的搜索按钮