最近在做一個基於有讚的多級分銷管理系統,所有成員的店面均在有贊商城,使用有贊API獲得他們的業績,但是有贊提供的分銷只有一級,故製作該系統。考慮到減輕工作量,理清層次關係,採用了OOP設計方法,將數據庫,表封裝爲基類,分銷成員,店面等繼承表。
但是在列出銷售量報表和分銷商的時候出現了嚴重性能問題,由於分銷商的業績獎勵是與其下級分銷商掛鉤的,故封裝數據庫的時候,進行了DFS遍歷來獲得所有分銷商的關係樹,然而,在列出銷售報表的時候卻並不需要這樣的關係,DFS在php語言上的時間花銷極大,導致一個頁面打開需要10s以上的時間,故對其進行第一步優化,在數據庫的封裝函數中設置DFS開關:
這個是Database類的部分實現,其中部分敏感數據已隱藏。
<?php
namespace System;
require_once('KdtApiClient.php');
class Database
{
const db_adr="";
const db_usr="";
const db_pwd="";
const db_db="";
public $member=array();
public function init($dfs=true,$store=true)
{
$mysqli=new \mysqli(self::db_adr,self::db_usr,self::db_pwd,self::db_db);
if($mysqli->connect_error) throw new Exception('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
$result=$mysqli->query("select `id` from member_new");
$row=$result->fetch_array();
$i=0;
while($row)
{
$this->member[$i]=new Member($row[0],$dfs,$store);
$row=$result->fetch_array();
$i++;
}
$mysqli->close();
}
static public function doQuery($string)
{
$mysqli=new \mysqli(self::db_adr,self::db_usr,self::db_pwd,self::db_db);
if($mysqli->connect_error) throw new Exception('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
return $mysqli->query($string);
}
static public function querySell($start,$end)
{}
}
member類是繼承自Table:
class Table
{
public $data=array();
protected $table_name;
public function __construct($id)
{
$this->data['id']=$id;
$mysqli=new \mysqli(Database::db_adr,Database::db_usr,Database::db_pwd,Database::db_db);
if($mysqli->connect_error) throw new Exception('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
$result=$mysqli->query("select * from ".$this->table_name." where `id`=$id");
$row=$result->fetch_assoc();
$this->data=$row;
$mysqli->close();
}
public function updateAll() //Do NOT CHANGE ID!!!
{
reset($this->data);
while($data=each($this->data))
{
$querystring="update ".$this->table_name." set `".$data[0]."`='$data[1]' where `id`='".$this->data['id']."'";
Database::doQuery($querystring);
}
reset($this->data);
}
public function update($key)
{
$querystring="update ".$this->table_name." set `$key`='".$this->data[$key]."' where `id`='".$this->data['id']."'";
Database::doQuery($querystring);
}
public function set($key,$data) //recommended
{
$this->data[$key]=$data;
$this->update($key);
}
public function get($key) //recommended
{
return $this->data[$key];
}
}
封裝了對錶的基本操作,簡化以後的代碼編寫.下面member類的構造函數可以完成DFS功能,加設開關參數後,可以在某些場合不試用DFS而大大提升效率:
class Member extends Table
{
protected $table_name="member_new";
public $infer=array();
public $store=array();
public function __construct($id,$dfs=true,$store=true)
{
parent::__construct($id);
$mysqli=new \mysqli(Database::db_adr,Database::db_usr,Database::db_pwd,Database::db_db);
if($mysqli->connect_error) throw new Exception('Connect Error (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error);
if($dfs){
$result=$mysqli->query("select `id` from ".$this->table_name." where `super`=".$this->data['id']);
if($result){
$row=$result->fetch_array();
$i=0;
while($row)
{
$this->infer[$i]=new Member($row[0]);
$row=$result->fetch_array();
$i++;
}
}
}
if($store)
{
$result=$mysqli->query("select `id` from store_new where `member`=".$this->data['id']);
if($result){
$row=$result->fetch_array();
$i=0;
while($row)
{
$this->store[$i]=new Store($row[0]);
$row=$result->fetch_array();
$i++;
}
}
}
$mysqli->close();
}
(部分敏感函數已隱藏)
另外,在對前端的AJAX響應的php腳本中,加入緩存的支持,由於有讚的api調用速度較慢,然而這種分銷商城的數據對實時性的要求並不是很高,故採用服務器緩存的方式來減少與有贊通訊這部分導致的速度減緩.另外,本地化的對象代替從遠程數據庫讀取信息,也減少了查詢數據庫的開支.Cache對象的實現如下,將對象的序列化代碼存於服務器本地:
<?php
namespace System;
class Cache
{
public function __construct()
{
}
static public function readCache($string)
{
error_reporting(1);
$file=fopen($string.".ser","r");
if(!$file)return false;
$ser=fread($file,filesize($string.".ser"));
fclose($file);
$array=array();
$array=unserialize($ser);
if(time()-$array['time']>3600*24)return false;
return $array['data'];
}
static public function updateCache($string,$data)
{
$array=array();
$array['time']=time();
$array['data']=$data;
$file=fopen($string.".ser","w");
fwrite($file,serialize($array));
fclose($file);
}
}
後端AJAX響應頁面根據Cache類的反饋選擇獲取本地數據,或是請求服務器更新本地數據:
<?php
require_once('System/db.php');
require_once('System/cache.php');
use \System\Database;
use \System\Cache;
switch($_GET['action'])
{
case 'num':
$result=Database::doQuery("select count(*) from member_new");
$row=$result->fetch_array();
echo $row[0];
exit(0);
case 'get':
if($array=Cache::readCache("member"))
{
echo json_encode($array);
}
else
{
$db=new Database();
$db->init(false,true);
$arr=array();
for($i=0;$i<count($db->member);$i++)
{
$arr[$i]=array();
$arr[$i]=$db->member[$i]->data;
$arr[$i]['password']=null;
$arr[$i]['name']=iconv("GBK","utf-8",$arr[$i]['name']);
$arr[$i]['nickname']=iconv("GBK","utf-8",$arr[$i]['nickname']);
$arr[$i]['sell']=$db->member[$i]->getSell();
}
Cache::updateCache("member",$arr);
echo json_encode($arr);
}
exit(0);
經過以上優化,原本響應時間在15s左右的頁面,現在響應時間爲0.5s左右.