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