[原创]构建支持Master/Slave读写分离的数据库操作类

本文介绍了一种MySQL主从配置的方法,支持一台主服务器(Master)和多台从服务器(Slave)的架构,实现了读写分离,提高了数据库的稳定性和性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一般对于访问量比较大的网站来说,采用基本的MySQL Master/Slave 结构是很正常,而且一般都是一台Master,多台Slave的情况,但是一般在进行这个访问的时候问题比较多,因为读写操作必须分离,所以今天没事就构造了这个数据库操作类。

数据库操作类基本特点:
1. 支持一台Master,多台Slave的情况,所有SQL能够强制调用Master来处理
2. 能够自动识别是写入还是读取操作,然后自动连接到需要的Master/Slave服务器。 操作过程中能够自动识别,如果没有Slave,则所有操作都是指向Master的,如果当前连接的Slave无法工作则自动连接其他Slave。对于Slave读取数据采用随机挑选Slave服务器的方式来读取数据
3. 在同一个PHP程序中能够重用连接,不会重复去连接数据库,减少因为反复连接带来的资源消耗

缺点:
1. 对于Slave是采取随机选择Slave来进行读取数据,所以不是真正意义上的负载均衡
2. 每次初始化都是连接好所有的数据库,比较浪费连接资源和时间。 在多个PHP程序中无法保持数据库连接,每次启动都需要连接到远程数据库,浪费了很多连接处理的时间,但是这个跟PHP是脚本程序有关。
3. 无法支持多个Master的情况,同时可定制性比较差,很多东西都是固定的,必须按照固有模式来执行查询

相关:
代码中注释不是很全,部分注释而且不一定正确,所以如果自己使用,最好进行二次修改。
本数据库类参考了我之前写的《简单快速有趣的MySQL数据库操作类:SimpleDB》,可以对照着看。
(类库中的接口我大部分都经过了测试,都正常使用,如果发现不正常或者有更好的想法,请留言)


文件:db_common.class.php

<? php
// ----------------------------------------------------
//Master/Slave数据库读写分开操作类
//
//作者:heiyeluren<http://blog.youkuaiyun.com/heiyeshuwu>
//时间:2007-7-30
//描述:支持所有写操作在一台Master执行,所有读操作在
//Slave执行,并且能够支持多台Slave主机
//----------------------------------------------------



/* *
*常量定义
*/
define ( " _DB_INSERT " , 1 );
define ( " _DB_UPDATE " , 2 );


/* *
*DBCommonclass
*
*描述:能够分别处理一台Master写操作,多台Slave读操作
*/
class DBCommon
{
/* *
*数据库配置信息
*/
var $wdbConf = array ();
var $rdbConf = array ();
/* *
*Master数据库连接
*/
var $wdbConn = null ;
/* *
*Slave数据库连接
*/
var $rdbConn = array ();
/* *
*数据库结果
*/
var $dbResult ;
/* *
*数据库查询结果集
*/
var $dbRecord ;

/* *
*SQL语句
*/
var $dbSql ;
/* *
*数据库编码
*/
var $dbCharset = " UTF8 " ;
/* *
*数据库版本
*/
var $dbVersion = " 4.1 " ;


/* *
*初始化的时候是否要连接到数据库
*/
var $isInitConn = false ;
/* *
*是否要设置字符集
*/
var $isCharset = true ;
/* *
*数据库结果集提取方式
*/
var $fetchMode = MYSQL_ASSOC;
/* *
*执行中发生错误是否记录日志
*/
var $isLog = true ;
/* *
*是否查询出错的时候终止脚本执行
*/
var $isExit = false ;



// ------------------------
//
//基础的DB操作
//
//------------------------


/* *
*构造函数
*
*传递配置信息,配置信息数组结构:
*$masterConf=array(
*"host"=>Master数据库主机地址
*"user"=>登录用户名
*"pwd"=>登录密码
*"db"=>默认连接的数据库
*);
*$slaveConf=array(
*"host"=>Slave1数据库主机地址|Slave2数据库主机地址|...
*"user"=>登录用户名
*"pwd"=>登录密码
*"db"=>默认连接的数据库
*);
*/
function DBCommon( $masterConf , $slaveConf = array ()){
// 构造数据库配置信息
if ( is_array ( $masterConf ) && ! empty ( $masterConf )){
$this -> wdbConf = $masterConf ;
}
if ( ! is_array ( $slaveConf ) || empty ( $slaveConf )){
$this -> rdbConf = $masterConf ;
}
else {
$this -> rdbConf = $slaveConf ;
}
// 初始化连接(一般不推荐)
if ( $this -> isInitConn){
$this -> getDbWriteConn();
$this -> getDbReadConn();
}
}

/* *
*获取Master的写数据连接
*/
function getDbWriteConn(){
// 判断是否已经连接
if ( $this -> wdbConn && is_resource ( $this -> wdbConn)){
return $this -> wdbConn;
}
// 没有连接则自行处理
$db = $this -> connect( $this -> wdbConf[ ' host ' ] , $this -> wdbConf[ ' user ' ] , $this -> wdbConf[ ' pwd ' ] , $this -> wdbConf[ ' db ' ]);
if ( ! $db || ! is_resource ( $db )){
return false ;
}
$this -> wdbConn = $db ;
return $this -> wdbConn;
}

/* *
*获取Slave的读数据连接
*/
function getDbReadConn(){
// 如果有可用的Slave连接,随机挑选一台Slave
if ( is_array ( $this -> rdbConn) && ! empty ( $this -> rdbConn)){
$key = array_rand ( $this -> rdbConn);
if ( isset ( $this -> rdbConn[ $key ]) && is_resource ( $this -> rdbConn[ $key ])){
return $this -> rdbConn[ $key ];
}
}
// 连接到所有Slave数据库,如果没有可用的Slave机则调用Master
$arrHost = explode ( " | " , $this -> rdbConf[ ' host ' ]);
if ( ! is_array ( $arrHost ) || empty ( $arrHost )){
return $this -> getDbWriteConn();
}
$this -> rdbConn = array ();
foreach ( $arrHost as $tmpHost ){
$db = $this -> connect( $tmpHost , $this -> rdbConf[ ' user ' ] , $this -> rdbConf[ ' pwd ' ] , $this -> rdbConf[ ' db ' ]);
if ( $db && is_resource ( $db )){
$this -> rdbConn[] = $db ;
}
}
// 如果没有一台可用的Slave则调用Master
if ( ! is_array ( $this -> rdbConn) || empty ( $this -> rdbConn)){
$this -> errorLog( " Notavailabilityslavedbconnection,callmasterdbconnection " );
return $this -> getDbWriteConn();
}
// 随机在已连接的Slave机中选择一台
$key = array_rand ( $this -> rdbConn);
if ( isset ( $this -> rdbConn[ $key ]) && is_resource ( $this -> rdbConn[ $key ])){
return $this -> rdbConn[ $key ];
}
// 如果选择的slave机器是无效的,并且可用的slave机器大于一台则循环遍历所有能用的slave机器
if ( count ( $this -> rdbConn) > 1 ){
foreach ( $this -> rdbConn as $conn ){
if ( is_resource ( $conn )){
return $conn ;
}
}
}
// 如果没有可用的Slave连接,则继续使用Master连接
return $this -> getDbWriteConn();
}

/* *
*连接到MySQL数据库公共方法
*/
function connect( $dbHost , $dbUser , $dbPasswd , $dbDatabase ){
// 连接数据库主机
$db = mysql_connect ( $dbHost , $dbUser , $dbPasswd );
if ( ! $db ){
$this -> errorLog( " Mysqlconnect " . $dbHost . " failed " );
return false ;
}
// 选定数据库
if ( ! mysql_select_db ( $dbDatabase , $db )){
$this -> errorLog( " selectdb$dbDatabasefailed " , $db );
return false ;
}
// 设置字符集
if ( $this -> isCharset){
if ( $this -> dbVersion == '' ){
$res = mysql_query ( " SELECTVERSION() " );
$this -> dbVersion = mysql_result ( $res , 0 );
}

if ( $this -> dbCharset != '' && preg_match ( " /^(5.|4.1)/ " , $this -> dbVersion)){
if ( mysql_query ( " SETNAMES' " . $this -> dbCharset . " ' " , $db ) === false ){
$this -> errorLog( " Setdb_host'$dbHost'charset= " . $this -> dbCharset . " failed. " , $db );
return false ;
}
}
}
return $db ;
}

/* *
*关闭数据库连接
*/
function disconnect( $dbConn = null , $closeAll = false ){
// 关闭指定数据库连接
if ( $dbConn && is_resource ( $dbConn )){
mysql_close ( $dbConn );
$dbConn = null ;
}
// 关闭所有数据库连接
if ( $closeAll ){
if ( $this -> rdbConn && is_resource ( $this->rdbConn )){
mysql_close ( $this->rdbConn );
$this -> rdbConn = null ;
}
if ( is_array ( $this -> rdbConn) && ! empty ( $this -> rdbConn)){
foreach ( $this -> rdbConn as $conn ){
if ( $conn && is_resource ( $conn )){
mysql_close ( $conn );
}
}
$this -> rdbConn = array ();
}
}
return true ;
}

/* *
*选择数据库
*/
function selectDb( $dbName , $dbConn = null ){
// 重新选择一个连接的数据库
if ( $dbConn && is_resource ( $dbConn )){
if ( ! mysql_select_db ( $dbName , $dbConn )){
$this -> errorLog( " Selectdatabase:$dbNamefailed. " , $dbConn );
return false ;
}
return true ;
}
// 重新选择所有连接的数据库
if ( $this -> wdbConn && is_resource ( $this -> wdbConn)){
if ( ! mysql_select_db ( $dbName , $this -> wdbConn)){
$this -> errorLog( " Selectdatabase:$dbNamefailed. " , $this -> wdbConn);
return false ;
}
}
if ( is_array ( $this -> rdbConn && ! empty ( $this -> rdbConn))){
foreach ( $this -> rdbConn as $conn ){
if ( $conn && is_resource ( $conn )){
if ( ! mysql_select_db ( $dbName , $conn )){
$this -> errorLog( " Selectdatabase:$dbNamefailed. " , $conn );
return false ;
}
}
}
}
return true ;
}

/* *
*执行SQL语句(底层操作)
*/
function query( $sql , $isMaster = false ){
if ( trim ( $sql ) == "" ){
$this -> errorLog( " Sqlqueryisempty. " );
return false ;
}
// 获取执行SQL的数据库连接
if ( ! $isMaster ){
$optType = trim ( strtolower ( array_shift ( explode ( " " , ltrim ( $sql )))));
}
if ( $isMaster || $optType != " select " ){
$dbConn = $this -> getDbWriteConn();
}
else {
$dbConn = $this -> getDbReadConn();
}
if ( ! $dbConn || ! is_resource ( $dbConn )){
$this -> errorLog( " Notavailabilitydbconnection.QuerySQL: " . $sql );
if ( $this -> isExit){
exit ;
}
return false ;
}
// 执行查询
$this -> dbSql = $sql ;
$this -> dbResult = null ;
$this -> dbResult = @ mysql_query ( $sql , $dbConn );
if ( $this -> dbResult === false ){
$this -> errorLog( " Querysqlfailed.SQL: " . $sql , $dbConn );
if ( $this -> isExit){
exit ;
}
return false ;
}
return true ;
}

/* *
*错误日志
*/
function errorLog( $msg = '' , $conn = null ){
if ( ! $this -> isLog){
return ;
}
if ( $msg == '' && ! $conn ){
return false ;
}
$log = " MySQLError:$msg " ;
if ( $conn && is_resource ( $conn )){
$log .= " mysql_msg: " . mysql_error ( $conn );
}
$log .= " [ " . date ( " Y-m-dH:i:s " ) . " ] " ;
error_log ( $log );
return true ;
}




// --------------------------
//
//数据获取接口
//
//--------------------------

/* *
*获取SQL执行的全部结果集(二维数组)
*
*@paramstring$sql需要执行查询的SQL语句
*@return成功返回查询结果的二维数组,失败返回false
*/
function getAll( $sql , $isMaster = false ){
if ( ! $this -> query( $sql , $isMaster )){
return false ;
}
$this -> dbRecord = array ();
while ( $row = @ mysql_fetch_array ( $this -> dbResult , $this -> fetchMode)){
$this ->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值