php核心技術與最佳實踐知識點(下)
九.緩存
1.緩存三大要素:
命中率, 緩存更新策略,緩存最大數據量
2.命中率(mysql為例):
mysql提供了一系列的query cache的global status來提現數據庫緩存的情況:
show global status like ‘%qcache%‘;
Qcache_free_blocks:目前處於空閑狀態的query cache中的內存block數量
Qcache_free_memory:目前處於空閑狀態的query cache內存總量
Qcache_hits:命中率
Qcache_inserts:向query cache插入新的緩存的次數,也就是沒有命中的次數
Qcache_lowmem_prunes:但query cache內存不足時,從query cache中刪除舊的緩存給新的query cache使用的次數
Qcache_not_cached:沒有被緩存的sql數目
Qcache_queries_in_cache:目前在query cache中的sql數
Qcache_total_blocks:query cache中總的block數
緩存命中率:
hit rate=Qcache_queries_in_cache/Com_select
如果數據頻繁更新,導致大量的query cache頻繁失效,query cache不僅不能夠提高效率,反而會使效率降低.
2.緩存更新策略
FIFO(First In First Out): 先進先出,緩存空間不夠的情況下,最先進入緩存的數據會被清除掉.
LFU(Less Frequently Used):最少使用的元素會被清理掉,這要求元素有hit屬性
LRU(Less Recently Used):最近最少使用的元素會被清理掉.緩存的元素有個時間戳,緩存容量不足時,現有緩存元素中時間戳離最近時間最遠的將被清除掉.
MySQL是簡單的FIFO策略
3.緩存最大數據量
超過設定的緩存最大數據量後,一般有四種處理方式:
(1).停止緩存服務,所有緩存數據被清空
(2).拒絕寫入,不再對緩存數據進行更新
(3).根據緩存策略清除舊數據
(4).在3的基礎上淘汰舊數據備份,騰出新空間
實際應用中,(3)和(4)較為常見.
4.apache配置瀏覽器緩存:
需要用到mod_expires和mod_headers
有全局設置和單獨設置:
(1)全局設置:
ExpiresActive On;
ExpiresByType image/gif "access plus 1 month";
....
(2)單獨設置:
<filematch "\.(jpg|gif|png|css|js)">
ExpiresActive On;
ExpiresDefault "access plus 1 year";
</filematch>
十.php一致性hash算法:
class FlexHash { private $serverList = array(); private $isSorted = false; /** * 增加一個服務器 * @param $server * @return bool */ public function addServer($server) { $hash = $this->hash($server); if (! array_key_exists($hash, $this->serverList)) { $this->serverList[$hash] = $server; } $this->isSorted = false; return true; } public function lookup($key) { $hash = $this->hash($key); // 如果沒有排序,先排序,形成一個倒序的環 if (! $this->isSorted) { krsort($this->serverList, SORT_NUMERIC); $this->isSorted = true; } // 如果hash值大於等於一個server的hash,直接返回該server foreach ($this->serverList as $pos => $server) { if ($hash >= $pos) { return $server; } } // 如果不符合,取出最後一個server return end($this->serverList); } /** * @param $key * @return int */ public function hash($key) { $md5 = substr(md5($key), 0, 8); $seed = 33; $hash = 0; for ($i = 0; $i<8; $i++) { $hash = $hash * $seed + ord($md5{$i}); } return $hash & 0x7FFFFFFF; } } $flexHash = new FlexHash(); $flexHash->addServer(‘192.168.2.1‘); $flexHash->addServer(‘127.0.0.1‘); $flexHash->addServer(‘192.168.2.3‘); $flexHash->addServer(‘192.168.2.4‘); echo $flexHash->lookup(‘key1‘) . PHP_EOL; echo $flexHash->lookup(‘key2‘) . PHP_EOL; echo $flexHash->lookup(‘key3‘) . PHP_EOL; echo $flexHash->lookup(‘key4‘) . PHP_EOL; echo $flexHash->lookup(‘key5‘) . PHP_EOL; echo $flexHash->lookup(‘key6‘) . PHP_EOL;
十一.MySQL主從復制步驟
1.BinLog Dump運行在主服務器上,當數據庫有更新時,會將更新以二進制的格式保存到binlog中,然後BinLog Dump線程將binlog日誌傳輸給從服務器.
使用:show processlists;查看,會發現一個命令為Binlog Dump的線程在運行.
2.Slave的I/O線程
從服務器再start slave;後,啟動一個I/O線程,與主服務器建立連接,然後從主服務器拉取binlog並復制到本地的Relay Log(中繼日誌)中
使用show slave status;可以查看該線程.
3.Slave的SQL線程
讀取Relay Log日誌中的更新操作,並在從服務器上回放.
使用show slave status;可以查看該線程.
十二.其他
1.PHP的寫時復制:
如果通過賦值的方式賦值給變量時不會申請新內存來存放新變量所保存的值,而是簡單的通過一個計數器來共用內存,只有在其中的一個引用指向變量的值發生變化時才申請新空間來保存值內容以減少對內存的占用。
看一段代碼: $a = 100; $b = $a; debug_zval_dump($a); 輸出long(100) refcount(3),引用統計為3(debug_zval_dump也引用了a,所以為3); $a = 100; $b = $a; $a = 50; // 寫時復制,會復制a的內容到新的內存,將b指向新的內存,斷開a的引用 debug_zval_dump($a); 輸出long(10) refcount(2)
2.PHP日誌記錄
php.ini
log_errors = On
error_reporting = E_ALL
error_log = PATH //錯誤日誌存放位置
記錄log不會對php運行效率產生實質影響.
3.apache日誌:
ErrorLog logs/error_log //存放位置
LogLevel warn //錯誤日誌級別
CustomLog logs/access_log combined // 訪問日誌
十三.用php實現一個HashTable
class HashTable { private $buckets; private $size = 10; public function __construct() { $this->buckets = new SplFixedArray($this->size); } public function insert($key, $value) { $index = $this->hash($key); $this->buckets[$index] = $value; } public function find($key) { $index = $this->hash($key); return $this->buckets[$index]; } private function hash($key) { $md5 = substr(md5($key), 0, 8); $seed = 31; $hash = 0; for ($i = 0; $i<8; $i++) { $hash = $hash * $seed + ord($md5{$i}); } return ($hash & 0x7FFFFFFF) % $this->size; } } $ht = new HashTable(); $ht->insert(‘key1‘, ‘key1‘); $ht->insert(‘key2‘, ‘key2‘); $ht->insert(‘key3‘, ‘key3‘); $ht->insert(‘key4‘, ‘key4‘); $ht->insert(‘key5‘, ‘key5‘); echo $ht->find(‘key1‘) . PHP_EOL; echo $ht->find(‘key2‘) . PHP_EOL; // 產生了hash沖突,輸出了key4,原因是key2產生的hash和key4相同
使用拉鏈法解決hash沖突,將所有hash值相同的節點鏈接在一個鏈表中,每個節點存放一個值保存下一個節點,查找的時候遍歷查找,這看起來想拉鏈拉開一樣:
class HashNode { public $key; public $value; public $nextNode; public function __construct($key, $value, $nextNode = null) { $this->key = $key; $this->value = $value; $this->nextNode = $nextNode; } } class HashTable { public $buckets; private $size = 10; public function __construct() { $this->buckets = new SplFixedArray($this->size); } public function insert($key, $value) { $index = $this->hash($key); if (array_key_exists($index, $this->buckets)) { // 如果存在,就新建節點,並將該節點的下一節點指向存在的節點 $newNode = new HashNode($key, $value, $this->buckets[$index]); } else { $newNode = new HashNode($key, $value, null); } $this->buckets[$index] = $newNode; } public function find($key) { $index = $this->hash($key); $current = $this->buckets[$index]; while (! is_null($current)) { if ($current->key == $key) { return $current->value; } $current = $current->nextNode; } return null; } private function hash($key) { $md5 = substr(md5($key), 0, 8); $seed = 33; $hash = 0; for ($i = 0; $i<8; $i++) { $hash = $hash * $seed + ord($md5{$i}); } return ($hash & 0x7FFFFFFF) % $this->size; } } $ht = new HashTable(); $ht->insert(‘key1‘, ‘key1‘); $ht->insert(‘key2‘, ‘key2‘); $ht->insert(‘key3‘, ‘key3‘); $ht->insert(‘key4‘, ‘key4‘); $ht->insert(‘key5‘, ‘key5‘); echo $ht->find(‘key1‘) . PHP_EOL; echo $ht->find(‘key2‘) . PHP_EOL; var_dump($ht->buckets);
php核心技術與最佳實踐知識點(下)