什么是Base64?
按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)
Base64编码详解
Base64 编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
具体转化形式间下图:
字符串”小李”,GBK的中文编码是D0A1 C0EE,转化成字节为
11010000 10100001 11000000 11101110
再把8位转为6位
00110100 00001010 00000111 00000000 00111011 00100000
可以这么考虑:把8位的字节连成一串 11010000101000011100000011101110
然后每次顺序选6个出来之后再把这6二进制数前面再添加两个0,就成了一个新的字节。之后再选出6个来,再添加0,依此类推,直到30个二进制数被选,剩下两个怎么办呢,前面照旧补两个0,后面不够6位的,全部用0补全。
让我们来看看实际结果:
字符串”小李”
11010000 HEX:D0 10100001 HEX:A1 11000000 HEX:C0 11101110 HEX:EE
00110100 00001010 00000111 00000000 00111011 00100000
十进制52 十进制10 十进制7 十进制0 十进制59 十进制32
Base64有其自身的编码表:
The Base64 Alphabet
Value Encoding
0 [...]
关键参数一:back_log
要求 MySQL 能有的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间(尽管很短)检查连接并且启动一个新线程。
back_log 值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值 对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制。 试图设定back_log高于你的操作系统的限制将是无效的。
当你观察你的主机进程列表,发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大 back_log 的值了。默认数值是50,我把它改为500。
关键参数二:interactive_timeout
服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户。 默认数值是28800,我把它改为7200。
关键参数三:key_buffer_size
索引块是缓冲的并且被所有的线程共享。key_buffer_size是用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写),到你 能负担得起那样多。如果你使它太大,系统将开始换页并且真的变慢了。默认数值是8388600(8M),我的MySQL主机有2GB内存,所以我把它改为 402649088(400MB)。
关键参数四:max_connections
允许的同时客户的数量。增加该值增加 mysqld 要求的文件描述符的数量。这个数字应该增加,否则,你将经常看到 Too many connections 错误。 默认数值是100,我把它改为1024 。
关键参数五:record_buffer
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。默认数值是 131072(128K),我把它改为16773120 (16M)
关键参数六:sort_buffer
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),我把它改为 16777208 (16M)。
关键参数七:table_cache
为所有线程打开表的数量。增加该值能增加mysqld要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。默认数值是64,我把它改为512。
关键参数八:thread_cache_size
可以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可 以这个变量值。通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。我把它设置为 80。
关键参数九:wait_timeout
服务器在关闭它之前在一个连接上等待行动的秒数。 默认数值是28800,我把它改为7200。
注:参数的调整可以通过修改 /etc/my.cnf 文件并重启 [...]
在Drupal中创建复杂的、动态的内容是很easy的,但需要做出一定的牺牲的。现在很多Web 2.0的特性的加入,使得网站在变得很cool的同时,也坠入了“性能恶梦(performance nightmare)”的深渊中。每一个节点的加载或一个页面的加载,都会引起高负载、大量的数据库访问、复杂的计算和大量的客户端脚本的执行。
一个解决方案是在Drupal的管理界面开启页面缓存。这个可以使匿名用户(即不登录的情况下)访问速度增加,因为Drupal会把每一个要输出的页面预先缓存(请注意!不是缓存成HTML页面,而是把整个页面的HTML内容序列化为字符串,然后存入数据库),这样可以大大的减少数据库的查询次数。但是,这个对于登录的用户是没有任何效果的:因为页面级的缓存是一种”要么全是要么就一点都没有(all-or-nothing)”的缓存方式,它只有在很标准的情况下才有效,并且所有用户看到的视图都是一样的。
不过,早晚有一天你需要挖掘你的代码,找出数据库的查询访问热点(hot spots),然后自己对程序进行缓存。幸运的是(我最喜欢看到Fortunately这个单词),Drupal有一些内建的关于缓存的API和一些指导性的文档,使得我们可以很easy的自定义Drupal程序的缓存。
基础
做优化和缓存的第一条(The first rule)就是:永远不要使消耗时间的操作执行两次!要充分利用第一次执行的结果,并重用它们(Never do something time consuming twice if you can hold onto the results and re-use them.)下面我们看一个简单的示例:
function my_module_function($reset = FALSE) {
static $my_data;
if (!isset($my_data) || $reset) {
// 在些进行复杂的计算,并使用正确的内容生成$my_data变量
}
return $my_data;
}
这段代码中最重要的部分,就是这个函数创建了一个静态变量——$my_data.静态变量的好处在于,当其第一次被赋值后,就可以被持续的使用,即使这个函数被重新调用。也就是说,我们先检查这个变量是否被赋值,如果已经有值了,那就不需要重新计算,只需要直接返回其值即可。
这种模式经常在Drupal中使用——其中包括很多关键的函数,如node_load。这个函数在被第一次调用时,根据传入的节点ID,查询数据库得到节点对像,并赋值于一个静态变量;然后如果这个节点再在一个Block中被调用,或被列表中调用,就不会再次查询数据库了。
另一个重要的特性是使用$reset参数。缓存确实很好,但偶尔你也需要确保用户访问的页面所得到的数据是绝对新鲜的(absolute freshest)。这时$reset变量就派上用场了。我们可以在需要更新缓存的时候,将$reset设置为TRUE。
Drpual的缓存函数
你可能注意到,静态变量只能在一个单独的页面生存周期内有效。如果需要更好的性能,我们可以使缓存的数据更持久……
function my_module_function($reset = FALSE) {
static $my_data;
if [...]
在数据库中,UNION和UNION ALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。
MySQL中的UNION
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from users1 union select * from user2
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
MySQL 中的UNION ALL
而UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL,如下:
select * from user1 union all select * from user2
1、选取最适用的字段属性
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
2、使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:
DELETE FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN).. 替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:
SELECT * FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
如果使用连接(JOIN).. 来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:
SELECT * FROM customerinfo
LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.
CustomerID
WHERE salesinfo.CustomerID IS NULL
连接(JOIN).. 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
3、使用联合(UNION)来代替手动创建的临时表
MySQL 从 4.0 的版本开始支持 UNION 查询,它可以把需要使用临时表的两条或更多的 SELECT 查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用 UNION 来创建查询的时候,我们只需要用 UNION作为关键字把多个 SELECT 语句连接起来就可以了,要注意的是所有 SELECT 语句中的字段数目要想同。下面的例子就演示了一个使用 UNION的查询。
SELECT Name, Phone FROM [...]
1. PHP加速器的概念
PHP加速器是一个为了提高PHP执行效率,从而缓存起PHP的操作码,这样PHP后面执行就不用解析转换了,可以直接调用PHP操作码,这样速度上就提高了不少。
Apache中使用mod_php的请求、响应执行流程:
1、Apache接收请求。
2、Apache传递请求给mod_php。
3、mod_php定位磁盘文件,并加载到内存中。
4、mod_php编译源代码成为opcode树。
5、mod_php执行opcode树。
PHP加速器相应的就是第四步,它的目的就是防止PHP每次请求都重复编译PHP代码,因为在高访问量的网站上,大量的编译往往没有执行速度快。所以这里面有个瓶颈就是PHP的重复编译既影响了速度又加载了服务器负载,为了解决此问题,PHP加速器就这样诞生了。
2. eAccelerator加速器
eAccelerator是一个自由开放源码php加速器,优化和动态内容缓存,提高了性能php脚本的缓存性能,使得PHP脚本在编译的状态下,对服务器的开销几乎完全消除。 它还有对脚本起优化作用,以加快其执行效率。使您的PHP程序代码执效率能提高1-10倍。
安装:
从官网下载源代码,现在最新代码是0.9.6. http://eaccelerator.net/wiki/Release-0.9.6
$ phpize
$ sudo ./configure –enable-eaccelerator=shared
$ sudo make
$ sudo make install
接下来修改php.ini文件
sudo vi /etc/cgi/php.ini
在文档上加上:
zend_extension=”/usr/lib/php5/20060613+lfs/eaccelerator.so”
eaccelerator.shm_size=”16″
eaccelerator.cache_dir=”/var/cache/eaccelerator”
eaccelerator.enable=”1″
eaccelerator.optimizer=”1″
eaccelerator.check_mtime=”1″
eaccelerator.debug=”0″
eaccelerator.filter=”"
eaccelerator.shm_max=”0″
eaccelerator.shm_ttl=”0″
eaccelerator.shm_prune_period=”0″
eaccelerator.shm_only=”0″
eaccelerator.compress=”1″
eaccelerator.compress_level=”9″
eaccelerator.allowed_admin_path=”/var/www/nginx-default/control.php”
最后一行的eaccelerator.allowed_admin_path的意思是eAccelerator的在线控制文件的实际路径,这样一来就可以使用http://你的网址/control.php来控制eAccelerator了。
下面再建立cache目录:
sudo mkdir /var/cache/eaccelerator
sudo chown root:www-data /var/cache/eaccelerator
sudo chmod u=rwx,g=rwx,o= /var/cache/eaccelerator
然后copy eAccelerator源码中的control.php到web根目录下。这样就可以通过浏览器来浏览控制了,如访问http://localhost/control.php
如果需要control访问权限控制,修改control.php文件,修改$user和$pw的内容。
最后重启nginx
sudo /etc/init.d/nginx restart
现在就安装完成了。
写个测试文件,输出phpinfo,可以看到如下内容:
This program makes use of the Zend Scripting Language Engine:
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
with eAccelerator v0.9.6-rc1, Copyright (c) 2004-2007 eAccelerator, by [...]
今天研究了下PHP MVC结构,所以决定自己写个简单的MVC,以待以后有空再丰富。
至于什么MVC结构,其实就是三个Model,Contraller,View单词的简称,,Model,主要任务就是把数据库或者其他文件系统的数据按照我们需要的方式读取出来。View,主要负责页面的,把数据以html的形式显示给用户。Controller,主要负责业务逻辑,根据用户的Request进行请求的分配,比如说显示登陆界面,就需要调用一个控制器userController的方法loginAction来显示。
下面我们用PHP来创建一个简单的MVC结构系统。
首先创建单点入口,即bootstrap文件index.php,作为整个MVC系统的唯一入口。什么是单点入口呢?所谓单点入口就是整个应用程序只有一个入口,所有的实现都通过这个入口来转发。为什么要做到单点入口呢?单点入口有几大好处:第一、一些系统全局处理的变量,类,方法都可以在这里进行处理。比如说你要对数据进行初步的过滤,你要模拟session处理,你要定义一些全局变量,甚至你要注册一些对象或者变量到注册器里面。第二、程序的架构更加清晰明了。当然好处还有很多的。:)
<?php
include(”core/ini.php”);
initializer::initialize();
$router = loader::load(”router”);
dispatcher::dispatch($router);
这个文件就只有4句,我们现在一句句来分析。
include(”core/ini.php”);
我们来看core/ini.php
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . “core/main”);
//set_include_path — Sets the include_path configuration option
function __autoload($object){
require_once(”{$object}.php”);
}
这个文件首先设置了include_path,也就是我们如果要找包含的文件,告诉系统在这个目录下查找。其实我们定义__autoload()方法,这个方法是在PHP5增加的,就是当我们实例化一个函数的时候,如果本文件没有,就会自动去加载文件。官方的解释是:
Many developers writing object-oriented applications create one PHP source file per-class definition. One of the biggest annoyances is having to write a long list of needed includes at the beginning of each script (one for each class).
In PHP [...]
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式在《Java与模式》中分为三类:
1)简单工厂模式(Simple Factory)
2)工厂方法模式(Factory Method)
3)抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。
GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
工厂方法模式
The main goal of this pattern is delivering an object by hiding all the complexities behind it.
假设我们现在要开发一个Blog系统,为了方便用户在部署这个系统的时候,可以选用不同的数据库系统支持,如MySQL,PostgreSQL,Oracle,MSSQL等。因此我们在系统中创建数据库连接及操作的时候,适合各个数据库系统。
MySQL数据库Manager类
class MySQLManager{
public function setHost($host) {
//set db host
}
public function setDB($db) {
//set db name
}
[...]
Strategy策略模式是属于设计模式中对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类. 策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
为了更好的理解策略模式,我们现在假设一个场景,实现一个通知程序,例如现在我们需要根据客户的需求,给客户发送一个通知,可以用SMS,Email,Fax三种方式传送给客户。
然后根据客户不同的决定,实现不同的发送方式。
简单示例:
首先我们定义接口,一个成员函数 notify().
<?php
//interface.Notifier.php
interface notifier
{
public function notify();
}
?>
下面三种通知方式,都实现上述接口的notify()方法。
class.emailnotifier.php
<?php
include_once(”interface.notifier.php”);
class EmailNotifier implements notifier
{
public function notify()
{
//do something to notify the user by Email
}
}
?>
class.faxnotifier.php
<?php
include_once(”notifier.php”);
class FaxNotifier implements notifier
{
public function notify()
{
//do something to notify the user by Fax
}
}
?>
class.smsnotifier.php
<?
include_once(”notifier.php”);
class [...]
gzcompress — 压缩字符串
string gzcompress (string data [, int level])
说明 :
结果返回一個输入值string的压缩版本,失败则返回false。非必需参数level可以给予0到9,0表示无压缩,9表示最大的压缩。
不过这个压缩不是gzip压缩
在PHP手册是有这么一句:
Note: This is not the same as gzip compression, which includes some header data. See gzencode() for gzip compression.
这个函数返回的压缩版本是This function compress the given string using the ZLIB data format.
例子:
$data = implode(”", file(”bigfile.txt”));
$gzdata = gzencode($data, 9);
$fp = fopen(”bigfile.txt.gz”, “w”);
fwrite($fp, $gzdata);
fclose($fp);
下面我们来看下gzencode:
gzencode
(PHP 4 >= 4.0.4, PHP 5)
gzencode [...]