PHP系統的伺服器端記憶體快取架構的分析和優化

2021-06-21 16:09:49 字數 4517 閱讀 1521

為了提交伺服器的效能和減小對資料庫的壓力,快取在寶貝專案中應用較多,基本上很少有直接對資料庫的訪問,絕大多數的資料均來自於快取系統。所以快取系統的整體結構和實現效率相對來說比較重要。

類別

memcache:對於由使用者建立的資訊,均採用memcache快取。其特點是快取鍵值中有_1234(userid)的字樣(memcache伺服器是一種高效的分布式記憶體快取伺服器,可以方便的進行擴充套件,「缺點」是資料是通過網路訪問,有一定的網路開銷,這種網路開銷甚至有時候會成為系統的瓶頸!)

apc:對於所有系統配置性的資料採用apc快取。apc快取就存放於web機自身,為了避免過多的占用web機記憶體,一般情況下都只會存放一些訪問頻率很高的資料。()

分析

採用apc之前有考慮過在web機架設memcache伺服器,但最終放棄這個方案。原因如下

如果在web機上架設memcache服務,那一台機器上提供了兩個服務 web 和 memcaced,當然這樣做也沒關係,只是隱隱覺得不太好。畢竟我們嚮往的架構還是各司其職,以便於分析效能和瓶頸,提高整體的效率。

memcache架設在web機上,php訪問memcache 是否就相當於訪問本機記憶體呢。答案是no,即使在web上架設memcache,php的訪問依然是通過網路進行的,memcache有提供乙個sock的連線方式用於本機連線,但網上查到的文章說效能很不理想。memcache是乙個分布式的高效能的快取方案,但他不是全能的方案,他被設計的目的就是為了提供分布式的記憶體快取,現在我們要將他做為乙個本地快取方案本身就是強人所難。

apc本身最大的功能並不是提供記憶體快取,主要是提供對php編譯過程中opcode快取的支援,能極大的提高php在編譯階段的效率。他同時也提供了記憶體快取的功能。並且記憶體快取的效率很高,曾經在網上看過一篇文章說訪問apc快取的速度與訪問php自身記憶體資料的速度差不多。因此這是乙個很高效的快取 機制,並且apc存放於web,對他的訪問沒有網路開銷。這是他與memcache相比的最大特性。

php結構

新增了兩個配置檔案,用於存放apc快取和memcache快取的配置。(過期時間配置),如下。

$config['usercache']['useroneinfo_']['time']='1000';

$config['usercache']['useroneinfo_']['cache_type']='2';

$config['usercache']['useroneinfo_']['descript']='單個使用者快取';

可以方便的在配置中對快取時間進行調整。避免後期再改對程式。

實際**中的快取結構。

相對很簡單的**

<?php

class user

$this->load->config('syscache');

$this->cache->apc_set($cachename, $list, $this->config->config['syscache']['allworklist']['time']);

}return emtpy($list) ? array() : $list;

}private function delinfocache()

public function updatesomething()

return true;}}

?>

如果有快取則讀快取,如果沒有快取則取資料並放入快取。 如果有更新或者刪除等操作時則需刪除快取(刪除即更新快取),其加需注意

刪除快取的操作與建立快取的操作在同乙個類中,並且為private方法,即只能在本類中建立,更新。該類的對像被呼叫時外部是不知道其中有快取的。(這類似於乙個原則,用於防止在**中隨意更新快取造成**混亂)

上面顯示的是apc快取的訪用,memcache訪問類似,其實可以構造cache類讓他根據配置來使用不同的快取方案(file,memcache,apc)。

不足與優化

memcache是分布式快取,所有的資料均請求自網路。有下面乙個應用。

for($i=0; $i<100; $i++ )

這在實際的**中是經常出現的。或者有時候一次php請求中會多次呼叫到getinfo方法。如果是採用的memcache快取則會出現多次請求網路取回相同的資料的情況。這當然是很浪費的。尤其是在所取的資料非常大的情況下。我們需要對這種情況進行處理。

改造getinfo方法

public function getinfo()

$this->load->config('syscache');

$this->cache->apc_set($cachename, $list, $this->config->config['syscache']['allworklist']['time']);

}return emtpy($list) ? array() : $list;

}改動在上面**中黑色標出的部份,即申明乙個全域性的靜態變數存放從快取中取回的資料,靜態變數在函式呼叫完成之後並不會馬上銷毀,而是等php請求結束才會**。在php執行過程中如果多次呼叫到getinfo方法,他會從靜態變數中取出資料並立即返回,不會發起網路請求了。

這樣的改動雖然達到了我們的要求但是在實際中增加了很大的**開銷。每次使用快取都需要單獨設定靜態變數並進行管理。如果可以我們可以改造cache類,將上面的操作在cache中進行,外部呼叫即不用做這些改變。也可以從cache類中繼承乙個子類實現相應的功能。

實施的具體快取方案。

單條記錄和列表記錄的關係

有這樣的情況,使用者的服裝列表,資料量相對較大,而且因為訪問好友家也會要顯示使用者寵物的著裝情況。(這個還是比較頻繁)。因此我們需要在memcache中快取乙個陣列如下$testarr。

array(

[123] =>array(

dress_id=>123

dressmod_id=>12

name=>'test'

.... )

[124]=>array(

... )

)方法大致如下

public function getuseralldresslist()

上面的返回陣列 鍵值即為id 。但在很多時候我們要對物品進行操作的時候需要先取出這個物品的資料然後進行相關的驗證。(防止別人非法穿上了別人的衣服)因此我們需要乙個getone的方法,有兩種方案如下。

function getonedressdetail($dress_id)

這種方案相當於在快取中人工進行了冗餘,因為單條記錄和資料和列表中的資料其實是一致的。這樣子在更新快取時需要對更新單條記錄的快取也需要更新列表的快取。因些不是乙個太好的方案,但是如果對列表的快取更新機制做一定的處理其實也未償不可以應用。比如列表的快取不主動更新(列表讀取的機會很少)

function getonedressdetail($userid, $dress_id)

這樣就取到了使用者的單個服裝。並沒有去查資料庫也沒有為單個衣服建立乙個單獨的快取。

這樣的方式比較靈活,**量較少,對於對單件衣服的操作相對較少的環境應該比較適合。但這種方式也有缺點,比如使用者穿上了某件衣服,就需要更新使用者的快取,我們這裡的單件快取是取自於列表資料,因些我們需要更新整個列表的資料。國為某件衣服的乙個小改變就更新整個列表聽起來似乎是很不合適。並且這樣的方式還有乙個比較大的問題就是我只需要取一條記錄,但是會把所有的列表都從快取伺服器中取出。$useralldresslist = $this->getuseralldresslist($userid);這句是取出了整個快取記錄然後由php選擇其中的一條。對於網路流量的開銷也還是比較大。

於是又有另外一種方案。

這種方案的應用發生了一點變化。

使用者使用者多個寵物,初始化時需一齊獲取所有的寵物資料。並進行相應的操作,比如完成打工,打工,培訓等等。同上面的情況類似我們也需要對寵物的操作進行驗證,也就是要取得單隻寵物的資料。寵物資料相對服裝資料不同,更新非常頻繁,如果每次更新都重建寵物列表的快取有點不划算。於是我想出了下面乙個解決方式。

將列表資料分成兩部分

public function getpetlist($userid)

return $list; }

private function getpetids($userid)

private function getonepetdetail($petid)

即建立乙個ids的快取資料表存放使用者所有寵物的id. 寵物id快取的更新只有在購買寵物時會觸發,相對非常少,建立後基本不需要更新。

通過迴圈去單獨取每個寵物的資料,並整合成列表資料。 寵物資料更新時只需要更新單隻寵物的資料,不需要重建列表的快取。

以上的三種方案根據不同的資料更新頻率來使用,應該會有一定的提公升。不過第三種方案相對更為複雜一些。也許並不是乙個很好的模式,而且在建立列表快取的時候有乙個迴圈,會多次連線資料庫。我也並沒有做過效能的測試,因些只是乙個解決的思路,使用中還需要再根據情況再定,不過對於單條記錄更新較頻繁的記錄這樣的方式應該是比較可行的。 *

快取全域性更新

伺服器端加入快取redis

選擇redis作為伺服器端的快取 預設10分鐘清空快取 對一些公用的資料可以加入redis快取,支援多種型別 另一種是memcached快取 命令 redis cli 進入redis 和普通的key value結構不同,redis的key支援靈活的資料結構,除了strings,還有hashes li...

socket伺服器端

伺服器 include winsock2.h include string.h include stdio.h include time.h include stdarg.h include stdlib.h pragma comment lib,ws2 32 void errexit const ...

kerberos伺服器端

1.安裝tcl wget tar zvxf tcl8.5.12 src.tar.gz cd tcl8.5.12 cd unix configure make make install 3.解壓 tar xvf krb5 1.10.3 signed.tar tar zvxf krb5 1.10.3.t...