/** * @brief 获取db对象 * * @param $clusterName 集群名称 * @param $key 负载均衡key * @param $getNew 是否重新连接 * * @return */ public static function getConn($clusterName, $key = NULL, $getNew = false) { $hookBeforeInit = Bd_Conf::getConf('/db/hook_before_init'); if ($hookBeforeInit === false) { //cannot find hookBeforeInit in conf file self::$_error['errno'] = LOAD_CONF_ERROR; self::$_error['error'] = 'Can not read hookBeforeInit, please check db/global.conf'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); return false; } if ($hookBeforeInit != NULL) { //user sets hookBeforeInit if (is_callable($hookBeforeInit)) { $clusterName = call_user_func($hookBeforeInit, $clusterName); } else { //warnning self::$_error['errno'] = SET_HOOK_ERROR; self::$_error['error'] = 'Hook(beforinit):' . $before . 'is not callable'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); } } $conf =& self::$_conf; $hosts =& self::$_hosts; $dbData =& self::$_dbData; $lastDb =& self::$_lastDb; //(1) alreay save a connection (2)user do not need to recreate if (!empty($lastDb[$clusterName]) && !$getNew) { //Bd_Log::trace('Return an existing connection',0,array('db_cluster'=>$clusterName)); return $lastDb[$clusterName]; } if (self::_init($clusterName) === false) { return false; } //create a new db object $db = new Bd_DB(Bd_Db_ConnMgr::$ENABLE_PROFILING); //add hook if ('' !== ($before = $conf['hook_before_query'])) { if (!$db->addHook(Bd_Db::HK_BEFORE_QUERY, $clusterName . '-before', $before)) { self::$_error['errno'] = SET_HOOK_ERROR; self::$_error['error'] = 'Hook(befor query):' . $before . ' is not callable'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); } } if ('' !== ($after = $conf['hook_after_query'])) { if (!$db->addHook(Bd_Db::HK_AFTER_QUERY, $clusterName . '-after', $after)) { self::$_error['errno'] = SET_HOOK_ERROR; self::$_error['error'] = 'Hook(after query):' . $after . ' is not callable'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); } } if ('' !== ($onFail = $conf['hook_on_fail'])) { if (!$db->onFail($onFail)) { self::$_error['errno'] = SET_HOOK_ERROR; self::$_error['error'] = 'Hook(on fail):' . $onFail . ' is not callable'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); } } //try to connect host until there is not host or connecting successfully while (true) { //balancer could not select a valid host to connect if (count($hosts['valid_hosts']) === 0 || ($index = $dbData['host_selector']->select($hosts, $key)) === false) { self::$_error['errno'] = ALL_CONNECT_ERROR; self::$_error['error'] = 'No host could be connected in the cluster'; // Bd_Log::warning( // self::$_error['error'], // self::$_error['errno'], // array('db_cluster'=>$clusterName) // ); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); $hookOnConnFail = $conf['hook_on_connect_fail']; if ($hookOnConnFail != NULL) { if (is_callable($hookOnConnFail)) { call_user_func($hookOnConnFail); } else { //warnning self::$_error['errno'] = SET_HOOK_ERROR; self::$_error['error'] = 'Hook(on connect fail):' . $hookOnConnFail . 'is not callable'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno']); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); } } return false; } //log parameters $logPara = array('db_cluster' => $clusterName, 'db_host' => $hosts['valid_hosts'][$index]['ip'], 'db_port' => $hosts['valid_hosts'][$index]['port'], 'default_db' => $conf['default_db']); for ($i = 1; $i <= $conf['retry_times']; $i++) { $timeout = $conf['connect_timeout_s']; if ($timeout > 0) { $db->setConnectTimeOut($timeout); } $r_timeout = $conf['read_timeout_s']; if ($r_timeout > 0) { $db->setOption(MYSQL_OPT_READ_TIMEOUT, $r_timeout); } $w_timeout = $conf['write_timeout_s']; if ($w_timeout > 0) { $db->setOption(MYSQL_OPT_WRITE_TIMEOUT, $w_timeout); } //Bd_Log::debug("retry times: $i"); Bd_Db_RALLog::warning(RAL_LOG_DEBUG, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", 0, "retry times: {$i}"); $start = microtime(true) * 1000; //connect $ret = $db->connect($hosts['valid_hosts'][$index]['ip'], $conf['username'], $conf['password'], $conf['default_db'], $hosts['valid_hosts'][$index]['port'], $conf['connect_flag']); $end = microtime(true) * 1000; if ($ret) { if (empty($conf['charset']) || $db->charset($conf['charset'])) { $logPara['time_ms'] = $end - $start; //Bd_Log::trace('Connect to Mysql successfully',0,$logPara); $lastDb[$clusterName] = $db; return $lastDb[$clusterName]; } else { //Bd_Log::debug('Set charset failed'); Bd_Db_RALLog::warning(RAL_LOG_DEBUG, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", 0, 'Set charset failed'); } } } //connect failed self::$_error['errno'] = CONNECT_ERROR; self::$_error['error'] = 'Connect to Mysql failed'; //Bd_Log::warning(self::$_error['error'],self::$_error['errno'],$logPara); Bd_Db_RALLog::warning(RAL_LOG_WARN, "ConnMgr", $clusterName, "getConn", "", 0, 0, 0, 0, 0, $clusterName, "", self::$_error['errno'], self::$_error['error']); self::_recordFailedHost($index); } return false; }
public function doSql($strSql, $fetchType = Bd_DB::FETCH_ASSOC, $bolUseResult = false) { // check the env before do sql query; //is in transaction is set outer; if ($this->_bolIsInTransaction) { if ($this->_intUseConnectionNum > 1) { return false; } $this->_intAffectedRows = 0; $this->_arrQueryError = array(); $this->_bolIsSqlTransformError = false; } else { //clear the last query; $this->reset(); } $this->_timer->start(); $arrSql = transform($this->_strDBName, $strSql, $this->_strConfPath, $this->_strConfFilename); $this->_transTime += $this->_timer->stop(); if (NULL === $arrSql || $arrSql['err_no'] !== 0) { $this->_bolIsSqlTransformError = true; Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'transform', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, $this->_transTime, '', $strSql, self::$TRANSFORM_ERRNO, self::$TRANSFORM_ERROR); return false; } //some check before conneciton; if (BD_DB_SplitDB::SPLITDB_SQL_SELECT === $arrSql['sql_type'] && $bolUseResult === true) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'check', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "select should be store result"); return false; } $arrSplitIndex = $this->analyseSqlTransResult($arrSql, BD_DB_SplitDB::MAX_SQL_SPLIT_COUNT); if (!$arrSplitIndex || count($arrSplitIndex) <= 0) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'check', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "splitindex count error"); return false; } //open the connection; if ($this->_bolIsInTransaction) { // in transaction, only 1 split is ok; if (count($arrSplitIndex) !== 1) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'check', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "in transaction split count should be 1"); return false; } //in transaction , the new sql split index should be the same as the last one; if ($this->_intUseConnectionNum === 1) { if ($arrSplitIndex[0] !== $this->_intLastDBNum) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'check', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "in transaction connect should be the same"); return false; } } else { if ($arrSplitIndex[0] === -1) { $strDBInstanceName = $this->_strDBName; } else { $strDBInstanceName = $this->_strDBName . $arrSplitIndex[0]; } if (is_null($this->_arrMysql[$strDBInstanceName])) { $ret = $this->connect($strDBInstanceName); if (!$ret) { $this->_arrQueryError[$strDBInstanceName] = ''; return false; } } else { //echo "connection exist!\n"; } $this->_arrMysql[$strDBInstanceName]->query('START TRANSACTION'); $this->_intLastDBNum = $arrSql['sqls'][0]['splitdb_index']; $this->_intUseConnectionNum = 1; } } else { foreach ($arrSplitIndex as $intSplitIndex) { if ($intSplitIndex === -1) { $strDBInstanceName = $this->_strDBName; } else { $strDBInstanceName = $this->_strDBName . $intSplitIndex; } if (is_null($this->_arrMysql[$strDBInstanceName])) { $ret = $this->connect($strDBInstanceName); if (!$ret) { $this->_arrQueryError[$strDBInstanceName] = ''; break; } } else { //echo "connection exist!\n"; } $this->_intUseConnectionNum++; } if ($this->_intUseConnectionNum !== count($arrSplitIndex)) { $this->_intUseConnectionNum = 0; return false; } if ($this->_intUseConnectionNum === 1) { $this->_intLastDBNum = $arrSplitIndex[0]; } } //build result switch ($fetchType) { case Bd_DB::FETCH_OBJ: $ret = new Bd_Db_SplitDBResult(); break; case Bd_DB::FETCH_ASSOC: $ret = array(); break; case Bd_DB::FETCH_ROW: $ret = array(); break; default: Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'check', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "fetch type error"); return false; } // execute hooks before query foreach ($this->hkBeforeQ as $arrCallback) { $func = $arrCallback[0]; $extArgs = $arrCallback[1]; if (call_user_func_array($func, array($this, &$sql, $extArgs)) === false) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'query', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, 0, 0, 0, '', $strSql, self::$COMMON_ERRNO, "execute hooks before query fail"); return false; } } //do sql query //at least one success $bolAtLeastOneSuccess = false; foreach ($arrSql['sqls'] as $oneSql) { if ($oneSql['splitdb_index'] === -1) { $strDBInstanceName = $this->_strDBName; } else { $strDBInstanceName = $this->_strDBName . $oneSql['splitdb_index']; } $this->_timer->start(); $res = $this->_arrMysql[$strDBInstanceName]->query($oneSql['sql'], $bolUseResult ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT); $time = $this->_timer->stop(); $this->_queryTime += $time; if (is_bool($res) || $res === NULL) { $ok = $res == true; // call fail handler if (!$ok) { //echo "query fail {$oneSql['sql']}\n"; if ($arrSql['sql_type'] === BD_DB_SplitDB::SPLITDB_SQL_SELECT) { if (is_null($this->_arrQueryError[$strDBInstanceName])) { $this->_arrQueryError[$strDBInstanceName] = $oneSql['sql']; } continue; } else { if (($arrSql['sql_type'] === BD_DB_SplitDB::SPLITDB_SQL_UPDATE || $arrSql['sql_type'] === BD_DB_SplitDB::SPLITDB_SQL_DELETE) && $this->_arrMysql[$strDBInstanceName]->errno === BD_DB_SplitDB::MYSQL_ERR_TABLE_NOT_EXIST && $arrSql['table_split_type'] === BD_DB_SplitDB::SPLITDB_SPLIT_TYPE_RANGE) { //echo "delete or update fail!\n"; continue; } else { if (is_null($this->_arrQueryError[$strDBInstanceName])) { $this->_arrQueryError[$strDBInstanceName] = $oneSql['sql']; } Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'query', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, round($time / 1000), 0, 0, $strDBInstanceName, $oneSql['sql'], self::$QUERY_ERRNO, self::$QUERY_ERROR); if ($this->onfail !== NULL) { call_user_func_array($this->onfail, array($this, &$ok)); } return false; } } } else { //commit query success if ($this->_arrMysql[$strDBInstanceName]->affected_rows >= 0) { $this->_intAffectedRows += $this->_arrMysql[$strDBInstanceName]->affected_rows; } } } else { //mysqli_result object returned if ($arrSql['sql_type'] === BD_DB_SplitDB::SPLITDB_SQL_SELECT) { //add to result switch ($fetchType) { case Bd_DB::FETCH_OBJ: $ret->addresult($res); break; case Bd_DB::FETCH_ASSOC: while ($row = $res->fetch_assoc()) { $ret[] = $row; } $res->free(); break; case Bd_DB::FETCH_ROW: while ($row = $res->fetch_row()) { $ret[] = $row; } $res->free(); break; default: return false; } //echo "query success ".$oneSql['sql']."\n"; $bolAtLeastOneSuccess = true; $this->_intAffectedRows = count($ret); } } } if ($arrSql['sql_type'] === BD_DB_SplitDB::SPLITDB_SQL_SELECT && !$bolAtLeastOneSuccess) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "SplitDB", $this->_strDBName, 'query', '', $this->_timer->getTotalTime(Bd_Timer::PRECISION_MS), 0, round($this->_queryTime / 1000), 0, 0, '', $strSql, self::$QUERY_ERRNO, self::$QUERY_ERROR); if ($this->onfail !== NULL) { call_user_func_array($this->onfail, array($this, &$ret)); } return false; } // execute hooks after query foreach ($this->hkAfterQ as $arrCallback) { $func = $arrCallback[0]; $extArgs = $arrCallback[1]; call_user_func_array($func, array($this, &$ret, $extArgs)); } if ($arrSql['sql_type'] !== BD_DB_SplitDB::SPLITDB_SQL_SELECT) { $ret = true; } return $ret; }
/** * @brief prepare查询接口 * * @param $query 查询语句 * @param $getRaw 是否返回原始的mysqli_stmt对象 * * @return */ public function prepare($query, $getRaw = false) { $stmt = $this->mysql->prepare($query); if ($stmt === false) { Bd_Db_RALLog::warning(RAL_LOG_SUM_FAIL, "Bd_DB", $this->dbConf['dbname'], "prepare", "{$this->dbConf['host']}:{$this->dbConf['port']}", 0, 0, 0, 0, 0, $this->dbConf['dbname'], $query, $this->mysql->errno, $this->mysql->error); return false; } if ($getRaw) { return $stmt; } else { return new Bd_Db_DBStmt($stmt); } }