天天看點

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)">

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日志中的更新操作,并在從伺服器上回放.

十二.其他

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相同
      
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);