多級分銷對接第三方API獲取數據系統的優化

最近在做一個基於有讚的多級分銷管理系統,所有成員的店面均在有贊商城,使用有贊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左右.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章