《PHP程序設計》讀書筆記

很久沒有更新過個人博客了,主要前一陣子去了實習,如今實習進入尾聲,終於有機會看看書了。php

在前一陣子的實習中,用到最多就是PHP的CI框架和Jquery,因此如今再看一本有關PHP的書籍來深入認識一下PHP吧。也推薦一下你們看這本書:http://book.douban.com/subject/2071057/html

話很少說,直接總結點有用的東西吧。mysql

Chapter2.語言基礎

1.資源

      許多模塊提供了一些函數來處理外部事務。例如:每個數據庫擴展至少有一個函數來鏈接數據庫,一個函數來向數據庫發送查詢,一個函數來關閉數據庫鏈接。由於能夠同時打開多個數據庫鏈接,鏈接函數須要提供標識不一樣鏈接的辦法:資源(或稱句柄)。這樣當你調用數據庫查詢和關閉函數時,才知道使用哪一個連接。程序員

      資源其實是整數。使用資源的主要好處是它會本身完成內存管理。當最後一個對資源值的引用銷燬時,建立該資源的擴展被調用來爲該資源釋放全部內存,關閉全部鏈接等。web

$res = database_connect();  //假定的數據庫鏈接函數
database_query($res); $res = "boo";  //數據庫鏈接自動關閉

在一個函數中咱們最容易發現自動清空資源的好處,將資源指派給一個局部變量,當函數調用結束時,變量的值會自動被PHP回收:正則表達式

function search(){ $res = database_connect(); database_query($res); }

當沒有任何對資源的引用時,它會自動關閉。sql

儘管資源能夠自動銷燬,大多數擴展提供了一個特定的關閉或結束函數。在合適的地方顯示地調用該函數,這種方式比依賴於變量做用域來觸發資源銷燬更好。數據庫

使用is_resource()函數可測試一個值是否爲資源;數組

if(is_resource($x)){ //$x 是一個資源
}

 

2.關於PHP的垃圾回收

PHP使用引用計數(reference counting)和寫時複製(copy-on-write)來管理內存。寫時複製保證了在變量間複製值時不浪費內存,引用計數保證了再也不須要時將內存交還給操做系統。瀏覽器

要理解PHP裏的內存管理,首先要知道符號表(symbol table)的思想。一個變量有兩部分——變量名(如$name)何變量值(如"Fred")。符號表是一個將變量名映射到內存中變量值所在地址的數組。

當從一個變量複製值到另外一個變量是,PHP沒有爲複製值使用更多的內存。相反,它更新了符號表來講明「這兩個變量都是同一個內存塊的名字」。因此下面的代碼實際上並無建立一個新數組:

$worker = array("Fred",35,"Wilma"); $other = $worker;  //數組沒有被複制

若是後來修改了任意一個拷貝,PHP將分配所需的內存來產生複製:

$worker[1] = 36;

經過延遲分配和複製,PHP在不少情形下節省了時間和內存,這就是寫時複製。

符號表指向的每個值都有一個引用計數(reference counting),它的數值表示取得那塊內存的途徑數目。在將數組初值賦給$worker和將$worker賦給$other以後,符號表中指向數組條目($worker和$other)的引用計數爲2。換言之,那片內存有兩種取得方式:

經過$worker或$other。可是在$worker[1]改變後,PHP爲$worker建立了一個新的數組,而且每一個數組的引用計數都只有1。

當一個變量離開做用域時(就像一個函數參數或局部變量到達函數結尾時),它的值的引用計數減爲1。當一個變量在其餘內存空間被賦值時,舊值得引用計數減1。當對一個值的引用計數到達0時,它的內存就會被釋放。這就是引用計數。

引用計數是管理內存的較好方式。保持變量做用域限制於函數中,經過值來傳遞,並讓引用計數來管理內存。若是你想要主動得到更多的信息和控制權來釋放變量的值,可使用isset()和unset()函數。

查看一個變量是否被設置甚至是空字符串,使用isset():

$s1 = isset($name);  //$s1是false
$name = "Fred"; $s2 = isset($name);  //是true

使用unset()來移除一個變量的值:

$name = "Fred"; unset($name);  //值爲NULL

 

3.包含代碼

PHP提供了兩種結構從其餘模塊加載代碼和HTML:require和include。二者的區別在於:嘗試require一個不存在的文件將致使一個致命錯誤(fatal error)而中止腳本的執行;而嘗試include一個不存在的文件則產生一個警告(warning),不會中止腳本的執行。若是PHP不能解析文件中用include或require包含的某些部分,將會打印出一個警告並繼續執行。能夠經過預先調用錯誤屏蔽操做符(@)來屏蔽出錯警告,例如:@include。

 

Chapter3.函數

1.可變參數的函數:經過函數func_num_args(),demo以下:

<?php function count_list(){ if(func_num_args() == 0){ return false; } else{ for($i = 0;$i < func_num_args();$i++){ $count_list += func_get_arg($i); } } } echo count_list(1,5,9);

2.返回值:

PHP函數可使用return關鍵字返回一個單值:

function return_one(){ return 42; }

要返回多個值,則須要返回一個數組:

function return_two(){ return array("Fred",35); }

默認從函數中複製出值。在函數聲明時若是函數名前有「&」符號的話,則返回對其返回值的引用(別名):

$names = array("Fred","Barney","Wilma","Betty"); function &find_one($n){ global $names; return $names[$n]; } $person = &find_one(1);   //Barney
$person = "Barnetta";    //改變了$names[1]

在這段代碼中,find_one()函數返回$name[1]的別名,而不是$name[1]值的拷貝。由於咱們是經過引用賦值,因此$person是$names[1]的別名,而且第二個賦值操做改變了$name[1]中的值。

 

這個技術有時被用於高效地從函數返回大型字符串或數組的值。然而,PHP的寫時複製/淺複製(copy-on-write/shallow-copy)機制一般意味着從函數返回一個引用並非必要的。除非你知道你可能將會改變數據,不然沒必要爲一些大型的數據返回引用。返回引用的缺點是它比返回值慢,而且須要依賴shallow-copy的機制來確保在數據改變以前不會生成數據的副本。

 

Chapter4.字符串

1.在實際應用中,這意味着除非你須要包含轉義序列或替換變量才使用雙引號,不然應該使用單引號括起來的字符串。

2.大括號的經典做用是把變量名從周圍的文本中分隔出來。

$n = 12;
echo "You are the {$n}th Person";
You are the 12th Person

若是沒有大括號的話,PHP就會嘗試打印出變量$nth的值。

3.關於單括號:

在用單括號括起來的字符串中惟一可用的轉義序列是\'(把單引號放在用單引號括起來的字符串中),\\(把一個反斜槓放在用單引號括起來的字符串中)。任何其餘的反斜槓只能被解釋爲一個反斜槓。

4.常見的字符串函數

  • 刪除空白符——trim(),ltrim(),rtrim();
  • 改變大小寫——strtolower(),strtoupper(),ucfirst(),ucwords();
  • html實體轉義——htmlentities();
  • 刪除html標籤——strip_tags();
  • url編碼與解碼——rawurlencode()和rawurldecode();
  • C語言字符串編碼——addcslashes()和stripcslashes();
  • 字符串比較——strcmp();
  • 子串的處理——substr(),substr_count(),substr_replace();
  • 字符串翻轉與填充——strrev()和str_pad();
  • 字符串的分解與合併——explode(),implode()和join();
  • Tokenizing標記——strtok();
  • 字符串查找——strpos();
  • 正則表達式——  
    • 匹配——preg_match();
    • 替換——preg_replace();
    • 拆分——preg_split();
    • 過濾——preg_grep();
    • 引用文本——preg_quote()

 

Chapter5.數組(Arrays)

1.關於索引數組,若是要經過變量替換獲得某個數組元素,則鍵名不要使用引號:

//這些是錯誤的
print "Hello, $person['name']"; print 'Hello, $person["name"]'; //這是正確的
print "Hello, $person[name]";

 2.關於一些數組的經常使用函數:

  1. 填充數組:
array array_pad ( array $input , int $pad_size , mixed $pad_value )

  2.  析取數組中的多個值:要把一個數組的全部值都複製到變量當中,可使用list()結構:

<?php $person = array("Fred",35,'Betty'); list($name,$age,$wife) = $person; ?>

  3.  析取數組:要析取數組的一個子集,可使用array_slice()函數:

array array_slice ( array $array , int $offset [, int $length = NULL [, bool $preserve_keys = false ]] )
<?php $people = array('Tom','Dick','Harriet','Brenda','Jo'); $middle = array_slice($people,2,2);  //$middle爲array('Harriet','Brenda')
?>

  4.  數組分塊:要把數組劃分爲小數組或固定大小的數組,可使用array_chunk()函數:

array array_chunk ( array $input , int $size [, bool $preserve_keys = false ] )

  5.檢查元素是否存在:使用array_key_exists()函數:

bool array_key_exists ( mixed $key , array $search )

注意:在檢查數組元素是否存在時,簡單地這樣進行判斷是不夠的:

if($person['name']){...}  //這樣會讓人誤解

即便數組中有一個元素使用了鍵name,它對應的值也多是false(例如:0,NULL或空字符串),因此咱們要用使用array_key_exists()來代替。

  6.  在數組中刪除和插入元素:

array_splice()函數能夠在數組中刪除或插入元素,而且能夠用被刪除的元素建立另外一個數組:

array array_splice ( array &$input , int $offset [, int $length = 0 [, mixed $replacement ]] )

  7.  在數組和變量間轉換:

   ·從數組建立變量:extract()函數自動地從一個數組建立局部變量。數組元素的鍵名就是變量名:

extract($person);  //變量$name,$age和$wife如今被設置。

  ·從變量建立數組:compact()函數和extract()函數互補。將多個變量名或單個數組做爲參數傳遞給compact(),可合併一個數組。compact()函數建立一個關聯數組,它的鍵是變量名而且值爲變量的值。

array compact ( mixed $varname [, mixed $... ] )

  8.  迭代器函數

current() —— 返回迭代器當前指向的函數;

reset() —— 移動迭代器到數組的第一個元素並返回該元素;

next() —— 移動迭代器到數組的下一個元素並返回該元素;

prev() —— 移動迭代器到數組的上一個元素並返回該元素;

end() —— 移動迭代器到數組的最後一個元素並返回該元素;

each() —— 以數組的形式返回當前元素的鍵和值,並移動迭代器到數組的下一個元素;

key() —— 返回當前元素的鍵;

 

  9.  爲數組的每一個元素調用函數

PHP提供了一個函數array_walk(),用於爲數組中的每一個元素調用用戶自定義函數;

bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] )

   10. 數組概括

array_walk()的近似函數array_reduce(),用於爲數組中的每一個元素調用用戶自定義函數:

mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )

  11. 查找元素值

in_array()函數返回true或false,取決於第一個參數是不是第二個參數指定的數組中元素:

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

  12. 排序——*sort()函數

  13. 翻轉數組

array_reverse()函數翻轉數組中元素的內部順序:

$reserved = array_reserve(array);

array_flip()函數返回數組,它翻轉了每一個元素的鍵-值對的順序(鍵值互換):

$flipped = array_flip(array);

(若是原數組中有幾個元素的值相等,則互換後最後一個鍵名將做爲它的值,全部其餘鍵名的都將丟失。)

  

  14. 隨機順序——shuffle()

  15. 做用於整個數組

  • 計算數組和——array_sum();
  • 合併兩個數組——array_merge();
  • 比較兩個數組的不一樣——array_diff();
  • 從數組中過濾元素——array_filter();

 

  16. 鍵(Key)和值(Value):

array array_keys ( array $input [, mixed $search_value = NULL [, bool $strict = false ]] )  //
array array_values ( array $input )  //

 

Chapter6.對象(Objects)

 1.構造函數

      PHP並不支持構造函數鏈的自動調用,也就是說,當你實例化子類時,只有子類本身的構造函數會被調用,父類的構造函數是不會被調用的。爲了使父類的構造函數也被調用,你要在子類的構造函數中顯式地調用父類的構造函數。

<?php class Person { public $age; function __construct(){ echo "I'm a person."; $this->age = 30; } function getAge(){ $this->age = 30; return $this->age; } } class Student extends Person { function __construct(){ parent::__construct(); echo "I'm a student."; } } $student = new Student; echo $student->getAge()."\n"; ?>

2.自省

自省是一種讓程序檢查對象特性的機制,能夠檢查對象的名稱,父類(若是存在),屬性和訪問等。下面是相關的一些函數:

  • class_exists()——肯定一個類是否存在;
  • get_declared_classes()——返回一個包含全部已定義的類的數組。
  • get_class_methods()和get_class_vars()——獲得一個類中全部的屬性和方法。
  • is_object()——確認某個變量是不是一個對象;
  • get_class()——獲取一個對象的類名;
  • method_exists()——確認一個方法是否存在;
  • get_object_vars()——返回一個對象的屬性數組;
  • get_parent_class()——返回一個類的父類名,如沒有父類,則返回FALSE。

 

Chapter7.Web技術

1.Get方法和Post方法:

一個Get請求把表單的參數編碼成URL形式,這也稱做查詢字符串:

/path/to/chunkify.php?word=despicable&length=3

一個Post請求則經過http請求的主體來傳遞表單參數,不須要考慮url。

 

Get和Post方法的最明顯區別在於URL行。由於Get請求的全部表單參數都編碼在URL中,用戶能夠把一個GET請求加入瀏覽器收藏夾,而對POST請求卻沒法這樣作。

 

GET和POST請求之間的最大不一樣是至關微妙的。HTTP規範指明GET請求是冪等的——也就是說,一個對於一個特定URL的GET請求(包含表單參數),與對應於這一特定URL的GET請求(包含表單參數),與對應於這一特定URL的兩個或多個GET請求是同樣。所以,Web瀏覽器能夠把GET請求(包含表單請求),與對應於這一特定URL的兩個或多個GET請求是同樣的。所以,Web瀏覽器能夠把GET請求獲得的響應頁面緩存起來。這是由於無論頁面被請求了多少次,響應頁面都是不變的。正由於冪等性,GET請求只適用於那些響應頁面永不改變的狀況,例如將一個單詞分解成小塊,或者對數字進行乘法運算。

 

POST請求不具備冪等性。這意味着它們沒法被緩存。在每次刷新頁面時,都會從新鏈接服務器。顯示或者刷新頁面時,你可能會看到瀏覽器提示「Repost from data?(從新發送表單數據)"。因此POST適用於響應內容可能會隨時間改變的狀況,例如:顯示購物車的內容,或者在一個論壇中顯示當前主題。

 

現實中,冪等性經常被忽略。目前瀏覽器的緩存功能不好,而且」刷新「按鈕很容易被用戶點到,因此程序員一般只考慮是否想將參數顯示在瀏覽器的URL地址欄上,若是不想顯示,就用POST方法。但你要記住,在服務器的相應頁面可能會變化的狀況下(例以下訂單或者更新數據庫),不要使用GET方法。

 

2.表單驗證:

當你容許輸入數據時,你一般須要在使用和存儲這些數據以前進行驗證。有多種驗證用戶數據的方法。首先是在客戶端使用JavaScript。可是用戶能夠禁用JavaScript,甚至使用一個不支持JavaScript的瀏覽器,因此用這個方法還不夠。

 

3.維持狀態:

HTTP是一個無狀態的通信協議,這意味着一旦Web服務器完成了客戶端的Web頁面請求,它們之間的鏈接也就斷開了。換句話說,咱們沒法使服務器識別來自於同一個客戶端的一系列請求。

爲了解決Web狀態不維持的問題,程序員提出了很多在兩次請求間跟蹤狀態信息的方法(這稱爲會話跟蹤,session tracking)。其中一種方法是使用隱藏的表單字段來傳遞信息。由於PHP以對待正常表單字段的方式來對待隱藏表單字段,因此能夠經過$_GET和$_POST數組訪問隱藏表單字段的值。

 

另外一個方法是URL重寫,用戶訪問的每個URL都加上附加的信息,附加信息做爲URL的參數。

 

實現狀態維持的第三種方法是使用cookie。Cookie是服務器給客戶端的一些信息。在後繼的請求中,客戶端將把信息返回給服務器,這樣就能夠標識本身的身份。Cookie對於瀏覽器重複訪問是保持客戶端信息頗有用,但它也有不足的地方。主要問題是有些瀏覽器不支持cookie,即便支持,用戶也能夠禁用cookie。因此使用cookie來維持狀態的程序須要使用另外一個叫回退的機制。

在PHP中維持狀態的最好方法是使用其內置的會話跟蹤系統(session-tracking system)。該系統容許你建立一個持久性變量。從程序的不一樣頁面和同一用戶對站點的不一樣訪問均可以訪問該變量。PHP的會話跟蹤機制使用cookie(或URL)在後臺優雅地解決了維持狀態的大部分問題。

 

4.Cookie:

使用cookie有不少須要注意的地方。不是全部客戶端支持和接受cookie,即便支持,用戶可能禁用cookie功能。另外,cookie規範規定了cookie不能大於4KB,一個域只能發送20個cookie,並且一個客戶端只能存儲300個cookie。有的瀏覽器可能限制寬鬆一些,但你不能依賴於此。最後,你沒法控制客戶端瀏覽器使cookie過時——當瀏覽器有能力而且須要新增一個cookie時,它將拋棄一個沒有過時的cookie。你在使cookie短期內過時時要當心,過時時間取決於客戶端機器的時鐘,它未必和服務器時鐘同樣準。有不少用戶的機器時間是不許的,因此你不能依賴於快速過時。

儘管有這麼多限制,cookie仍不失爲一種在瀏覽器重複訪問時保持信息的實用方法。

 

Chapter12.安全(Security)

1.輸入過濾:

建立安全系統最基本的觀念之一是:任何不在系統內部生成的信息都應被視爲「污點」,即潛在的危險。由這個觀念,咱們能夠推論出:每個輸入數據都是危險的,可能被污染的,由於它們都是在系統外部產生的。

當咱們說數據被「污染」,並不表明它就不安全。只是說它多是不安全的,若是咱們沒法肯定它的來源,就必需要檢查和判斷數據的有效性以確保安全。這個檢查的過程叫作過濾。你必須確保只有安全有效的數據進入你的程序。

下面列舉一些在過濾過程當中的最佳實踐的法則:

  • 使用「白名單」,把全部證實有效的數據加入白名單,名單外的數據是非法的。這意味着在你證實這些數據是有效數據以前,要把他們所有視做無效和非法的;
  • 不要試圖修正無效數據,只須要拒絕便可。歷史證實試圖修正無效數據經常會因錯誤致使系統安全漏洞;
  • 使用必定多個命名規則來將已過濾的數據和未通過濾的數據區分開。若是不加以區分,過濾將變得沒有意義。

在過濾以前,不要相信任何數據的安全性。

 

2.SQL注入和轉義輸出:

轉義是一種當數據進入另外的上下文環境時,仍然保持原有的數據的技術。PHP經常被用做不一樣的數據源之間的橋樑。當發送數據到一個遠端的數據源時,你有責任保持正確的數據,使它不會被錯誤地解析。

PHP應用程序會發送到兩個主要的數據源是:解析HTML,Javascript和其餘客戶端技術的HTTP客戶端和解析SQL的數據庫。對於前者,PHP提供了htmlentities()函數:

<?php
    $html = array();
    $html['username'] = htmlentities($clean['username'],ENT_QUOTES,'UTF-8');
    echo "<p>Welcome back, {$html['username']}.</p>";
?>

而對於後者,大多數數據庫提供原生的數據轉義函數。例如MySQL擴展提供mysql_real_escape_string()函數。

 

3.老是初始化你的變量,這在當register_globals指令打開時尤爲重要。

4.關閉register_globals,magic_quotes_gpc和allow_url_fopen。

5.每次使用用戶提供的數據構造一個文件名時,都要用basename()和realpath()來檢查其組成。

6.將包含的文件存儲在網頁跟目錄(document root)以外,最好不要用.inc爲擴展名來命名你要包含的文件。而用.php爲擴展名來命名它們,或者是其餘並不明顯的擴展名。

7.每當用戶的權限等級改變時,都要調用session_regenerate_id()函數。

8.不要建立一個文件而後在修改它的權限。相反,設置umask()以便文件使用正確的權限來建立。

9.不要在eval(),帶有/e選項的preg_replace()以及任何系統命令(exec(),system(),popen(),passthru()和反引號操做符)中使用用戶提供的數據。

 

 

Chapter13.高級應用技術

1.代碼庫

經常使用的三個函數:require(),require_once(),include()和include_once();

2.模板系統

網上一大堆,諸如smarty之類的,不說了。

 

3.輸出及緩衝區

能夠看我轉的另一篇文章,看完很是夠用。http://www.cnblogs.com/sysu-blackbear/p/4022238.html

 

4.壓縮輸出:

如今的瀏覽器均可以壓縮和解壓縮Web頁面中的文本信息;服務器端可把已經壓縮的文本信息發送到客戶端瀏覽器並在瀏覽器端解壓縮。要自動壓縮你的Web頁面,要像這樣對它進行包裝:

<?php
    ob_start('ob_gzhandler');
?>

對短小的頁面進行壓縮是沒有太大意義的,由於壓縮和解壓縮所花費的時間將會超過它發送的時間。可是,對於較大的web頁面(大於5kb)來講壓縮傳輸是很是有意義的。

若是不想在每一個PHP頁面的頂部調用ob_start(),你也能夠在你的php.ini文件中設定output_handler選項的值爲一個回調函數來使他對於每個頁面都生效。若是要進行壓縮的話,該選項的值應該設定爲ob_gzhandler。

 

5.錯誤處理

  ·錯誤報告:int error_reporting ([ int $level ] )

  ·禁止出錯信息:$value = @(2/0); error_reporting(0);

  ·觸發錯誤:能夠用trigger_error()函數在腳本中拋出一個錯誤:bool trigger_error ( string $error_msg [, int $error_type = E_USER_NOTICE ] )

function divider($a,$b) {
    if($b == 0) {
        trigger_error('$b cannot be 0", E_USER_ERROR);
    }
    return ($a / $b);
}

echo divider(200,3);
echo divider(10,0);

  ·打印錯誤日誌:bool error_log ( string $message [, int $message_type = 0 [, string $destination [, string $extra_headers ]]] )

 

6.代碼優化

優化執行時間(Optimizing Execution Time):

  ·若是echo能夠知足你的需求,避免使用printf();

  ·避免在循環裏面從新計算數值,由於PHP解釋器不會移除循環中的不變的量。例如:若是$array的大小沒有變化,請不要這麼作:

 

for ($i = 0; $i < count($array); $i++) { /* do something */ }

 

相反,要像這樣作:

$num = count($array);
for ($i = 0; $i < $num; $i++) { /* do something */ }

  ·只包含(include)你須要的文件,將要包含的文件切分紅只包含要用到的函數。雖然這樣代碼可能會有一點難於管理,但能夠下降很多代碼解析時的開銷。

  ·當一個簡單的字符串操做函數能夠知足須要時,不要使用正則表達式,例如,將字符串中的一個字符轉換爲另外的字符,使用str_replace(),而不是preg_replace();

 

優化內存需求(Optimizing Memory Requirements):

這裏有一些技巧,能夠幫助你減小腳本的內存需求:

  ·儘量使用數字代替字符串

for($i = 0; $i < "10"; $i++)   //很差的做法
for($i = 0; $i < 10; $i++)   //好的做法

  ·使用完一個很大的字符串,請將容納那個字符串的變量設置爲一個空字符串。這樣能夠釋放內存以便從新利用。

  ·只include或者require實際須要的文件,使用include_once或者require_once代替include和require。

  ·若是使用MySQL而且產生了很大的數據集,考慮使用MySQL專用的數據庫擴展,你可使用mysql_unbuffered_query()函數。該函數並不會馬上將整個數據集載入到內存中,而是在須要時逐行進行讀取。

  ·當使用完MySQL或其餘數據庫的數據集後,儘快釋放它們。在用完它們以後還在內存中維持這些數據是沒有任何好處的。

 

7.反向代理和數據同步

雖然添加硬件一般是得到更高性能的最快的方式,但最好仍是先對你的軟件進行性能測試,一般來講,修改軟件比購買新的硬件成本更低。如下是3種常見的針對高流量瓶頸的解決方案:反向代理緩存,負載均衡服務器和數據庫同步。

反向代理緩存:

反向代理(reserve proxy)是位於Web服務器前的一個程序,它處理從客戶端瀏覽器發來的全部鏈接。代理通過優化,能夠快速處理靜態頁面。大多數的動態站點均可以在一小段時間內緩存起來而不會影響服務。一般,你要用Web服務器以外的一臺獨立的機器來運行反向代理程序。

 

舉個例子,假設有一個繁忙的站點,它的首頁每秒鐘被訪問50次。若是首頁頁面中包含兩個數據庫查詢,而且數據庫更新的頻率是一分鐘兩次,那麼經過使用Cache-Control頭信息告訴反向代理對頁面緩存30秒,每分鐘你能夠避免5994次數據庫查詢。這樣作最壞的結果是從數據庫更新到用戶,看到這個新的信息有30秒的延遲。對於大多數應用來講,30秒並非很嚴重的延遲,並且給性能提高帶來很大的益處。

反向代理甚至能夠智能地緩存那些根據瀏覽器類型,接受的語言或者相似特性進行個性化的或者定製的內容。典型的解決方法是發送不一樣的頭信息給緩存,告訴它哪些請求的參數會影響緩存過程。

如今有很多可用的硬件實現的代理緩存,同時也有很是好的軟件實現的代理緩存。如:Squid。

 

負載均衡和重定向:

提升性能的另外一種方法是把負載分攤到一組機器上。一個負載均衡系統(load-balancing system)能夠均勻地分配負載或者將傳入的請求發送到負載最小的機器上。重定向器(redirector)則是一個對傳入的URL地址進行重寫的程序,它能夠精細控制分配請求到單個服務器的過程。

再次強調,完成重定向和負載均衡的軟件是SquidGuard。

 

MySQL同步:

有時候數據庫服務器是整個應用的瓶頸——大量併發請求可讓數據庫服務器陷入癱瘓狀態,從而致使性能降低。同步是最好的解決方案之一,它讓一個數據庫中全部發生的事情很快地同步到一臺或更多其餘的服務器上,這樣你就獲得了多個徹底同樣的數據庫。這可讓你將查詢分散到不少數據庫服務器中,而不老是查詢一臺服務器。

最有效的模型是使用單向同步(one-way replication):系統中只有一臺主數據庫(master),將數據複製到幾個從數據庫(slave)。全部的數據庫寫操做都在主數據庫進行,數據庫讀操做(即select)則平衡分佈到多個從數據庫上。這個技術針對於那些進行大量的讀操做可是寫操做不多的架構。大多數Web應用程序正好適合這種狀況。

 

綜合運用:

對於一個實際應用的高效的系統架構,咱們須要綜合應用以上提到的各類概念,來完成如圖配置:

咱們使用5個獨立的服務器——一個用於反向代理和重定向器,3個Web服務器和1個主數據庫。這個架構能夠處理很是龐大數量的請求,其精確數量僅僅取決於兩個瓶頸——單個的代理和單個的數據庫主服務器。你能夠試着作一些創新:將這兩臺服務器也切分紅多個服務器。除此以外,若是你的應用程序是能夠緩存的而且主要是進行數據庫讀操做的,那麼這樣的系統架構將是一個至關完美的解決方案。

每個Apache服務器有它本身的只讀MySQL服務器,PHP程序中全部的數據庫讀請求都通過一個UNIX-domain的本地socket鏈接到專門的MySQL實例上。在這個系統框架下面,你能夠加入你所須要的任何數量的Apache/PHP/MySQL服務器。任何來自PHP腳本的數據庫寫操做請求將通過TCP socket到達MySQL的主服務器。

相關文章
相關標籤/搜索