2014年5月

PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。Predis是PHP语言编写。

PHP redis的使用方法详解。php上使用redis主要有两种方式,一种是Predis,一种是phpredis。phpredis是php的一个扩展,以C语言编写的高性能链表。本文讲解Predis的使用。
Predis是Redis官方推出的由PHP原生语言编写的客户端。由于Predis采用了命名空间的方式,所以Predis要求PHP版本最低为5.3。
Predis开源且托管在GitHub上https://github.com/nrk/predis/。下载整个文件夹复制到项目目录即可。

//引入autoload.php文件
require './predis/autoload.php';

//实例化
$redis = New Predis\Client();
/*这个是简化版,等同于$redis = New Predis\Client(array(
 * 'scheme' => 'tcp',
 * 'host' => '127.0.0.1'
 * 'port' => 6379
 *));
 */

//GET
$redis->get('key');

//LPUSH
$redis->lpush('key', '1', '2', '3');

//MSET 相当于$redis->MSET('article:1:title', 'biaoti', 'article:1:content', 'neirong', 'ctime', 'shijian');
$article = array('article:1:title'=>'biaoti', 'article:1:content'=>'neirong', 'article:1:ctime'=>'shijian');
$redis->MSET('key', $article);

//MGET
$articleKeys = array_keys($article);
$redis->MGET($articleKeys);

//SORT
//SORT articleList BY article:*->time LIMIT 0 10 GET article:*->title GET # DESC ALPHA STORE storeKey
$sort = array(
    'by' => 'article:*->time',
    'limit' => array(0, 10),
    'get' => array('article:*->title', '#'),
    'sort' => 'desc',
    'alpha' => true,
    'store' => 'storeKey'
);

Predis的封装之后,用起来非常方便,关联数组的引入是开发效率非常高的。更多的内容可以参考Predis文档:https://github.com/nrk/predis/blob/v0.8/FAQ.md

Redis管道是大幅提升传输速度和用户体验的一个功能。Redis是使用TCP协议进行传输,当客户端发送一条命令之后,到服务器端接收到这个命令,这个过程是需要发送时间的。当服务器处理完命令返回结果给客户端的时候,这个是需要返回时间的。统称为往返时延。如果多条命令一次性发送过去,Redis服务器端全部处理好后一次性发送回客户端,那么就只需要花费一份的往返时延。这就是Redis的管道命令。Redis底层通信协议对管道命令提供了支持。如:

$ (echo -en "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379
+PONG
+PONG
+PONG

关于管道的具体使用,后期在详细说明。这里只需要知道有这个概念即可。

用文件的方式读写,一个文件是索引文件,另一个文件是真实的数据文件。
索引文件分为2部分,第一部分是所有的指针,记录第二部分的位置;第二部分是索引记录。所有的索引指针:是记录所有相同Hash值的key的指针,它是一个链表结构,记录在数据文件的位置和同key的下一个值。
索引记录中:每条记录有四部分,第一部分4个字节,是下一条索引的偏移量;第二部分是该记录的key,128字节;第三部分是数据偏移量,4个字节;第四部分是数据记录长度,4个字节。
我们设定文件的存储上限为262144个。

查找流程如下:
1、根据key算出hash值,获取该hash值的链表在索引文件的第一部分(所有指针区)的位置。
2、根据步骤一的位置,获取值,时间复杂度O(1);
2、根据步骤一中的值,找到索引文件中第二部分(索引记录)的位置,也就是和key相同hash值的所有指针的链表。顺着链表查找该key,获取该key在链表中存放的数据,数据只包含该key在索引文件中的位置,时间复杂度为O(n);
3、根据步骤二所获取的key在索引文件位置,得到索引文件中存放该key的信息。信息包含在真实数据文件中存放真实数据的位置。
4、根据步骤三所获取的位置,在真实数据文件中获取数据,并返回给应用程序。

测试结果:插入10000条耗时:793ms。查找10000条耗时:149ms。虽然这效率只有Redis的十分之一。。。但是请不要在意这些细节。。。

代码做了注释,上述文字有些乱。代码只实现三个方法,一个插入(如果存在则跳过),一个是查找,一个是删除。

思路来源:《PHP核心技术与最佳实践》一书。尊重作者,转载请保留该书名。

<?php
//Hash表中的元素指针个数,每个指针都是int,存储hash链表的文件偏移量
define('DB_BUCKET_SIZE', 262144);
//每条记录的key的长度
define('DB_KEY_SIZE', 128);
//一条索引记录的长度
define('DB_INDEX_SIZE', DB_KEY_SIZE + 12);

//成功-返回码
define('DB_SUCCESS', 1);
//失败-返回码
define('DB_FAILURE', -1);
//key重复-返回码
define('DB_KEY_EXISTS', -2);

class DB{
    private $idx_fp;
    private $dat_fp;
    private $closed;

    /**
     * Description: 打开数据库
     * @param $pathName 数据文件的存放路径
     * @return mixed
     */
    public function open($pathName){
        $idx_path = $pathName . '.idx';
        $dat_path = $pathName . '.dat';
        if(!file_exists($idx_path)){
            $init = true;
            $mode = "w+b";
        }else{
            $init = false;
            $mode = 'r+b';
        }
        $this->idx_fp = fopen($idx_path, $mode);
        if(!$this->idx_fp){
            return DB_FAILURE;
        }
        if($init){
            //把0x00000000转换成无符号长整型的二进制
            $elem = pack('L', 0x00000000);
            for($i=0; $i< DB_BUCKET_SIZE; $i++){
                fwrite($this->idx_fp, $elem, 4);
            }
        }
        $this->dat_fp = fopen($dat_path, $mode);
        if(!$this->dat_fp){
            return DB_FAILURE;
        }

        return DB_SUCCESS;
    }

    /**
     * Description: Times33 Hash算法
     * @param $key
     * @return int
     */
    private function times33Hash($key){
        $len = 8;
        $key = substr(md5($key), 0, $len);
        $hash = 0;
        for($i=0; $i<$len; $i++){
            $hash += 33 * $hash + ord($key[$i]);
        }
        //0x7FFFFFFF:一个十六进制的数是4bit,8个就是32位,就是4字节,和一个int一样大。而F是1111,7是0111,那么这个十六进制的数就是头为0,其余为1的,首位是符号位,也就是说7fffffff是最大的整数。
        //& 0x7fffffff 可以保证返回的数是正整数
        return $hash & 0x7FFFFFFF;
    }

    /**
     * Description: 插入记录
     * @param $key
     * @param $value
     */
    public function add($key, $value){
        $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;

        $idxoff = fstat($this->idx_fp);
        $idxoff = intval($idxoff['size']);

        $datoff = fstat($this->dat_fp);
        $datoff = intval($datoff['size']);

        $keylen = strlen($key);
        $vallen = strlen($value);
        if($keylen > DB_KEY_SIZE){
            return DB_FAILURE;
        }
        //0表示这是最后一个记录,该链再无其他记录。
        $block = pack('L', 0x00000000);
        //键值
        $block .= $key;
        //如果键值的长度没有达到最大长度,则用0填充
        $space = DB_KEY_SIZE - $keylen;
        for($i=0; $i<$space; $i++){
            $block .= pack('C', 0x00);
        }
        //数据所在文件的偏移量
        $block .= pack('L', $datoff);
        //数据记录的长度
        $block .= pack('L', $vallen);
        //尽管SEEK_SET是默认值,但是显式声明了就不怕以后官方会改变了-.-
        fseek($this->idx_fp, $offset, SEEK_SET);
        //检测该key所对应的hash值是否存在了
        $pos = @unpack('L', fread($this->idx_fp, 4));
        $pos = $pos[1];
        //如果key不存在
        if($pos == 0){
            fseek($this->idx_fp, $offset, SEEK_SET);
            fwrite($this->idx_fp, pack('L', $idxoff), 4);

            fseek($this->idx_fp, 0, SEEK_END);
            fwrite($this->idx_fp, $block, DB_INDEX_SIZE);

            fseek($this->dat_fp, 0, SEEK_END);
            fwrite($this->dat_fp, $value, $vallen);

            return DB_SUCCESS;
        }
        //如果key存在
        $found = false;
        while($pos){
            fseek($this->idx_fp, $pos, SEEK_SET);
            $tmp_block = fread($this->idx_fp, DB_INDEX_SIZE);
            $cpkey = substr($tmp_block, 4, DB_KEY_SIZE);
            //$cpkey==$key时返回0,小于返回负数,大于返回正数
            if(!strncmp($cpkey, $key, $keylen)){
                $dataoff = unpack('L', substr($tmp_block, DB_KEY_SIZE + 4, 4));
                $dataoff = $dataoff[1];
                $datalen = unpack('L', substr($tmp_block, DB_KEY_SIZE + 8, 4));
                $datalen = $datalen[1];
                $found = true;
                break;
            }
            $prev = $pos;
            $pos = @unpack('L', substr($tmp_block, 0, 4));
            $pos = $pos[1];
        }

        if($found){
            return DB_KEY_EXISTS;
        }
        fseek($this->idx_fp, $prev, SEEK_SET);
        fwrite($this->idx_fp, pack('L', $idxoff), 4);
        fseek($this->idx_fp, 0, SEEK_END);
        fwrite($this->idx_fp, $block, DB_INDEX_SIZE);
        fseek($this->dat_fp, 0, SEEK_END);
        fwrite($this->dat_fp, $value, $vallen);
        return DB_SUCCESS;
    }

    /**
     * Description: 查询一条记录
     * @param $key
     */
    public function get($key){
        //计算偏移量,key的hash值对索引文件的大小求模,再乘4。因为每个链表指针大小为4
        $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;
        //SEEK_SET是默认的
        fseek($this->idx_fp, $offset, SEEK_SET);
        $pos = unpack('L', fread($this->idx_fp, 4));
        $pos = $pos[1];

        $found = false;
        while($pos){
            fseek($this->idx_fp, $pos, SEEK_SET);
            $block = fread($this->idx_fp, DB_INDEX_SIZE);
            $cpkey = substr($block, 4, DB_KEY_SIZE);

            if(!strncmp($key, $cpkey, strlen($key))){
                $dataoff = unpack('L', substr($block, DB_KEY_SIZE + 4, 4));
                $dataoff = $dataoff[1];

                $datalen = unpack('L', substr($block, DB_KEY_SIZE + 8, 4));
                $datalen = $datalen[1];

                $found = true;
                break;
            }
            $pos = unpack('L', substr($block, 0, 4));
            $pos = $pos[1];
        }
        if(!$found){
            return null;
        }
        fseek($this->dat_fp, $dataoff, SEEK_SET);
        $data = fread($this->dat_fp, $datalen);
        return $data;
    }

    /**
     * Description: 删除
     * @param $key
     */
    public function delete($key){
        $offset = ($this->times33Hash($key) % DB_BUCKET_SIZE) * 4;
        fseek($this->idx_fp, $offset, SEEK_SET);
        $head = unpack('L', fread($this->idx_fp, 4));
        $head = $head[1];
        $curr = $head;
        $prev = 0;
        $found = false;
        while($curr){
            fseek($this->idx_fp, $curr, SEEK_SET);
            $block = fread($this->idx_fp, DB_INDEX_SIZE);

            $next = unpack('L', substr($block, 0, 4));
            $next = $next[1];

            $cpkey = substr($block, 4, DB_KEY_SIZE);
            if(!strncmp($key, $cpkey, strlen($key))){
                $found = true;
                break;
            }
            $prev = $curr;
            $curr = $next;
        }
        if(!$found){
            return DB_FAILURE;
        }
        //删除索引文件。
        if($prev == 0){
            fseek($this->idx_fp, $offset, SEEK_SET);
            fwrite($this->idx_fp, pack('L', $next), 4);
        }else{
            fseek($this->idx_fp, $prev, SEEK_SET);
            fwrite($this->idx_fp, pack('L', $next), 4);
        }
        return DB_SUCCESS;
    }

    public function close(){
        if(!$this->closed){
            fclose($this->idx_fp);
            fclose($this->dat_fp);
            $this->closed = true;
        }
    }
}
?>

测试,测试添加一万条和查找一万条:

<?php
//先include上面的类。。如果在同一个文件中就不用了。
//测试
$db = new DB();
$db->open('/var/www/data/');

$startTime = microtime(true);

//插入测试...插入10000条:成功,耗时: 793.48206520081ms
//for($i=0; $i<10000; $i++){
//    $db->add('key'.$i, 'value'.$i);
//}

//查找测试...查找10000条:成功,耗时: 149.08313751221ms
for($i=0; $i<10000; $i++){
    $db->get('key'.$i);
}

$endTime = microtime(true);
echo '成功,耗时: ' . (($endTime - $startTime)*1000) . 'ms';
$db->close();
?>

Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。
Hash函数把任意长度的和类型的key转换成固定长度输出。不同的key可能拥有相同的hash。
Hash表的时间复杂度为O(1)

<?php
/**
 * hash表类
 * Class HashTable
 * Auth Lane
 * Mail lixuan868686@163.com
 * Blog http://www.lanecn.com
 */
class HashTable{
    private $arr = array();
    private $size = 10;
    public function __construct(){
        //SplFixedArray创建的数组比一般的Array()效率更高,因为更接近C的数组。创建时需要指定尺寸
        $this->arr = new SplFixedArray($this->size);
    }

    /**
     * Description: 简单hash算法。输入key,输出hash后的整数
     * @param $key
     * @return int
     */
    private function simpleHash($key){
        $len = strlen($key);
        //key中每个字符所对应的ASCII的值
        $asciiTotal = 0;
        for($i=0; $i<$len; $i++){
            $asciiTotal += ord($key[$i]);
        }
        return $asciiTotal % $this->size;
    }

    /**
     * Description: 赋值
     * @param $key
     * @param $value
     * @return bool
     */
    public function set($key, $value){
        $hash = $this->simpleHash($key);
        $this->arr[$hash] = $value;
        return true;
    }

    /**
     * Description: 取值
     * @param $key
     * @return mixed
     */
    public function get($key){
        $hash = $this->simpleHash($key);
        return $this->arr[$hash];
    }

    public function getList(){
        return $this->arr;
    }

    public function editSize($size){
        $this->size = $size;
        $this->arr->setSize($size);
    }
}
?>

下面对我们的HashTable进行测试。

<?php
//测试1
$arr = new HashTable();
for($i=0; $i<15; $i++){
    $arr->set('key'.$i, 'value'.$i);
}
print_r($arr->getList());
//SplFixedArray Object
//(
//    [0] => value14
//    [1] => value4
//    [2] => value5
//    [3] => value6
//    [4] => value7
//    [5] => value8
//    [6] => value10
//    [7] => value11
//    [8] => value12
//    [9] => value13
//)
//不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作。

//测试2
$arr->editSize(15);
for($i=0; $i<15; $i++){
    $arr->set('key'.$i, 'value'.$i);
}
print_r($arr->getList());
//SplFixedArray Object
//(
//    [0] => value14
//    [1] => value4
//    [2] => value0
//    [3] => value1
//    [4] => value2
//    [5] => value3
//    [6] => value10
//    [7] => value11
//    [8] => value12
//    [9] => value13
//    [10] => value14
//    [11] => value9
//    [12] =>
//    [13] =>
//    [14] =>
//)
?>

改变了值之后可以存放更多的元素。但是仍然存在不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作的问题。这种冲突的问题我们来用拉链法解决。

拉链法解决冲突。拉链法解决冲突的做法是将所有的相同Hash值的key放在一个链表中,比如key3和key14在hash之后都是0,那么在数组的键为0的地方存储这两个值,形式是链表。如果不能理解我的文字,请看下面的示例,看一下打印信息就明白了。拉链法是什么,就是链表。
创建一个HashNode类,用来存储key和value的值,并且存储相同hash的另一个元素。在同一条链上,查找越后的元素越费时。时间复杂度为O(n).

<?php
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 NewHashTable{
    private $arr;
    private $size = 10;
    public function __construct(){
        $this->arr = new SplFixedArray($this->size);
    }
    private function simpleHash($key){
        $asciiTotal = 0;
        $len = strlen($key);
        for($i=0; $i<$len; $i++){
            $asciiTotal += ord($key[$i]);
        }
        return $asciiTotal % $this->size;
    }
    public function set($key, $value){
        $hash = $this->simpleHash($key);
        if(isset($this->arr[$hash])){
            $newNode = new HashNode($key, $value, $this->arr[$hash]);
        }else{
            $newNode = new HashNode($key, $value, null);
        }
        $this->arr[$hash] = $newNode;
        return true;
    }
    public function get($key){
        $hash = $this->simpleHash($key);
        $current = $this->arr[$hash];
        while(!empty($current)){
            if($current->key == $key){
                return $current->value;
            }
            $current = $current->nextNode;
        }
        return NULL;
    }
    public function getList(){
        return $this->arr;
    }
}
?>

对我们新的HashTable进行测试

<?php
//测试1
$newArr = new NewHashTable();
for($i=0; $i<30; $i++){
    $newArr->set('key'.$i, 'value'.$i);
}
print_r($newArr->getList());
var_dump($newArr->get('key3'));
//SplFixedArray Object
//(
//    [0] => HashNode Object
//(
//    [key] => key23
//            [value] => value23
//            [nextNode] => HashNode Object
//(
//    [key] => key14
//                    [value] => value14
//                    [nextNode] => HashNode Object
//(
//    [key] => key3
//                            [value] => value3
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [1] => HashNode Object
//(
//    [key] => key24
//            [value] => value24
//            [nextNode] => HashNode Object
//(
//    [key] => key15
//                    [value] => value15
//                    [nextNode] => HashNode Object
//(
//    [key] => key4
//                            [value] => value4
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [2] => HashNode Object
//(
//    [key] => key25
//            [value] => value25
//            [nextNode] => HashNode Object
//(
//    [key] => key16
//                    [value] => value16
//                    [nextNode] => HashNode Object
//(
//    [key] => key5
//                            [value] => value5
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [3] => HashNode Object
//(
//    [key] => key26
//            [value] => value26
//            [nextNode] => HashNode Object
//(
//    [key] => key17
//                    [value] => value17
//                    [nextNode] => HashNode Object
//(
//    [key] => key6
//                            [value] => value6
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [4] => HashNode Object
//(
//    [key] => key27
//            [value] => value27
//            [nextNode] => HashNode Object
//(
//    [key] => key18
//                    [value] => value18
//                    [nextNode] => HashNode Object
//(
//    [key] => key7
//                            [value] => value7
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [5] => HashNode Object
//(
//    [key] => key28
//            [value] => value28
//            [nextNode] => HashNode Object
//(
//    [key] => key19
//                    [value] => value19
//                    [nextNode] => HashNode Object
//(
//    [key] => key8
//                            [value] => value8
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [6] => HashNode Object
//(
//    [key] => key29
//            [value] => value29
//            [nextNode] => HashNode Object
//(
//    [key] => key10
//                    [value] => value10
//                    [nextNode] => HashNode Object
//(
//    [key] => key9
//                            [value] => value9
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [7] => HashNode Object
//(
//    [key] => key20
//            [value] => value20
//            [nextNode] => HashNode Object
//(
//    [key] => key11
//                    [value] => value11
//                    [nextNode] => HashNode Object
//(
//    [key] => key0
//                            [value] => value0
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [8] => HashNode Object
//(
//    [key] => key21
//            [value] => value21
//            [nextNode] => HashNode Object
//(
//    [key] => key12
//                    [value] => value12
//                    [nextNode] => HashNode Object
//(
//    [key] => key1
//                            [value] => value1
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//    [9] => HashNode Object
//(
//    [key] => key22
//            [value] => value22
//            [nextNode] => HashNode Object
//(
//    [key] => key13
//                    [value] => value13
//                    [nextNode] => HashNode Object
//(
//    [key] => key2
//                            [value] => value2
//                            [nextNode] =>
//                        )
//
//                )
//
//        )
//
//)
//string(6) "value3"
?>

高性能网站架构方案,本文谈了七点网站架构方案,用以优化网站响应时间,实现大型网站技术架构方案。无论是电子商务或者其他网站且可使用。
一、优化网站响应时间的架构方案:
网站能不能留的住用户,一方面是看内容,另一方面是看响应时间。通常有以下几个方式来降低网站响应时间:
1、减少HTTP请求。包括合并css和javascript。减少图片数量,比如利用css的偏移技术来在一个图片中选择不同的位置内容。利用浏览器的Cache功能,我们可以在头中声明是否被浏览器缓存。
2、动态内容静态化。比如永久生成HTML文件。生成静态文件并设定生存时间,到期后查询新的动态内容进行替换。
3、优化数据库。数据库的性能对于项目整体性能中是重中之重。设计良好的Mysql比乱糟糟的Mysql性能高出N个数量级,更别论再引入NOSQL了,比如Redis,MongoDB。
4、使用负载均衡。将请求合理的分发到更多服务器。
5、使用缓存。把花费时间和资源成本高昂的计算结果取出缓存起来,避免重复计算。比如在Mysql前面挡一层Memcached。比如生成一个文件,使用的时候include进来。再比如PHP中的OPCACHE等。

二、压力测试的架构方案:
吞吐率是指单位时间内处理的请求数,单位reqs/s。最大吞吐率是指单位时间内能够处理的最大请求出。模拟足够多的人数和并发请求来测试最大吞吐率的方法叫做压力测试。比如Apache自带的ab(Apache Bench)。ab的参数很多,常用的有请求数(-n),并发用户数(-c),超时时间(-t),长连接(-k),附件一个Cookie(-c name=value)

$ab -c 10 -n 1000 http://localhost/

三、长连接的架构方案:
每次请求都需要TCP的三次握手,握手完比表示连接正式联通,之后再发送数据。那么,把N个请求,就需要3N次握手,传递N次数据,得到N次响应,总共5N。如果把N个请求合成一个请求,就是3次握手,1次传递数据,1次返回响应,共5次。但是,有时候我们需要上一次响应的返回结果来发送新一轮的请求,在这个时候,合并请求并不好实现,这就需要长连接。使用起来很简单,在头中包含如下:

Connection: Keep-Alive

客户端和服务器端都可以设置长连接的最大时间,当两者不统一时以小的一方为准。开启长连接后进行压力测试:

$ab -c 10 -n 1000 http://localhost/

发现提升不止三五倍。本机是提升了8倍的性能。

四、提高Mysql的响应速度的架构方案:
Handlerocker是日本的一位架构师开发。Mysql的一种插件。Handlerocker实现了绕过Mysql的SQL解析层。在Mysql5.1以上版本可以使用,详情可以查看Mysql手册。这里就不在阐述。

五、Mysql主从复制的架构方案:
在分布式部署中,1台主库,N台从库。主库只写,从库只查。主库从库数据需要实现统一,这就是主从复制。优点是:
1、从库备份时,主库可以继续处理更新。
2、优化响应时间。
3、增加健壮性。主库挂了可以切换到从库作为备份。
主从复制的实现过程有三步,1个在主库,2个在从库:
1、主库服务器将用户对数据库更新的操作以二进制格式保存到Binary Log日志文件。然后Binlog Dump线程将Binary Log日志文件传输给从库服务器。
2、从库服务器通过一个I/O线程将主库服务器的Binary Log日志文件中的更新操作复制到一个叫做Relay Log中的中继日志文件中。
3、从库服务器通过另一个SQL线程Relay Log中继日志文件中的操作依次在本地执行,从而实现主从数据库之间数据的同步。
本篇只是简单的列出方案,详细的配置和实现步骤将在另一篇中写到。

六、代理的架构方案:
读取内存的速度是读取硬盘的100000-1000000倍。把访问过的页面缓存在内存中,下次直接从内存中读取,可以有效加速。
1、传统代理。客户端发送请求给代理服务器,代理服务器向WEB服务器取到数据并返回给浏览器。代理服务器就是一个有大的存储空间的Cache。
2、反向代理。和传统代理原理类似,只是使用对象不同。传统代理的使用对象是客户端,反向代理的使用对象是服务器。用户通过反向代理访问Web服务器,Web服务器是隐藏起来的。不过用户不关心这些,权把代理服务器当作真实的Web服务器。反向代理有Vamish。

七、异步计算的架构方案:
比较耗时的比如将用户上传的文件分发到多台机器,比如裁剪图片,视频转码等。可以使用异步方案。让用户无须等待计算结束而是先行返回结果。代表产品有和Memcache同一家的Gearman。关于Gearman的使用可以查看PHP手册。