大型B2C网站高性能可伸缩架构技术探秘

在《世界最大的PHP站点 Facebook后台技术探秘》一文中介绍了一个大型SNS网站的技术组成。今天我们继续大型网站探秘,一起来探秘大型B2C网站的架构技术。作为国内最大的B2C网站,其网站架构一直承载着数据量高速增长压力,要保证良好的负载和流程的使用体验,一个可伸缩性的高性能网站架构必不可少。

 

一、应用无状态

一个系统的伸缩性的好坏取决于应用的状态如何管理。试想一下,假如我们在session中保存了大量与客户端的状态信息的话,那么当保存状态信息的 server宕机的时候,我们怎么办?通常来说,我们都是通过集群来解决这个问题,而通常所说的集群,不仅有负载均衡,更重要的是要有失效恢复 failover,比如tomcat采用的集群节点广播复制,Jboss采用的配对复制等session状态复制策略,但是集群中的状态恢复也有其缺点, 那就是严重影响了系统的伸缩性,系统不能通过增加更多的机器来达到良好的水平伸缩,因为集群节点间session的通信会随着节点的增多而开销增大,因此 要想做到应用本身的伸缩性,我们需要保证应用的无状态性,这样集群中的各个节点来说都是相同的,从而是的系统更好的水平伸缩。

上面说了无状态的重要性,那么具体如何实现无状态呢?此时一个session框架就会发挥作用了。一般通过cookie来实现,或者也可以采用集中 式session管理来完成,说具体点就是多个无状态的应用节点连接一个session 服务器,session服务器将session保存到缓存中,session服务器后端再配有底层持久性数据源,比如数据库,文件系统等等。

 

二、有效使用缓存

做互联网应用的兄弟应该都清楚,缓存对于一个互联网应用是多么的重要,从浏览器缓存,反向代理缓存,页面缓存,局部页面缓存,对象缓存等等都是缓存应用的场景。

一般来说缓存根据与应用程序的远近程度不同可以分为:local cache 和 remote cache。一般系统中要么采用local cache,要么采用remote cache,两者混合使用的话对于local cache和remote cache的数据一致性处理会变大比较麻烦。

在大部分情况下,我们所说到的缓存都是读缓存,缓存还有另外一个类型:写缓存。对于一些读写比不高,同时对数据安全性需求不高的数据,我们可以将其 缓存起来从而减少对底层数据库的访问,比如统计商品的访问次数,统计API的调用量等等,可以采用先写内存缓存然后延迟持久化到数据库,这样可以大大减少 对数据库的写压力。

 

三、应用拆分

首先,在说明应用拆分之前,我们先来回顾一下一个系统从小变大的过程中遇到的一些问题,通过这些问题我们会发现拆分对于构建一个大型系统是如何的重要。
系统刚上线初期,用户数并不多,所有的逻辑也许都是放在一个系统中的,所有逻辑跑到一个进程或者一个应用当中,这个时候因为比较用户少,系统访问量低,因 此将全部的逻辑都放在一个应用未尝不可。但是,兄弟们都清楚,好景不长,随着系统用户的不断增加,系统的访问压力越来越多,同时随着系统发展,为了满足用 户的需求,原有的系统需要增加新的功能进来,系统变得越来越复杂的时候,我们会发现系统变得越来越难维护,难扩展,同时系统伸缩性和可用性也会受到影响。 那么这个时候我们如何解决这些问题呢?明智的办法就是拆分(这也算是一种解耦),我们需要将原来的系统根据一定的标准,比如业务相关性等分为不同的子系 统,不同的系统负责不同的功能,这样切分以后,我们可以对单独的子系统进行扩展和维护,从而提高系统的扩展性和可维护性,同时我们系统的水平伸缩性 scale out大大的提升了,因为我们可以有针对性的对压力大的子系统进行水平扩展而不会影响到其它的子系统,而不会像拆分以前,每次系统压力变大的时候,我们都 需要对整个大系统进行伸缩,而这样的成本是比较大的,另外经过切分,子系统与子系统之间的耦合减低了,当某个子系统暂时不可用的时候,整体系统还是可用 的,从而整体系统的可用性也大大增强了。

因此一个大型的互联网应用,肯定是要经过拆分,因为只有拆分了,系统的扩展性,维护性,伸缩性,可用性才会变的更好。但是拆分也给系统带来了问题, 就是子系统之间如何通信的问题,而具体的通信方式有哪些呢?一般有同步通信和异步通信,这里我们首先来说下同步通信,下面的主题“消息系统”会说到异步通 信。既然需要通信,这个时候一个高性能的远程调用框架就显得非常总要。
 
上面所说的都是拆分的好处,但是拆分以后必然的也会带来新的问题,除了刚才说的子系统通信问题外,最值得关注的问题就是系统之间的依赖关系,因为系统多 了,系统的依赖关系就会变得复杂,此时就需要更好的去关注拆分标准,比如能否将一些有依赖的系统进行垂直化,使得这些系统的功能尽量的垂直,这也是目前公 司正在做的系统垂直化,同时一定要注意系统之间的循环依赖,如果出现循环依赖一定要小心,因为这可能导致系统连锁启动失败。

从上面可以看出,一个大型系统要想变得可维护,可扩展,可伸缩,我们必须的对它进行拆分,拆分必然也带来系统之间如何通信以及系统之间依赖管理等问题。

 

四、数据库拆分

在前面“应用拆分”主题中,我们提到了一个大型互联网应用需要进行良好的拆分,而那里我们仅仅说了”应用级别”的拆分,其实我们的互联网应用除了应 用级别的拆分以外,还有另外一个很重要的层面就是存储如何拆分的。因此这个主题主要涉及到如何对存储系统,通常就是所说的RDBMS进行拆分。

确定了这个小节的主题之后,我们回顾一下,一个互联网应用从小变大的过程中遇到的一些问题,通过遇到的问题来引出我们拆分RDBMS的重要性。

系统刚开始的时候,因为系统刚上线,用户不多,那个时候,所有的数据都放在了同一个数据库中,这个时候因为用户少压力小,一个数据库完全可以应付的 了,但是随着运营那些哥们辛苦的呐喊和拼命的推广以后,突然有一天发现,oh,god,用户数量突然变多了起来,随之而来的就是数据库这哥们受不了,它终 于在某一天大家都和惬意的时候挂掉啦。此时,咱们搞技术的哥们,就去看看究竟是啥原因,我们查了查以后,发现原来是数据库读取压力太大了,此时咱们都清楚 是到了读写分离的时候,这个时候我们会配置一个server为master节点,然后配几个salve节点,这样以来通过读写分离,使得读取数据的压力分 摊到了不同的salve节点上面,系统终于又恢复了正常,开始正常运行了。但是好景还是不长,有一天我们发现master这哥们撑不住了,它负载老高了, 汗流浃背,随时都有翘掉的风险,这个时候就需要咱们垂直分区啦(也就是所谓的分库),比如将商品信息,用户信息,交易信息分别存储到不同的数据库中,同时 还可以针对商品信息的库采用master,salve模式,OK,通过分库以后,各个按照功能拆分的数据库写压力被分担到了不同的server上面,这样 数据库的压力终于有恢复到正常状态。但是是不是这样,我们就可以高枕无忧了呢?NO,这个NO,不是我说的,是前辈们通过经验总结出来的,随着用户量的不 断增加,你会发现系统中的某些表会变的异常庞大,比如好友关系表,店铺的参数配置表等,这个时候无论是写入还是读取这些表的数据,对数据库来说都是一个很 耗费精力的事情,因此此时就需要我们进行“水平分区”了(这就是俗话说的分表,或者说sharding)。

上面说了很多,无非就是告诉大家一个事实“数据库是系统中最不容易scale out的一层”,一个大型的互联网应用必然会经过一个从单一DB server,到Master/salve,再到垂直分区(分库),然后再到水平分区(分表,sharding)的过程,而在这个过程中,Master /salve 以及垂直分区相对比较容易,对应用的影响也不是很大,但是分表会引起一些棘手的问题,比如不能跨越多个分区join查询数据,如何平衡各个shards的 负载等等,这个时候就需要一个通用的DAL框架来屏蔽底层数据存储对应用逻辑的影响,使得底层数据的访问对应用透明化。  

五、异步通信

在”远程调用框架”的介绍中,我们说了一个大型的系统为了扩展性和伸缩性方面的需求,肯定是要进行拆分,但是拆分了以后,子系统之间如何通信就成了 我们首要的问题,在”远程调用框架”小节中,我们说了同步通信在一个大型分布式系统中的应用,那么这一小节我们就来说说异步通信。好了,既然说到了异步通 信,那么”消息中间件”就要登场了,采用异步通信这其实也是关系到系统的伸缩性,以及最大化的对各个子系统进行解耦。

说到异步通信,我们需要关注的一点是这里的异步一定是根据业务特点来的,一定是针对业务的异步,通常适合异步的场合是一些松耦合的通信场合,而对于本身业务上关联度比较大的业务系统之间,我们还是要采用同步通信比较靠谱。

OK,那么下一步我们说说异步能给系统带来什么样子的好处。首先我们想想,假如系统有A和B两个子系统构成,假如A和B是同步通信的话,那么要想使 得系统整体伸缩性提高必须同时对A和B进行伸缩,这就影响了对整个系统进行scale out。其次,同步调用还会影响到可用性,从数学推理的角度来说,A同步调用B,如果A可用,那么B可用,逆否命题就是如果B不可用,那么A也不可用,这 将大大影响到系统可用性,再次,系统之间异步通信以后可以大大提高系统的响应时间,使得每个请求的响应时间变短,从而提高用户体验,因此异步在提高了系统 的伸缩性以及可用性的同时,也大大的增强了请求的响应时间(当然了,请求的总体处理时间也许不会变少)。  

六、非结构化数据存储

在一个大型的互联网应用当中,我们会发现并不是所有的数据都是结构化的,比如一些配置文件,一个用户对应的动态,以及一次交易的快照等信息,这些信 息一般不适合保存到RDBMS中,它们更符合一种Key-value的结构,另外还有一类数据,数据量非常的大,但是实时性要求不高,此时这些数据也需要 通过另外的一种存储方式进行存储,另外一些静态文件,比如各个商品的图片,商品描述等信息,这些信息因为比较大,放入RDBMS会引起读取性能问题,从而 影响到其它的数据读取性能,因此这些信息也需要和其它信息分开存储,而一般的互联网应用系统都会选择把这些信息保存到分布式文件系统中。

随着互联网的发展,业界从08年下半年开始逐渐流行了一个概念就是NOSQL。我们都知道根据CAP理论,一致性,可用性和分区容错性3者不能同时 满足,最多只能同时满足两个,我们传统的关系数据采用了ACID的事务策略,而ACID的事务策略更加讲究的是一种高一致性而降低了可用性的需求,但是互 联网应用往往对可用性的要求要略高于一致性的需求,这个时候我们就需要避免采用数据的ACID事务策略,转而采用BASE事务策略,BASE事务策略是基 本可用性,事务软状态以及最终一致性的缩写,通过BASE事务策略,我们可以通过最终一致性来提升系统的可用性,这也是目前很多NOSQL产品所采用的策 略,包括facebook 的cassandra,apache hbase,google bigtable等,这些产品非常适合一些非结构化的数据,比如key-value形式的数据存储,并且这些产品有个很好的优点就是水平伸缩性。目前公司 也在研究和使用一些成熟的NOSQL产品。
     

七 监控、预警系统

对于大型的系统来说,唯一可靠的就是系统的各个部分是不可靠。

因为一个大型的分布式系统中势必会涉及到各种各样的设备,比如网络交换机,普通PC机,各种型号的网卡,硬盘,内存等等,而这些东东都在数量非常多 的时候,出现错误的概率也会变大,因此我们需要时时刻刻监控系统的状态,而监控也有粒度的粗细之分,粒度粗一点的话,我们需要对整个应用系统进行监控,比 如目前的系统网络流量是多少,内存利用率是多少,IO,CPU的负载是多少,服务的访问压力是多少,服务的响应时间是多少等这一系列的监控,而细粒度一点 的话,我们就需对比如应用中的某个功能,某个URL的访问量是多,每个页面的PV是多少,页面每天占用的带宽是多少,页面渲染时间是多少,静态资源比如图 片每天占用的带宽是多少等等进行进一步细粒度的监控。因此一个监控系统就变得必不可少了。

前面说了一个监控系统的重要性,有了监控系统以后,更重要的是要和预警系统结合起来,比如当某个页面访问量增多的时候,系统能自动预警,某台 Server的CPU和内存占用率突然变大的时候,系统也能自动预警,当并发请求丢失严重的时候,系统也能自动预警等等,这样以来通过监控系统和预警系统 的结合可以使得我们能快速响应系统出现的问题,提高系统的稳定性和可用性。

 
八、配置统一管理

一个大型的分布式应用,一般都是有很多节点构成的,如果每次一个新的节点加入都要更改其它节点的配置,或者每次删除一个节点也要更改配置的话,这样 不仅不利于系统的维护和管理,同时也更加容易引入错误。另外很多时候集群中的很多系统的配置都是一样的,如果不进行统一的配置管理,就需要再所有的系统上 维护一份配置,这样会造成配置的管理维护很麻烦,而通过一个统一的配置管理可以使得这些问题得到很好的解决,当有新的节点加入或者删除的时候,配置管理系 统可以通知各个节点更新配置,从而达到所有节点的配置一致性,这样既方便也不会出错。

 

Windows系统下的Apache性能优化

一般来说,WinNT系统下使用IIS,而Apache在Linux下应用的比较多,但是依然有很多人在WinNT系统下使用Apache而非 IIS,可能是基于对Windows系统的熟悉吧。今天就来说一下在Windows系统下如果优化Apache的性能。 mpm_winnt.c 是专门针对Windows NT优化的MPM(多路处理模块),它使用一个单独的父进程产生一个单独的子进程,在这个子进程中轮流产生多个线程来处理请求。也就是说 mpm_winnt只能启动父子两个进程, 不能像Linux下那样同时启动多个进程。 mpm_winnt主要通过ThreadsPerChild和MaxRequestsPerChild两个参数来优化Apache,下面详细来说明一下。ThreadsPerChild 这个参数用于设置每个进程的线程数, 子进程在启动时建立这些线程后就不再建立新的线程了. 一方面因为mpm_winnt不能启动多个进程, 所以这个数值要足够大,以便可以处理可能的请求高峰; 另一方面该参数以服务器的响应速度为准的, 数目太大的反而会变慢。因此需要综合均衡一个合理的数值。
mpm_winnt上的默认值是64, 最大值是1920. 这里建议设置为100-500之间,服务器性能高的话值大一些,反之值小一些。
MaxRequestsPerChild 该参数表示每个子进程能够处理的最大请求数, 即同时间内子进程数目.设置为零表示不限制, mpm_winnt上的默认值就是0。官方参考手册中不建议设置为0, 主要基于两点考虑: (1) 可以防止(偶然的)内存泄漏无限进行,从而耗尽内存; (2) 给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。因此这个参数的值更大程度上取决于服务器的内存,如果内存比较大的话可以设置为0或很大的数字,否则设置一个小的数值。需要说明的是,如果这个值设置的太小的话会造成Apache频繁重启,在日志文件中会看到如下的文字:     Process exiting because it reached MaxRequestsPerChild. Signaling the parent 这样一来降低了Apache的总体性能。另外,可以通过查看Apache提供的server-status(状态报告)来验证当前所设置数值是否合理,在httpd.conf文件中做如下设置来打开它:
# 首先需要加载mod_status模块
LoadModule status_module modules/mod_status.so
# 然后设置访问的地址
   SetHandler server-status
   Order deny,allow
   Deny from all
    # 如果限制某个IP访问则设置为 Allow from 192.168.1.1
    Allow from all
综合来说,因为Windows NT下Apache只能启动父子两个进程,因此只能通过增大单个进程的线程数以及单个进程能够处理的最大请求数来进行优化。其他优化的参数同Linux系统下是一样的,大家可以加以参考。下面针对上述两个参数给出一个建议的设置:
ThreadsPerChild 250
MaxRequestsPerChild 5000
其它就和linux下的apache优化一样
三、apache普通配置参数
  1、静态还是动态
  使用apache的动态载入模块非常方便,因为在需要时模块才会被载入。虽然有些性能开销,但同时有利于减少服务器对内存的需求。
   静态载入虽然一次性载入所有需要的模块,增加内存消耗。因此我们全部采用动态载入的方法。
2、hhostnamelookups off
   域名查找:这增加了处理每个请求的开销,首先,服务器会对dns系统做一个反向查询以找出客户系统的主机名,然后又进行正向查询看获得的主机名是否真实指向客户的ip。大多数情况下,你可以简单的关闭这个功能,如果你经常处理服务器日志,这个工作完全可以在以后进行。你可以通过在设置文件中加入指示 hostnamelookups off来关闭这个功能。
  3、options -followsymlinks
   符号连接:当打开这个选项时,apache将检查每个请求中是否包含对符号连接的引用,这将对请求中包含的每个路径调用一次lstat()系统调用。除非你准备使用符号连接,否则用 options -followsymlinks 来关掉它。
  4、sethandler server-status
   服务器状态信息,默认已经关闭。该模块尽管这对测试与监控服务器很有用,但它也为服务器带来了额外的开销,你可以通过寻找任何类似sethandler server-status的指示来关闭,如果可能,你可以在安装apache时移除这个模块。
  5、options -indexes
   关闭目录浏览
   6、directoryindex index.php index.html    
  7、cgi模块
   除非你有很好的理由否则就允许cgi的执行,将似有的cgi文件放到一个特定的目录并为之设定正确的权限,这避免了apache对每一个请求都要判断一次要求的是一个静态文件还是一个动态文件。
  8、写入日志
   写入日志信息是一个很花费时间的工作,apache保持日志文件的打开状态以节省打开文件的时间,如果没有必要存储日志信息,你可以关闭这个选项以节省出更多的处理器时间,只需要在设置文件中把日志那一行注释掉就可以关掉它。
  如果必须保留日志,你可以关闭hostnamelookups选项(见上文)然后把日志文件拷备到另一台机器上做进一步分析。
  9、allowoverride none
  .htaccess文件可以极大的扩展apache的设置参数,而无需每次你改变设计都要编辑apache主设置文件,但对这个文件的使用也降低了服务器的性能。
  如果使用这个文件,apache必需首先在当前目录中查找是否存在这个文件,如果存在就解析这个文件并在当前目录中应用文件中的设置。更坏的是,apache不仅要查看当前的目录,还要查看当前目录的所有上层目录是否包括htaccess文件以根据所有这些文件最终确定设置。
  如果你想最优化服务器的性能,你应该禁止使用htaccess文件,任何基本目录的设置都可以在主设置文件中进行,而主设置文件仅在服务器启动时解析一次。为了禁用htaccess文件,在任何节里加上指示allowoverride none。
   10、timeout 5
   timeout 设置 apache 等待一个连接读写操作的时间长度,也就是连接建立后,apache 等待客户端完成请求发送的时间,或者是响应开始之后,apache 写出数据到客户端连接的时间长度。
   无论对于哪种应用来说,300秒的缺省值都有些过长了,因为这就意味着,如果客户端发生了某种未知因素导致的迟滞的话,服务器的一个连接和与之对应的所有资源都要维持 300 秒,这个对于重载的服务器来说是在是有些过长,所以,我建议将其设置得小一些,这个长度只要足够保证各种客户端的应用能够正常传递数据即可。这里需要考虑的因素主要有各种客户端的连接状况和服务器的繁忙程度。一般来说,我建议设置为 3~5。
   11、keepalive on
   这个选项明确 httpd 进程对每个请求的链接是否保持长链接。如果保持长链接,则从同一个客户端的连续两次请求会使用同一个连接,而不用重复发送请求。
   对于下载类的应用,因为连接时间都比较长,因此这个值设置成 on 还是 off 关系不大,从节约每一滴资源角度考虑,可以设置为 off。
对于网页类应用来说,如果你的静态页面上有一些图标、图片、和js、css等东西的话,并且如果有超过两个的资源的话,我建议是设置为 on。
   12、maxkeepaliverequests 100
   最多保持多少个活动的长链接
   13、 keepalivetimeout 5    apache进程在使用内存时,是“渐长”的。也就是说,直到这个进程死掉,使用内存的数量是一直增长而不会减少的。这样的话,apache进程使用内存的多少,就决定于你的应用程序最大使用内存量了。
   keepalivetimeout 这个参数决定了,在什么都不做之前,一个http进程能够等待多长时间?设想一下,如果keepalive设置为on,而 keepalivetimeout设置为一个比较大的数字,apache占用内存会很快的增长。这是因为,一个apache进程完成了一个任务(并达到了一定的内存占用,想一下“渐进”模式),并不会马上退出,而是等待一个keepalivetimeout时间。假设用户的链接请求持续不断的到来,则积累起来的无用的apache进程就会相当多,直到timeout,这些进程才会被杀死。
   但是,keepalive的确对于静态的文件,比如图像文件的传送是很有效的,因此,keepalive要设置为on,但是keepalvietimeout要设置的小些,比如5s
   14、serversignature off
   默认情况下,很多apache安装时会显示版本号及操作系统版本,甚至会显示服务器上安装的是什么样的apache模块。这些信息可以为黑客所用,并且黑客还可以从中得知你所配置的服务器上的很多设置都是默认状态。    serversignature off
   servertokens prod
   serversignature 出现在apache所产生的像404页面、目录列表等页面的底部。servertokens目录被用来判断apache会在server http响应包的头部填充什么信息。如果把servertokens设为prod,那么http响应包头就会被设置成:server:apache
四、MPM模块    多处理方式(multi-processing module/mpm)他允许特定平台处理多个并发连接
  apache的mpm模块可运行在多种模式之下,其中beos、mpmt_os2分别是beos和os/2上缺省的mpm, perchild主要设计目的是以不同的用户和组的身份来运行不同的子进程。这在运行多个需要cgi的虚拟主机时特别有用,会比1.3版中的suexec 机制做得更好。leader和threadpool都是基于worker的变体,还处于实验性阶段,某些情况下并不会按照预期设想的那样工作,所以 apache官方也并不推荐使用。因此,我们主要阐述prefork和worker这两种和性能关系最大的产品级mpm ( 有关其它的mpm详细说明,请参见apache官方文档:http://httpd.apache.org/docs-2.0/mod/)。
  1、prefork的工作原理及配置
  prefork就是unix平台上缺省的mpm。它所采用的预派生子进程方式也是 apache 1.3中采用的模式。prefork本身并没有使用到线程,2.0版使用它是为了与1.3版保持兼容性;另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的mpm之一。
  如果是使用debian的apt安装的apache,使用”apache2ctl -l”来确定当前使用的mpm,应该会看到prefork.c(如果看到worker.c说明使用的是worker mpm,依此类推),在apache2.conf中可以找到这一段配置
<IfModule mpm_prefork_module>
   StartServers          5
   MinSpareServers       5
   MaxSpareServers      10
   MaxClients          150
   MaxRequestsPerChild   0
</IfModule>
  prefork的工作原理是,控制进程在最初建立”StartServers”个子进程后,为了满足”MinSpareServers”设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足 MinSpareServers设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。
  MaxSpareServers设置了最大的空闲进程数,如果空闲进程数大于这个值,apache会自动kill掉一些多余进程。这个值不要设得过大,但如果设的值比MinSpareServers小,apache会自动把其调整为MinSpareServers+ 1。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。
   MaxRequestsPerChild设置的是每个子进程可处理的请求数。每个子进程在处理了”MaxRequestsPerChild” 个请求后将自动销毁。0意味着无限,即子进程永不销毁。虽然缺省设为0可以使每个子进程处理更多的请求,但如果设成非零值也有两点重要的好处:   在服务器负载下降的时侯会自动减少子进程数。
  因此,可根据服务器的负载来调整这个值。但也不能太小,不然系统不断的开启新的apache进程,造成资源浪费。
  MaxClients是这些指令中最为重要的一个,设定的是apache可以同时处理的请求,是对apache性能影响最大的参数。其缺省值 150是远远不够的,如果请求总数已达到这个值(可通过ps -ef|grep http|wc -l来确认),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而http访问却很慢的主要原因。系统管理员可以根据硬件配置和负载情况来动态调整这个值。虽然理论上这个值越大,可以处理的请求就越多,但apache默认的限制不能大于256。如果把这个值设为大于256,那么 apache将无法起动。事实上,256对于负载稍重的站点也是不够的。在apache 1.3中,这是个硬限制。如果要加大这个值,必须在“configure”前手工修改的源代码树下的src/include/httpd.h中查找 256,就会发现“#define hard_server_limit 256”这行。把256改为要增大的值(如4000),然后重新编译apache即可。在apache 2.0中新加入了serverlimit指令,使得无须重编译apache就可以加大maxclients。
   
<IfModule mpm_prefork_module>
   StartServers    10
   MinSpareServers 10
   MaxSpareServers 15
   ServerLimit     600
   MaxClients      300
   MaxRequestsPerChild 600
</IfModule>
  2、worker的工作原理及配置
  相对于prefork,worker是2.0 版中全新的支持多线程和多进程混合模型的mpm。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的服务器。但是, worker也使用了多进程,每个进程又生成多个线程,以获得基于进程服务器的稳定性。这种mpm的工作方式将是apache 2.0的发展趋势。
<IfModule mpm_worker_module>
   StartServers          2
   MaxClients          150
   MinSpareThreads      25
   MaxSpareThreads      75
   ThreadsPerChild      25
   MaxRequestsPerChild   0
</IfModule>
  worker的工作原理是,由主控制进程生成”startservers”个子进程,每个子进程中包含固定的”threadsperchild”线程数,各个线程独立地处理请求。同样,为了不在请求到来时再生成线程,minsparethreads和 maxsparethreads设置了最少和最多的空闲线程数;而maxclients设置了所有子进程中的线程总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。
  minsparethreads和maxsparethreads的最大缺省值分别是75和250。这两个参数对apache的性能影响并不大,可以按照实际情况相应调节。
  threadsperchild是worker mpm中与性能相关最密切的指令。threadsperchild的最大缺省值是64,如果负载较大,64也是不够的。这时要显式使用 threadlimit指令,它的最大缺省值是20000。上述两个值位于源码树server/mpm/worker/worker.c中的以下两行:
究竟是选取prefork还是worker需要具体分析,相对而言高负载下perfork拥有更高的稳定性和运行速度,而worker的资源消耗更小。也已经有人在对两种工作模式作了各种测试:
实际情况看来,worker现在还没能达到所期望的效果,性能比frefork差一些,资源消耗少一点。更可惜的是debian下worker还不能与PHP5完美结合,所以只能选用perfork了。
五、性能测试
为了获得优化有性能提高的幅度,评估优化工作的成效,需要对apache2服务器进行测试。 apache2和php5服务器:debian4.0、apache2.2.3、 php5.2.0-8+etch0、256M内存
在另一台机器上使用apachebench工具模拟多个浏览器向服务器的测试页面发起HTTP请求,为了减少网络带宽的影响,测试页面的返回值尽可能的小,此处只有1 byte,并为发起测试的机器和服务器组建了一个单独的局域网。每种并发测试11次,以后10次的结果为准,取平均值。
以下是测试的数据:其中并发数是指apachebench同时发起的请求个数,优化前和优化后是指平均每个请求花费的处理时间,单位毫秒
并发数优化前(毫秒)优化后(毫秒)
102.0481.7549
502.13891.927
1002.20841.9238
2002.76892.5915
4003.05232.797
需要指出的是,尽管高负载时优化后性能提高的百分比并不明显,但在并发数400时,测试18次失败7次,而优化后测试14次失败3次。优化不仅仅提高了服务器的性能,还提高了负载的能力。
六、结论
优化可以有效的提高apache2的性能。
对于WMS等设备上的配置页面,第三部分的“apache普通配置参数”可以应用,MPM主要是以资源换取速度的优化,可以酌情调整。
对于EMS、升级系统和应用系统,可以全面优化以提高性能和高负载能力。
本贴来自天极网群乐社区–http://q.yesky.com/group/review-17535369.html

require(),include(),require_once()和include_once()的异同

require()和include()有许多相似之处,也有些不同。理解它们的不同点非常重要,否则很容易犯错误。
我把这两个语句放在一起介绍,读者可以比较学习。
1.require()语句
     require()语句用于指定的文件代替语句本身,就象C语言中的include()语句一样。如果php配置文件php.ini中的URL fopen wrappers 是打开的(默认情况下是打开的),就可以使用URL来指定文件的位置从而实现远程文件的调用。
     有一点就是使用require()和include()语句时要特别的注意。那就是在被包含的文件中,处理器是按照html模式来解释其中的内容的,处理完被包含的内容后又恢复到php模式。所以如果需要在被包含文件中使用php语法,就要使用正确的php开始和结束标记来把这些语句包含进去。
     require()和include()知识php中的一种语言特性,而不是函数。它们和函数有许多不同的地方。
比如:require()所包含的文件中不能包含控制结构,而且不能使用return这样的语句。在require()所包含的文件中使用return语句会产生处理错误。
     不象include()语句,require()语句会无条件地读取它所包含的文件的内容,而不管这些语句是否执行。所以如果你想按照不同的条件包含不同的文件,就必须使用include()语句。当然,如果require()所在位置的语句不被执行,require()所包含的文件中的语句也不会被执行。
     require()不能在循环体中根据条件的不同而包含不同的文件。require()语句只会在第一次执行时调用它所包含的文件中的内容替换本身这条语句,当再次被执行时只能执行第一次所包含的语句。但是include()语句可以在循环体中来包含不同的文件。
     require()语句中的变量继承require()语句所在位置的变量作用域。所有在require()语句的位置可以访问的变量,在require()语句所包含的文件中都可以访问。如果require()语句位于一个函数内部,那么被包含文件内的语句都相当于定义在函数内部。
     require()语句在PHP程序执行前就会将使用require引用的文件读入,因此require通常放到程序的开始处。因此要特别注意一点,require语句有一点强,不管程序是否真的需要引用的文件,只要你使用require语句,它都会把他们包含进来!即使你是在条件控制语句中使用这个函数进行包含,那怕是那个条件不为真,引用文件也会被包含进来!形成了僵尸,在运行过程中这些僵尸是不起任何可见作用的,但是很明显它会加重负担,所以这一点要特别注意!如果使用require语句发生了包含错误,那么程序将输出出错信息并停止运行!!
     如果require()语句通过声明文件的URL来包含远程文件,而且远程服务器按照php代码来解释该文件的话,本地php文件中所包含的内容是在远程服务器上处理以后的结果。例如:
     /*
        这个例子假设some_server服务器可以解释.php文件,而不对.txt文件进行解释。在远程文件中  
     需要变量$varfirst和$varsecond
     */
    /*不能正确执行,远程服务器不处理.txt文件*/
    require(“http://some_server/file.txt?varfirst=1&varsecond=2”);
   
    /*不正确,这样只能在本地机上寻找file.php文件*/
    require(“file.php?varfirst=1&varsecond=2”);
    /*正确的语句*/
    require(“http://some_server/file.php?varfirst=1&varsecond=2”);
   
    $varfirst=1;
    $varsecond=2;
    require(“file.txt”); /*正确的语句*/
    require(“file.php”); /*正确的语句*/
     本来在php3.0中,require()所包含的文件可以使用return语句,但条件是return语句不能出现在{}内部,而必须出现在被包含文件的全局范围内。在php4.0中已经取消了require()的这个功能,但是仍然可以使用include()来实现。
2.include()语句
     include()语句和require()语句有许多相同的地方。凡是在上边require()语句中没有明确说明不能适用于include()的部分外,require()语句的功能完全适用于include()语句。下边介绍require()语句所没有的include()语句的功能和特点。
     include语句只有在被执行时才会读入要包含的文件。在错误处理方便,使用include语句,如果发生包含错误,程序将跳过include语句,虽然会显示错误信息但是程序还是会继续执行!
      php处理器会在每次遇到include()语句时,对它进行重新处理,所以可以根据不同情况的,在条件控制语句和循环语句中使用include()来包含不同的文件。
      例如:
    <?php
        $files=array(‘first.php’,’second.php’,’third.php’);
        for($i=0;$i<count($files);$i++)
        {
            include $files[$i];
        }
    ?>
      在php3.0和php4.0中include()语句所包含的文件中都可以使用return语句来返回一个值,并停止执行被包含文件下面的内容。但php3.0和php4.0在处理这样的情况时有所不同。在php3.0中return语句不能包含在{}内,除非它在一个函数中,因为这时它表示函数的返回值而不是文件的返回值。而在php4.0中就没有了这样的限制,用户甚至可以在文件中返回一个数字,就象函数的返回值一样。这样的语句在
php3.0中通常会报告错误。以下举例说明:
      假设被包含的文件为test.inc和主文件main.php位于一个目录中。test.inc的内容如下:
test.inc
<?php
    echo “Before the return<br>\n”;
    if(1)
    {
        return 27;
    }
    echo “After the return<br>\n”;
?>
假设在main.php文件中包含下面的语句:
<?php
    $retval=include(‘test.inc’);
    echo “File returned:’$retval'<br>\n”;
?>
     php3.0解释器会在第二行报告错误,而不能得到include()语句的返回值。但在php4.0中会得到下面的结果:
    Before the return
    File returned: ’27’
    下边假设main.php改为:
<?php
    include(‘test.inc’);
    echo “Back in main.html<br>\n”;
?>
    在php4.0中的输出结果是:
    Before the return
    Back in main.html
   
   在php5.0中的输出结果也是:
    Before the return
    Back in main.html
    在php3.0中的输出结果是:
    Before the return
    27Back in main.html
  
    Parse error:parse error in /apache/htdocs/phptest/main.html on line 5
   
    出现上面的错误是因为return语句位于{}内部而且不是一个函数内部。如果把{}去掉,使它位于test.inc的最外层,输出结果是:
    Before the return
    27Back in main.html
    之所以出现27,是因为在php3.0中不支持include()返回。
3.require_once()和include_once()语句
    require_once()和include_once()语句分别对应于require()和include()语句。require_once()和include_once()语句主要用于需要包含多个文件时,可以有效地避免把同一段代码包含进去而出现函数或变量重复定义的错误。例如:如果创建两个文件util.inc和fool.inc,程序代码分别为:
   util.inc:
    <?php
       define(PHPVERSION,floor(phpversion()));
       echo “GLOBALS ARE NICE<br>\n”;
       function goodTea()
       {
           return “Olong tea tasts good!”;
       }
   ?>
    和fool.inc:
   <?php
       require (“util.inc”);
       function showVar($var)
       {
           if(PHPVERSION==4)
           {
               print_r($var);
           }
           else
           {
               var_dump($var);
           }
       }
   ?>
    然后在error_require.php中包含这两个文件:
    <?php
        require(“fool.inc”);
        require(“util.inc”);//此句会产生一个错误
        $foo=array(“1”,array(“complex”,”quaternion”));
        echo “this is requiring util.inc again which is also<br>\n”;
        echo “required in fool.inc\n”;
        echo “Running goodTea:”.goodTea().”<br>\n”;
        echo “Printing foo:<br>\n”;
        showVar($foo);
    ?>
     当运行error_require.php时,输出结果如下:
     GLOBALS ARE NICE
     GLOBALS ARE NICE
    Fatal error:Cannot redeclare goodTea() in util.inc on line 4
     
    如果使用require_once()语句来代替 require()语句,就不会出现上面的错误。我们把error_require.php和fool.inc中的require()语句改为require_once()语句并重命名为error_require_once.php,这是显示结果如下:
    GLOBALS ARE NICE
    this is requiring util.inc again which is also
    required in fool.inc Running goodTea:Olong tea tastes good!
    Printing foo:
    Array([0] => 1 [1] => Array ([0] => complex [1] = quaternion))
   
    include_once()语句的语法和include()语句类似,主要区别也是避免多次包含一个文件而引起函数或变量的重复定义。
    require_once语句有一个引用链,它可以保证文件加入你的程序仅仅只有一次,而且会避开变量值和函数名之间的冲突。
   
    和require_once语句一样,include_once语句把include的功能扩展了。在程序执行期间,将指定的文件包含进来,如果从文件引用进来的程序先前已经包含过的时候,include_once()就不会把它再包含进来。也就是仅仅可以引用同一个文件一次!
    include_once() 语句在脚本执行期间包含并运行指定文件。此行为和 include() 语句类似,唯一区别是如果该文件中的代码已经被包含了,则不会再次包含。如同此语句名字暗示的那样,只会包含一次。
include_once() 应该用于在脚本执行期间同一个文件有可能被包含超过一次的情况下,想确保它只被包含一次以避免函数重定义,变量重新赋值等问题。
使用 require_once() 和 include_once() 的更多例子见最新的 PHP 源程序发行包中的 PEAR 代码。
返回值和 include() 相同。如果文件已被包含,本函数返回 TRUE。
注: include_once() 是 PHP 4.0.1pl2 中新加入的。
注: 要注意 include_once() 和 require_once() 在大小写不敏感的操作系统中(例如 Windows)的行为
可能不是所期望的。
例子:  include_once() 在 Windows 下不区分大小写
<?php
include_once(“a.php”); // this will include a.php
include_once(“A.php”); // this will include a.php again on Windows! (PHP 4 only)
?>  
此行为在 PHP 5 中改了,路径先被规格化,因此 C:\PROGRA~1\A.php 和 C:\Program Files\a.php 的实现一样,文件只会被包含一次。
    如果要包含的文件不存在,include提示notice,然后继续执行下面的语句,require提示致命错误并且退出。
  win32平台下它们都是先包含后执行,所以被包含文件里最好不要再有include或require语句,这样会造成目录混乱。或许Linux下情况不同,暂时还没测试。
  如果一个文件不想被包含多次可以使用include_once或require_once## 读取,可以写入文档数据。
<?php
function r($file_name) {
 $filenum=@fopen($file_name,”r”);
 @flock($filenum,LOCK_SH);
 $file_data=@fread($filenum,filesize($file_name));
 @fclose($filenum);
 return $file_data;
}
function w($file_name,$data,$method=”w”){
 $filenum=@fopen($file_name,$method);
 flock($filenum,LOCK_EX);
 $file_data=fwrite($filenum,$data);
 fclose($filenum);
 return $file_data;
}
?>

PHP中的CURL函数库

目前为目最全的CURL中文说明了,PHP的要好好掌握.有很多的参数.大部份都很有用.真正掌握了它和正则,一定就是个采集高手了.

PHP中的CURL函数库(Client URL Library Function

curl_close — 关闭一个curl会话
curl_copy_handle —
拷贝一个curl连接资源的所有内容和参数
curl_errno —
返回一个包含当前会话错误信息的数字编号
curl_error —
返回一个包含当前会话错误信息的字符串
curl_exec —
执行一个curl会话
curl_getinfo —
获取一个curl连接资源句柄的信息
curl_init —
初始化一个curl会话
curl_multi_add_handle —
curl批处理会话中添加单独的curl句柄资源
curl_multi_close —
关闭一个批处理句柄资源
curl_multi_exec —
解析一个curl批处理句柄
curl_multi_getcontent —
返回获取的输出的文本流
curl_multi_info_read —
获取当前解析的curl的相关传输信息
curl_multi_init —
初始化一个curl批处理句柄资源
curl_multi_remove_handle —
移除curl批处理句柄资源中的某个句柄资源
curl_multi_select — Get all the sockets associated with the cURL extension, which can then be “selected”
curl_setopt_array —
以数组的形式为一个curl设置会话参数
curl_setopt —
为一个curl设置会话参数
curl_version —
获取curl相关的版本信息

curl_init()函数的作用初始化一个curl会话,curl_init()函数唯一的一个参数是可选的,表示一个url地址。
curl_exec()
函数的作用是执行一个curl会话,唯一的参数是curl_init()函数返回的句柄。
curl_close()
函数的作用是关闭一个curl会话,唯一的参数是curl_init()函数返回的句柄。


<?php
$ch = curl_init(http://www.baidu.com/);
curl_exec($ch);
curl_close($ch);
?>

curl_version()函数的作用是获取curl相关的版本信息,curl_version()函数有一个参数,不清楚是做什么的

<?php
print_r(curl_version())
?>

curl_getinfo()函数的作用是获取一个curl连接资源句柄的信息,curl_getinfo()函数有两个参数,第一个参数是curl的资源句柄,第二个参数是下面一些常量:

<?php
$ch = curl_init(http://www.baidu.com/);
print_r(curl_getinfo($ch));
?>

可选的常量包括:

CURLINFO_EFFECTIVE_URL
最后一个有效的url地址

CURLINFO_HTTP_CODE
最后一个收到的HTTP代码

CURLINFO_FILETIME
远程获取文档的时间,如果无法获取,则返回值为“-1”

CURLINFO_TOTAL_TIME
最后一次传输所消耗的时间

CURLINFO_NAMELOOKUP_TIME
名称解析所消耗的时间

CURLINFO_CONNECT_TIME
建立连接所消耗的时间

CURLINFO_PRETRANSFER_TIME
从建立连接到准备传输所使用的时间

CURLINFO_STARTTRANSFER_TIME
从建立连接到传输开始所使用的时间

CURLINFO_REDIRECT_TIME
在事务传输开始前重定向所使用的时间

CURLINFO_SIZE_UPLOAD
上传数据量的总值

CURLINFO_SIZE_DOWNLOAD
下载数据量的总值

CURLINFO_SPEED_DOWNLOAD
平均下载速度

CURLINFO_SPEED_UPLOAD
平均上传速度

CURLINFO_HEADER_SIZE
header部分的大小

CURLINFO_HEADER_OUT
发送请求的字符串

CURLINFO_REQUEST_SIZE
HTTP请求中有问题的请求的大小

CURLINFO_SSL_VERIFYRESULT
Result of SSL certification verification requested by setting CURLOPT_SSL_VERIFYPEER

CURLINFO_CONTENT_LENGTH_DOWNLOAD
Content-Length: field中读取的下载内容长度

CURLINFO_CONTENT_LENGTH_UPLOAD
上传内容大小的说明

CURLINFO_CONTENT_TYPE
下载内容的“Content-type”值,NULL表示服务器没有发送有效的“Content-Type: header”

curl_setopt()函数的作用是为一个curl设置会话参数。curl_setopt_array()函数的作用是以数组的形式为一个curl设置会话参数。

<?php
$ch = curl_init();
$fp = fopen(example_homepage.txt, w);
curl_setopt($ch, CURLOPT_FILE, $fp);
$options = array(
CURLOPT_URL => http://www.baidu.com/,
CURLOPT_HEADER => false
);
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?>

可设置的参数有:

CURLOPT_AUTOREFERER
自动设置header中的referer信息

CURLOPT_BINARYTRANSFER
在启用CURLOPT_RETURNTRANSFER时候将获取数据返回

CURLOPT_COOKIESESSION
启用时curl会仅仅传递一个session cookie,忽略其他的cookie,默认状况下curl会将所有的cookie返回给服务端。session cookie是指那些用来判断服务器端的session是否有效而存在的cookie

CURLOPT_CRLF
启用时将Unix的换行符转换成回车换行符。

CURLOPT_DNS_USE_GLOBAL_CACHE
启用时会启用一个全局的DNS缓存,此项为线程安全的,并且默认为true

CURLOPT_FAILONERROR
显示HTTP状态码,默认行为是忽略编号小于等于400HTTP信息

CURLOPT_FILETIME
启用时会尝试修改远程文档中的信息。结果信息会通过curl_getinfo()函数的CURLINFO_FILETIME选项返回。

CURLOPT_FOLLOWLOCATION
启用时会将服务器服务器返回的“Location:”放在header中递归的返回给服务器,使用CURLOPT_MAXREDIRS可以限定递归返回的数量。

CURLOPT_FORBID_REUSE
在完成交互以后强迫断开连接,不能重用。

CURLOPT_FRESH_CONNECT
强制获取一个新的连接,替代缓存中的连接。

CURLOPT_FTP_USE_EPRT
TRUE to use EPRT (and LPRT) when doing active FTP downloads. Use FALSE to disable EPRT and LPRT and use PORT only.
Added in PHP 5.0.0.

CURLOPT_FTP_USE_EPSV
TRUE to first try an EPSV command for FTP transfers before reverting back to PASV. Set to FALSE to disable EPSV.

CURLOPT_FTPAPPEND
TRUE to append to the remote file instead of overwriting it.

CURLOPT_FTPASCII
An alias of CURLOPT_TRANSFERTEXT. Use that instead.

CURLOPT_FTPLISTONLY
TRUE to only list the names of an FTP directory.

CURLOPT_HEADER
启用时会将头文件的信息作为数据流输出。

CURLOPT_HTTPGET
启用时会设置HTTPmethodGET,因为GET是默认是,所以只在被修改的情况下使用。

CURLOPT_HTTPPROXYTUNNEL
启用时会通过HTTP代理来传输。

CURLOPT_MUTE
curl函数中所有修改过的参数恢复默认值。

CURLOPT_NETRC
在连接建立以后,访问~/.netrc文件获取用户名和密码信息连接远程站点。

CURLOPT_NOBODY
启用时将不对HTML中的body部分进行输出。

CURLOPT_NOPROGRESS
启用时关闭curl传输的进度条,此项的默认设置为true

CURLOPT_NOSIGNAL
启用时忽略所有的curl传递给php进行的信号。在SAPI多线程传输时此项被默认打开。

CURLOPT_POST
启用时会发送一个常规的POST请求,类型为:application/x-www-form-urlencoded,就像表单提交的一样。

CURLOPT_PUT
启用时允许HTTP发送文件,必须同时设置CURLOPT_INFILECURLOPT_INFILESIZE

CURLOPT_RETURNTRANSFER
curl_exec()获取的信息以文件流的形式返回,而不是直接输出。

CURLOPT_SSL_VERIFYPEER
FALSE to stop cURL from verifying the peer’s certificate. Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option. CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2). TRUE by default as of cURL 7.10. Default bundle installed as of cURL 7.10.

CURLOPT_TRANSFERTEXT
TRUE to use ASCII mode for FTP transfers. For LDAP, it retrieves data in plain text instead of HTML. On Windows systems, it will not set STDOUT to binary mode.

CURLOPT_UNRESTRICTED_AUTH
在使用CURLOPT_FOLLOWLOCATION产生的header中的多个locations中持续追加用户名和密码信息,即使域名已发生改变。

CURLOPT_UPLOAD
启用时允许文件传输

CURLOPT_VERBOSE
启用时会汇报所有的信息,存放在STDERR或指定的CURLOPT_STDERR

CURLOPT_BUFFERSIZE
每次获取的数据中读入缓存的大小,这个值每次都会被填满。

CURLOPT_CLOSEPOLICY
不是CURLCLOSEPOLICY_LEAST_RECENTLY_USED就是CURLCLOSEPOLICY_OLDEST,还存在另外三个,但是curl暂时还不支持。.

CURLOPT_CONNECTTIMEOUT
在发起连接前等待的时间,如果设置为0,则不等待。

CURLOPT_DNS_CACHE_TIMEOUT
设置在内存中保存DNS信息的时间,默认为120秒。

CURLOPT_FTPSSLAUTH
The FTP authentication method (when is activated): CURLFTPAUTH_SSL (try SSL first), CURLFTPAUTH_TLS (try TLS first), or CURLFTPAUTH_DEFAULT (let cURL decide).

CURLOPT_HTTP_VERSION
设置curl使用的HTTP协议,CURL_HTTP_VERSION_NONE(让curl自己判断),CURL_HTTP_VERSION_1_0HTTP/1.0),CURL_HTTP_VERSION_1_1HTTP/1.1

CURLOPT_HTTPAUTH
使用的HTTP验证方法,可选的值有:CURLAUTH_BASICCURLAUTH_DIGESTCURLAUTH_GSSNEGOTIATECURLAUTH_NTLMCURLAUTH_ANYCURLAUTH_ANYSAFE,可以使用“|”操作符分隔多个值,curl让服务器选择一个支持最好的值,CURLAUTH_ANY等价于CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLMCURLAUTH_ANYSAFE等价于CURLAUTH_DIGEST | CURLAUTH_GSSNEGOTIATE | CURLAUTH_NTLM

CURLOPT_INFILESIZE
设定上传文件的大小

CURLOPT_LOW_SPEED_LIMIT
当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。

CURLOPT_LOW_SPEED_TIME
The number of seconds the transfer should be below CURLOPT_LOW_SPEED_LIMIT for PHP to consider the transfer too slow and abort.
当传输速度小于CURLOPT_LOW_SPEED_LIMIT时,PHP会根据CURLOPT_LOW_SPEED_TIME来判断是否因太慢而取消传输。

CURLOPT_MAXCONNECTS
允许的最大连接数量,超过是会通过CURLOPT_CLOSEPOLICY决定应该停止哪些连接

CURLOPT_MAXREDIRS
指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的。

CURLOPT_PORT
一个可选的用来指定连接端口的量

CURLOPT_PROXYAUTH
The HTTP authentication method(s) to use for the proxy connection. Use the same bitmasks as described in CURLOPT_HTTPAUTH. For proxy authentication, only CURLAUTH_BASIC and CURLAUTH_NTLM are currently supported.

CURLOPT_PROXYPORT
The port number of the proxy to connect to. This port number can also be set in CURLOPT_PROXY.

CURLOPT_PROXYTYPE
Either CURLPROXY_HTTP (default) or CURLPROXY_SOCKS5.

CURLOPT_RESUME_FROM
在恢复传输时传递一个字节偏移量(用来断点续传)

CURLOPT_SSL_VERIFYHOST
1 to check the existence of a common name in the SSL peer certificate.
2 to check the existence of a common name and also verify that it matches the hostname provided.

CURLOPT_SSLVERSION
The SSL version (2 or 3) to use. By default PHP will try to determine this itself, although in some cases this must be set manually.

CURLOPT_TIMECONDITION
如果在CURLOPT_TIMEVALUE指定的某个时间以后被编辑过,则使用CURL_TIMECOND_IFMODSINCE返回页面,如果没有被修改过,并且CURLOPT_HEADERtrue,则返回一个“304 Not Modified”headerCURLOPT_HEADERfalse,则使用CURL_TIMECOND_ISUNMODSINCE,默认值为CURL_TIMECOND_IFMODSINCE

CURLOPT_TIMEOUT
设置curl允许执行的最长秒数

CURLOPT_TIMEVALUE
设置一个CURLOPT_TIMECONDITION使用的时间戳,在默认状态下使用的是CURL_TIMECOND_IFMODSINCE

CURLOPT_CAINFO
The name of a file holding one or more certificates to verify the peer with. This only makes sense when used in combination with CURLOPT_SSL_VERIFYPEER.

CURLOPT_CAPATH
A directory that holds multiple CA certificates. Use this option alongside CURLOPT_SSL_VERIFYPEER.

CURLOPT_COOKIE
设定HTTP请求中“Set-Cookie:”部分的内容。

CURLOPT_COOKIEFILE
包含cookie信息的文件名称,这个cookie文件可以是Netscape格式或者HTTP风格的header信息。

CURLOPT_COOKIEJAR
连接关闭以后,存放cookie信息的文件名称

CURLOPT_CUSTOMREQUEST
A custom request method to use instead of “GET” or “HEAD” when doing a HTTP request. This is useful for doing “DELETE” or other, more obscure HTTP requests. Valid values are things like “GET”, “POST”, “CONNECT” and so on; i.e. Do not enter a whole HTTP request line here. For instance, entering “GET /index.html HTTP/1.0″r”n”r”n” would be incorrect.
Note: Don’t do this without making sure the server supports the custom request method first.

CURLOPT_EGBSOCKET
Like CURLOPT_RANDOM_FILE, except a filename to an Entropy Gathering Daemon socket.

CURLOPT_ENCODING
header“Accept-Encoding: ”部分的内容,支持的编码格式为:“identity”“deflate”“gzip”。如果设置为空字符串,则表示支持所有的编码格式

CURLOPT_FTPPORT
The value which will be used to get the IP address to use for the FTP “POST” instruction. The “POST” instruction tells the remote server to connect to our specified IP address. The string may be a plain IP address, a hostname, a network interface name (under Unix), or just a plain ‘-‘ to use the systems default IP address.

CURLOPT_INTERFACE
在外部网络接口中使用的名称,可以是一个接口名,IP或者主机名。

CURLOPT_KRB4LEVEL
KRB4(Kerberos 4)安全级别的设置,可以是一下几个值之一:“clear”“safe”“confidential”“private”。默认的值为“private”,设置为null的时候表示禁用KRB4,现在KRB4安全仅能在FTP传输中使用。

CURLOPT_POSTFIELDS
HTTP中的“POST”操作。如果要传送一个文件,需要一个@开头的文件名

CURLOPT_PROXY
设置通过的HTTP代理服务器

CURLOPT_PROXYUSERPWD
连接到代理服务器的,格式为“[username]:[password]”的用户名和密码。

CURLOPT_RANDOM_FILE
设定存放SSL用到的随机数种子的文件名称

CURLOPT_RANGE
设置HTTP传输范围,可以用“X-Y”的形式设置一个传输区间,如果有多个HTTP传输,则使用逗号分隔多个值,形如:“X-Y,N-M”

CURLOPT_REFERER
设置header“Referer: ” 部分的值。

CURLOPT_SSL_CIPHER_LIST
A list of ciphers to use for SSL. For example, RC4-SHA and TLSv1 are valid cipher lists.

CURLOPT_SSLCERT
传递一个包含PEM格式证书的字符串。

CURLOPT_SSLCERTPASSWD
传递一个包含使用CURLOPT_SSLCERT证书必需的密码。

CURLOPT_SSLCERTTYPE
The format of the certificate. Supported formats are “PEM” (default), “DER”, and “ENG”.

CURLOPT_SSLENGINE
The identifier for the crypto engine of the private SSL key specified in CURLOPT_SSLKEY.

CURLOPT_SSLENGINE_DEFAULT
The identifier for the crypto engine used for asymmetric crypto operations.

CURLOPT_SSLKEY
The name of a file containing a private SSL key.

CURLOPT_SSLKEYPASSWD
The secret password needed to use the private SSL key specified in CURLOPT_SSLKEY.
Note: Since this option contains a sensitive password, remember to keep the PHP script it is contained within safe.

CURLOPT_SSLKEYTYPE
The key type of the private SSL key specified in CURLOPT_SSLKEY. Supported key types are “PEM” (default), “DER”, and “ENG”.

CURLOPT_URL
需要获取的URL地址,也可以在PHPcurl_init()函数中设置。

CURLOPT_USERAGENT
HTTP请求中包含一个”user-agent”头的字符串。

CURLOPT_USERPWD
传递一个连接中需要的用户名和密码,格式为:“[username]:[password]”

CURLOPT_HTTP200ALIASES
设置不再以error的形式来处理HTTP 200的响应,格式为一个数组。

CURLOPT_HTTPHEADER
设置一个header中传输内容的数组。

CURLOPT_POSTQUOTE
An array of FTP commands to execute on the server after the FTP request has been performed.

CURLOPT_QUOTE
An array of FTP commands to execute on the server prior to the FTP request.

CURLOPT_FILE
设置输出文件的位置,值是一个资源类型,默认为STDOUT (浏览器)

CURLOPT_INFILE
在上传文件的时候需要读取的文件地址,值是一个资源类型。

CURLOPT_STDERR
设置一个错误输出地址,值是一个资源类型,取代默认的STDERR

CURLOPT_WRITEHEADER
设置header部分内容的写入的文件地址,值是一个资源类型。

CURLOPT_HEADERFUNCTION
设置一个回调函数,这个函数有两个参数,第一个是curl的资源句柄,第二个是输出的header数据。header数据的输出必须依赖这个函数,返回已写入的数据大小。

CURLOPT_PASSWDFUNCTION
设置一个回调函数,有三个参数,第一个是curl的资源句柄,第二个是一个密码提示符,第三个参数是密码长度允许的最大值。返回密码的值。

CURLOPT_READFUNCTION
设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是读取到的数据。数据读取必须依赖这个函数。返回读取数据的大小,比如0或者EOF

CURLOPT_WRITEFUNCTION
设置一个回调函数,有两个参数,第一个是curl的资源句柄,第二个是写入的数据。数据写入必须依赖这个函数。返回精确的已写入数据的大小

curl_copy_handle()函数的作用是拷贝一个curl连接资源的所有内容和参数

<?php
$ch = curl_init(http://www.baidu.com/);
$another = curl_copy_handle($ch);
curl_exec($another);
curl_close($another);
?>

curl_error()函数的作用是返回一个包含当前会话错误信息的字符串。
curl_errno()函数的作用是返回一个包含当前会话错误信息的数字编号。

curl_multi_init()函数的作用是初始化一个curl批处理句柄资源。
curl_multi_add_handle()函数的作用是向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。
curl_multi_exec()函数的作用是解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。
curl_multi_remove_handle()函数表示移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。
curl_multi_close()函数的作用是关闭一个批处理句柄资源。

<?php
$ch1 = curl_init();
$ch2 = curl_init();
curl_setopt($ch1, CURLOPT_URL, http://www.baidu.com/);
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, http://www.google.com/);
curl_setopt($ch2, CURLOPT_HEADER, 0);
$mh = curl_multi_init();
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
do {
curl_multi_exec($mh,$flag);
} while ($flag > 0);
curl_multi_remove_handle($mh,$ch1);
curl_multi_remove_handle($mh,$ch2);
curl_multi_close($mh);
?>

curl_multi_getcontent()函数的作用是在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。

curl_multi_info_read()函数的作用是获取当前解析的curl的相关传输信息。

curl_multi_select()
Get all the sockets associated with the cURL extension, which can then be “selected”

 

 

MySQL常用数据类型和建库策略

1. MYSQL 5数据类型,长度范围

1.1数值类型

MySQL支持所有标准SQL数 值数据类型.这些类型包括严格数值数据类型(INTEGER,SMALLINT,DECIMAL和NUMERIC),以及近似数值数据类型 (FLOAT,REAL和DOUBLE PRECISION).关键字INT是INTEGER的同义词,关键字DEC是DECIMAL的同义词.

BIT 数据类型保存位字段值,并且支持MyISAM,MEMORY,InnoDB和BDB表.作为SQL标准的扩展,MySQL也支持整数类型 TINYINT,MEDIUMINT和BIGINT.下面的表显示了需要的每个整数类型的存储和范围.

MySQL还支持选择在该类型关键字 后面的括号内指定整数值的显示宽度(例如,INT(4)).该可选显示宽度规定用于显示宽度小于指定的列宽度的值时从左侧填满宽度.

显示宽 度并不限制可以在列内保存的值的范围,也不限制超过列的指定宽度的值的显示.

当结合可选扩展属性ZEROFILL使用时, 默认补充的空格用零代替.例如,对于声明为INT(5) ZEROFILL的列,值4检索为00004.请注意如果在整数列保存超过显示宽度的一个值,当MySQL为复杂联接生成临时表时会遇到问题,因为在这些 情况下MySQL相信数据适合原列宽度.

所有整数类型可以有一个可选(非标准)属性UNSIGNED.当你想要在列内只允许非负数和该列需 要较大的上限数值范围时可以使用无符号值.

浮点和定点类型也可以为UNSIGNED.同数类型,该属性防止负值保存到列中.然而,与整数类 型不同的是,列值的上范围保持不变.

如果为一个数值列指定ZEROFILL,MySQL自动为该列添加UNSIGNED属性.

对 于浮点列类型,在MySQL中单精度值使用4个字节,双精度值使用8个字节.

FLOAT类型用于表示近似数值数据类型.SQL标准允许在关 键字FLOAT后面的括号内选择用位指定精度(但不能为指数范围).MySQL还支持可选的只用于确定存储大小的精度规定.0到23的精度对应FLOAT 列的4字节单精度.24到53的精度对应DOUBLE列的8字节双精度.

MySQL允许使用非标准语法:FLOAT(M,D)或 REAL(M,D)或DOUBLE PRECISION(M,D).这里,”(M,D)”表示该值一共显示M位整数,其中D位位于小数点后面.例如,定义为FLOAT(7,4)的一个列可以 显示为-999.9999.MySQL保存值时进行四舍五入,因此如果在FLOAT(7,4)列内插入999.00009,近似结果是999.0001.

MySQL 将DOUBLE视为DOUBLE PRECISION(非标准扩展)的同义词.MySQL还将REAL视为DOUBLE PRECISION(非标准扩展)的同义词,除非SQL服务器模式包括REAL_AS_FLOAT选项.

为了保证最大可能的可移植性,需要 使用近似数值数据值存储的代码应使用FLOAT或DOUBLE PRECISION,不规定精度或位数.

DECIMAL和NUMERIC类 型在MySQL中视为相同的类型.它们用于保存必须为确切精度的值,例如货币数据.当声明该类型的列时,可以(并且通常要)指定精度和标度;例如:

salary DECIMAL(5,2)
在该例子中,5是精度,2是标度.精度表示保存值的主要位数,标度表示小数点后面可以保存的位数.

MySQL 5.1中以二进制格式保存DECIMAL和NUMERIC值.

标准SQL要求salary列能够用5位整数位和两位小数保存任何值.因 此,在这种情况下可以保存在salary列的值的范围是从-999.99到999.99.

在标准SQL中,语法DECIMAL(M)等价于 DECIMAL(M,0).同样,语法DECIMAL等价于DECIMAL(M,0),可以通过计算确定M的值.在MySQL 5.1中支持DECIMAL和NUMERIC数据类型的变量形式.M默认值是10.

DECIMAL或NUMERIC的最大位数是65,但具 体的DECIMAL或NUMERIC列的实际范围受具体列的精度或标度约束.如果此类列分配的值小数点后面的位数超过指定的标度允许的范围,值被转换为该 标度.(具体操作与操作系统有关,但一般结果均被截取到允许的位数).

BIT数据类型可用来保存位字段值.BIT(M)类型允许存储M位 值.M范围为1到64.

要指定位值,可以使用b’value’符.value是一个用0和1编写的二进制值.例如,b’111’和 b’100000000’分别表示7和128.参见9.1.5节,”位字段值”.

如果为BIT(M)列分配的值的长度小于M位,在值的左边 用0填充.例如,为BIT(6)列分配一个值b’101′,其效果与分配b’000101’相同.

当要在一个数值列内保存一个超出该列允许 范围的值时,MySQL的操作取决于此时有效的SQL模式.如果模式未设置,MySQL将值裁剪到范围的相应端点,并保存裁减好的值.但是,如果模式设置 为traditional(“严格模式”),超出范围的值将被拒绝并提示错误,并且根据SQL标准插入会失败.参见5.3.2节,”SQL服务器模式”.

如 果INT列是UNSIGNED,列范围的大小相同,但其端点会变为到0和4294967295.如果你试图保存-9999999999和 9999999999,以非严格模式保存到列中的值是0和4294967296.

如果在浮点或定点列中分配的值超过指定(或默认)精度和标 度规定的范围,MySQL以非严格模式保存表示范围相应端点的值.

当MySQL没有工作在严格模式时,对于ALTER TABLE,LOAD DATA INFILE,UPDATE和多行INSERT语句,由于裁剪发生的转换将报告为警告.当MySQL工作在严格模式时,这些语句将失败,并且部分或全部值 不会插入或更改,取决于是否表为事务表和其它因素.

Type

Bytes

Minimum Value

Maximum Value

(Signed/Unsigned)

(Signed/Unsigned)

TINYINT

1

-128

127

0

255

SMALLINT

2

-32768

32767

0

65535

MEDIUMINT

3

-8388608

8388607

0

16777215

INT

4

-2147483648

2147483647

0

4294967295

BIGINT

8

-9223372036854775808

9223372036854775807

0

18446744073709551615

这 里将数字类型按照分类方法分为三类:整数类,小数类和数字类.

我所谓的”数字类”,就是指 DECIMAL 和 NUMERIC,它们是同一种类型.它严格的说不是一种数字类型,因为他们实际上是将数字以字符串形式保存的;他的值的每一位 (包括小数点) 占一个字节的存储空间,因此这种类型耗费空间比较大.但是它的一个突出的优点是小数的位数固定,在运算中不会”失真”,所以比较适合用于”价格”,”金 额”这样对精度要求不高但准确度要求非常高的字段.

小数类,即浮点数类型,根据精度的不同,有 FLOAT 和 DOUBLE 两种.它们的优势是精确度,FLOAT 可以表示绝对值非常小,小到约 1.17E-38 (0.000…0117,小数点后面有 37 个零) 的小数,而 DOUBLE 更是可以表示绝对值小到约 2.22E-308 (0.000…0222,小数点后面有 307 个零) 的小数.FLOAT 类型和 DOUBLE 类型占用存储空间分别是 4 字节和 8 字节.如果需要用到小数的字段,精度要求不高的,当然用 FLOAT 了.可是说句实在话,我们”民用”的数据,哪有要求精度那么高的呢?这两种类型至今我没有用过――我还没有遇到适合于使用它们的事例.

用 的最多的,最值得精打细算的,是整数类型.从只占一个字节存储空间的 TINYINT 到占 8 个字节的 BIGINT,挑选一个”够用”并且占用存储空间最小的类型是设计数据库时应该考虑的.TINYINT,SMALLINT,MEDIUMINT,INT 和 BIGINT 占用存储空间分别为 1 字节,2 字节,3 字节,4 字节和 8 字节,就无符号的整数而言,这些类型能表示的最大整数分别为 255,65535,16777215,4294967295 和 18446744073709551615.如果用来保存用户的年龄 (举例来说,数据库中保存年龄是不可取的),用 TINYINT 就够了;九城的《纵横》里,各项技能值,用 SMALLINT 也够了;如果要用作一个肯定不会超过 16000000 行的表的 AUTO_INCREMENT 的 IDENTIFY 字段,当然用 MEDIUMINT 不用 INT,试想,每行节约一个字节,16000000 行可以节约 10 兆多.

1.2日期和时 间类型

表示时间值的DATE和时间类型为DATETIME,DATE,TIMESTAMP,TIME和YEAR.每个时间类型有一 个有效值范围和一个”零”值,当指定不合法的MySQL不能表示的值时使用”零”值.TIMESTAMP类型有专有的自动更新特性,将在后面描述.

如 果试图插入一个不合法的日期,MySQL将给出警告或错误.可以使用ALLOW_INVALID_DATES SQL模式让MySQL接受某些日期,例如’1999-11-31′.当你想要保存一个”可能错误的”用户已经在数据库中指定(例如,以web形式)用于 将来处理的值时很有用.在这种模式下,MySQL只验证月范围为从0到12,日范围为从0到31.这些范围可以包括零,因为MySQL允许在DATE或 DATETIME列保存日/月和日是零的日期.这在应用程序需要保存一个你不知道确切日期的生日时非常有用.在这种情况下,只需要将日期保存为 ‘1999-00-00’或’1999-01-00′.如果保存此类日期,DATE_SUB()或DATE_ADD等需要完整日期的函数不会得到正确的结 果.(如果你不想在日期中出现零,可以使用NO_ZERO_IN_DATE SQL模式).

MySQL还允许将’0000-00-00’保 存为”伪日期”(如果不使用NO_ZERO_DATE SQL模式).这在某些情况下比使用NULL值更方便(并且数据和索引占用的空间更小).

sql_mode系统变量设置为相应模式值,可以更确切你想让MySQL支持哪种日期.参见5.3.2节,”SQL服务器模式”.

当使用日 期和时间类型时应记住以下几点:

· MySQL以标准输出格式检索给定日期或时间类型的值,但它尽力解释你指定的各种输入值格式(例如,当你指定一个分配给或与日期或时间类型进行比较的值 时).只支持下面章节中描述的格式.期望你能提供有效值.如果你使用其它格式的值会发生意想不到的结果.

· 包含两位年值的日期会令人模糊,因为世纪不知道.MySQL使用以下规则解释两位年值:

o 70-99范围的年值转换为1970-1999.

o 00-69范围的年值转换为2000-2069.

· 尽管MySQL尝试解释几种格式的值,日期总是以年-月-日顺序(例如,’98-09-04′),而不是其它地方常用的月-日-年或日-月-年顺序(例 如,’09-04-98′,’04-09-98′).

· 如果值用于数值上下文中,MySQL自动将日期或时间类型的值转换为数字,反之亦然.

· 当 MySQL遇到一个日期或时间类型的超出范围或对于该类型不合法的值时(如本节开始所描述),它将该值转换为该类的”零”值.一个例外是超出范围的 TIME值被裁剪到TIME范围的相应端点.

下面的表显示了各类”零”值的格式.请注意如果启用NO_ZERO_DATE SQL模式,使用这些值会产生警告.

Data Type

“Zero” Value

DATETIME

‘0000-00-00 00:00:00’

DATE

‘0000-00-00’

TIMESTAMP

‘0000-00-00 00:00:00’

TIME

’00:00:00′

YEAR

0000

1.3 String类型

字符串类型指CHAR,VARCHAR,BINARY,VARBINARY,BLOB,TEXT,ENUM和 SET.该节描述了这些类型如何工作以及如何在查询中使用这些类型.

CHAR和VARCHAR类型类似,但它们保存和检索的方式不 同.它们的最大长度和是否尾部空格被保留等方面也不同.在存储或检索过程中不进行大小写转换.

CHAR和VARCHAR类型声明的 长度表示你想要保存的最大字符数.例如,CHAR(30)可以占用30个字符.

CHAR列的长度固定为创建表时声明的长度.长度可以为从0 到255的任何值.当保存CHAR值时,在它们的右边填充空格以达到指定的长度.当检索到CHAR值时,尾部的空格被删除掉.在存储或检索过程中不进行大 小写转换.

VARCHAR列中的值为可变长字符串.长度可以指定为0到65,535之间的值.(VARCHAR的最大有效长度由最大行大小 和使用的字符集确定.整体最大长度是65,532字节).

同CHAR对比,VARCHAR值保存时只保存需要的字符数,另加一个字节来记录 长度(如果列声明的长度超过255,则使用两个字节).

VARCHAR值保存时不进行填充.当值保存和检索时尾部的空格仍保留,符合标准 SQL.

如果分配给CHAR或VARCHAR列的值超过列的最大长度,则对值进行裁剪以使其适合.如果被裁掉的字符不是空格,则会产生一条 警告.如果裁剪非空格字符,则会造成错误(而不是警告)并通过使用严格SQL模式禁用值的插入.

下面的表显示了将各种字符串值保存到 CHAR(4)和VARCHAR(4)列后的结果,说明了CHAR和VARCHAR之间的差别:

Value

CHAR(4)

Storage Required

VARCHAR(4)

Storage Required

‘ ‘

4 bytes

1 byte

‘ab’

‘ab ‘

4 bytes

‘ab’

3 bytes

‘abcd’

‘abcd’

4 bytes

‘abcd’

5 bytes

‘abcdefgh’

‘abcd’

4 bytes

‘abcd’

5 bytes

请注意上表中最后一行的值只适用不使用严格模式时;如果MySQL运行在严格 模式,超过列长度不的值不保存,并且会出现错误.

从CHAR(4)和VARCHAR(4)列检索的值并不总是相同,因为检索时从CHAR列 删除了尾部的空格.通过下面的例子说明该差别:

mysql> CREATE TABLE vc (v VARCHAR(4), c CHAR(4));
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO vc VALUES (‘ab ‘, ‘ab ‘);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT CONCAT(v, ‘+’), CONCAT(c, ‘+’) FROM vc;

BINARY和VARBINARY类类似于CHAR和VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串.也就是 说,它们包含字节字符串而不是字符字符串.这说明它们没有字符集,并且排序和比较基于列值字节的数值值.

BINARY和 VARBINARY允许的最大长度一样,如同CHAR和VARCHAR,不同的是BINARY和VARBINARY的长度是字节长度而不是字符长度.

BINARY 和VARBINARY数据类型不同于CHAR BINARY和VARCHAR BINARY数据类型.对于后一种类型,BINARY属性不会将列视为二进制字符串列.相反,它致使使用列字符集的二元 校对规则,并且列自身包含非二进制字符字符串而不是二进制字节字符串.例如CHAR(5) BINARY被视为CHAR(5) CHARACTER SET latin1 COLLATE latin1_bin,假定默认字符集是latin1.这不同于BINARY(5),它保存5字节二进制字符串,没有字符集或 校对规则.

当 保存BINARY值时,在它们右边填充值以达到指定长度.填充值是0x00(零字节).插入值时在右侧添加0x00 on,并且选择时不删除尾部的字节.比较时所有字节很重要,包括ORDER BY和DISTINCT操作.比较时0x00字节和空格是不同的,0x00<空格.

例如:对于一个BINARY(3)列,当插入时 ‘a’ 变为 ‘a ‘.’a插入时变为’a��‘.当选择时两个插入的值均不更改.

对于VARBINARY,插入时不填充字符,选 择时不裁剪字节.比较时所有字节很重要,包括ORDER BY和DISTINCT操作.比较时0x00字节和空格是不同的,0x00<空格.

对 于尾部填充字符被裁剪掉或比较时将它们忽视掉的情形,如果列的索引需要唯一的值,在列内插入一个只是填充字符数不同的值将会造成复制键值错误.

如 果你计划使用这些数据类型来保存二进制数据并且需要检索的值与保存的值完全相同,应考虑前面所述的填充和裁剪特征.下面的例子说明了用0x00填充的 BINARY值如何影响列值比较:

mysql> CREATE TABLE t (c BINARY(3));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO t SET c = ‘a’;
Query OK, 1 row affected (0.01 sec)

mysql> SELECT HEX(c), c = ‘a’, c = ‘a��‘ from t;
+——–+———+————-+
| HEX(c) | c = ‘a’ | c = ‘a
��‘ |
+——–+———+————-+
| 610000 | 0 | 1 |
+——–+———+————-+
1 row in set (0.09 sec)
如果检索的值必须与指定进行存储而没有填充的值相同,最好使用BLOB数据类型.

BLOB是一个二进制 大对象,可以容纳可变数量的数据.有4种BLOB类型:TINYBLOB,BLOB,MEDIUMBLOB和LONGBLOB.它们只是可容纳值的最大长 度不同.

有4种TEXT类型:TINYTEXT,TEXT,MEDIUMTEXT和LONGTEXT.这些对应4种BLOB类型, 有相同的最大长度和存储需求.

BLOB 列被视为二进制字符串(字节字符串).TEXT列被视为非二进制字符串(字符字符串).BLOB列没有字符集,并且排序和比较基于列值字节的数值 值.TEXT列有一个字符集,并且根据字符集的 校对规则对值进行排序和比较.

在TEXT或BLOB列的存储或检索过程中,不存在大小写转 换.

当未运行在严格模式时,如果你为BLOB或TEXT列分配一个超过该列类型的最大长度的值值,值被截取以保证适合.如果截掉的字符不是 空格,将会产生一条警告.使用严格SQL模式,会产生错误,并且值将被拒绝而不是截取并给出警告.

在大多数方面,可以将BLOB列视为能够 足够大的VARBINARY列.同样,可以将TEXT列视为VARCHAR列.BLOB和TEXT在以下几个方面不同于VARBINARY和 VARCHAR:

· 当保存或检索BLOB和TEXT列的值时不删除尾部空格.(这与VARBINARY和VARCHAR列相同).

请 注意比较时将用空格对TEXT进行扩充以适合比较的对象,正如CHAR和VARCHAR.

· 对于BLOB和TEXT列的索引,必须指定索引前缀的长度.对于CHAR和VARCHAR,前缀长度是可选的.

· BLOB和TEXT列不能有 默认值.

LONG和LONG VARCHAR对应MEDIUMTEXT数据类型.这是为了保证兼容性.如果TEXT列类型使用BINARY属性,将为列分配列字符集的二元 校对规则.

MySQL 连接程序/ODBC将BLOB值定义为LONGVARBINARY,将TEXT值定义为LONGVARCHAR.

由于BLOB和TEXT值 可能会非常长,使用它们时可能遇到一些约束:

· 当排序时只使用该列的前max_sort_length个字节.max_sort_length的 默认值是1024;该值可以在启动mysqld服务器时使用–max_sort_length选项进行更改.

运行时增加 max_sort_length的值可以在排序或组合时使更多的字节有意义.任何客户端可以更改其会话max_sort_length变量的值:

mysql> SET max_sort_length = 2000;
mysql> SELECT id, comment FROM tbl_name
-> ORDER BY comment;
当你想要使超过max_sort_length的字节有意义,对含长值 的BLOB或TEXT列使用GROUP BY或ORDER BY的另一种方式是将列值转换为固定长度的对象.标准方法是使用SUBSTRING函数.例如,下面的语句对comment列的2000个字节进行排序:

mysql> SELECT id, SUBSTRING(comment,1,2000) FROM tbl_name
-> ORDER BY SUBSTRING(comment,1,2000);
· BLOB
或TEXT对象的最大大小由其类型确定,但在客户端和服务器之间实际可以传递的最大值由可用内存数量和通信缓存区大小确定.你可以通过更改 max_allowed_packet变量的值更改消息缓存区的大小,但必须同时修改服务器和客户端程序.例如,可以使用 mysql和mysqldump来更改客户端的max_allowed_packet值.参见7.5.2节,”调节服务器参数”,8.3 节,”mysql:MySQL命令行工具”和8.8节,”mysqldump:数据库备份程序”.

每个BLOB或TEXT值分别由内部分配 的对象表示.这与其它列类型形成对比,后者是当打开表时为每1列分配存储引擎.

不要以为字符类型就是 CHAR,CHAR 和 VARCHAR 的区别在于 CHAR 是固定长度,只要你定义一个字段是 CHAR(10),那么不论你存储的数据是否达到了 10 个字节,它都要占去 10 个字节的空间;而 VARCHAR 则是可变长度的,如果一个字段可能的值是不固定长度的,我们只知道它不可能超过 10 个字符,把它定义为 VARCHAR(10) 是最合算的,VARCHAR 类型的占用空间是它的值的实际长度 +1.为什么要 +1 呢?这一个字节用于保存实际使用了多大的长度.从这个 +1 中也应该看到,如果一个字段,它的可能值最长是 10 个字符,而多数情况下也就是用到了 10 个字符时,用 VARCHAR 就不合算了:因为在多数情况下,实际占用空间是 11 个字节,比用 CHAR(10) 还多占用一个字节.

举 个例子,就是一个存储股票名称和代码的表,股票名称绝大部分是四个字的,即 8 个字节;股票代码,上海的是六位数字,深圳的是四位数字.这些都是固定长度的,股票名称当然要用 CHAR(8);股票代码虽然是不固定长度,但如果使用 VARCHAR(6),一个深圳的股票代码实际占用空间是 5 个字节,而一个上海的股票代码要占用 7 个字节!考虑到上海的股票数目比深圳的多,那么用 VARCHAR(6) 就不如 CHAR(6) 合算了.
虽然一个 CHAR 或 VARCHAR 的最大长度可以到 255,我认为大于 20 的 CHAR 是几乎用不到的――很少有大于 20 个字节长度的固定长度的东东吧?不是固定长度的就用 VARCHAR.大于 100 的 VARCHAR 也是几乎用不到的――比这更大的用 TEXT 就好了.TINYTEXT,最大长度为 255,占用空间也是实际长度 +1;TEXT,最大长度 65535,占用空间是实际长度 +2;MEDIUMTEXT,最大长度 16777215,占用空间是实际长度 +3;LONGTEXT,最大长度 4294967295,占用空间是实际长度 +4.为什么 +1,+2,+3,+4?你要是还不知道就该打 PP 了.这些可以用在论坛啊,新闻啊,什么的,用来保存文章的正文.根据实际情况的不同,选择从小到大的不同类型.

ENUM是 一个字符串对象,其值来自表创建时在列规定中显式枚举的一列值.

在某些情况下,ENUM值也可以为空字符串(”)或NULL:

· 如果你将一个非法值插入ENUM(也就是说,允许的值列之外的字符串),将插入空字符串以作为特殊错误值.该字符串与”普通”空字符串不同,该字符串有数 值值0.后面有详细讨论.

· 如果将ENUM列声明为允许NULL,NULL值则为该列的一个有效值,并且 默认值为NULL.如果ENUM列被声明为NOT NULL,其默认值为允许的值列的第1个元素.

每个枚举值有一个索引:

· 来自列规定的允许的值列中的值从1开始编号.

· 空字符串错误值的索引值是0.这说明你可以使用下面的SELECT语句来找出分配了非法ENUM值的行:

· mysql> SELECT * FROM tbl_name WHERE enum_col=0;
· NULL
值的索引是NULL.

例如,定义 为ENUM的列(‘one’,’two’,’three’)可以有下面所示任何值.还显示了每个值的索引:

Value

Index

NULL

NULL

0

‘one’

1

‘two’

2

‘three’

3

枚举最多可以有65,535个元素.

当 创建表时,ENUM成员值的尾部空格将自动被删除.

当检索时,保存在ENUM列的值使用列定义中所使用的大小写来显示.请注意可以为 ENUM列分配字符集和 校对规则.对于二进制或大小写敏感的校对规则,当为列分配值时应考虑大小写.

如果在数值上下文中检索一个ENUM 值,将返回列值的索引.例如,你可以这样从ENUM列搜索数值值:

mysql> SELECT enum_col+0 FROM tbl_name;
如果将一个数字保存到ENUM列,数字被视为索引,并且保存的值是该索引对应的枚举成员.(但是,这不适合LOAD DATA,它将所有输入视为字符串).不建议使用类似数字的枚举值来定义一个ENUM列,因为这很容易引起混淆.例如,下面的列含有字符串值 ‘0’,’1’和’2’的枚举成员,但数值索引值为1,2和3:

numbers ENUM(‘0′,’1′,’2’)
根据枚举成员 在列定义中列出的顺序对ENUM值进行排序.(换句话说,ENUM值根据索引编号进行排序).例如,对于ENUM(‘a’,’b’),’a’排在’b’前 面,但对于ENUM(‘b’,’a’),’b’排在’a’前面.空字符串排在非空字符串前面,并且NULL值排在所有其它枚举值前面.要想防止意想不到的 结果,按字母顺序规定ENUM列.还可以使用GROUP BY CAST(col AS CHAR)或GROUP BY CONCAT(col)来确保按照词汇对列进行排序而不是用索引数字.

如果你想要确定一个ENUM列的所有可能的值,使用SHOW COLUMNS FROM tbl_name LIKE enum_col,并解析输出中第2列的ENUM定义.

2.字段列类型存储 条件说明

根据类别列出了MySQL支持的每个列类型的存储需求.

MyISAM表中行的最大大小为65,534字节. 每个BLOB和TEXT列 账户只占其中的5至9个字节.

如果MyISAM表包括变长列类型,记录格式也是可变长度.当创建表时,在某些条 件下,MySQL可以将一个列从变长类型改为固定长度的类型或反之亦然.

数 值类型存储规则

Data Type

Storage Required

TINYINT

1 byte

SMALLINT

2 bytes

MEDIUMINT

3 bytes

INT, INTEGER

4 bytes

BIGINT

8 bytes

FLOAT(p)

4 bytes if 0 <= p <= 24, 8 bytes if 25 <= p <= 53

FLOAT

4 bytes

DOUBLE [PRECISION], REAL

8 bytes

DECIMAL(M,D), NUMERIC(M,D)

Varies; see following discussion

BIT(M)

approximately (M+7)/8 bytes

DECIMAL(和NUMERIC)的存储需求与具体 版本有关:

使用二进制格式将9个十进制(基于10)数压缩为4个字节来表示DECIMAL列值.每个值的整数和分数部分的存储分别确定.每 个9位数的倍数需要4个字节,并且”剩余的”位需要4个字节的一部分.下表给出了超出位数的存储需求:

Leftover Digits

Number of Bytes

0

0

1

1

2

1

3

2

4

2

5

3

6

3

7

4

8

4

日期和时间类型的存储需求

Data Type

Storage Required

DATE

3 bytes

TIME

3 bytes

DATETIME

8 bytes

TIMESTAMP

4 bytes

YEAR

1 byte

The storage requirements shown in the table arise from the way that MySQL represents temporal values:

DATE: A three-byte integer packed as DD + MM×32 + YYYY×16×32

TIME: A three-byte integer packed as DD×24×3600 + HH×3600 + MM×60 + SS

DATETIME: Eight bytes:

A four-byte integer packed as YYYY×10000 + MM×100 + DD

A four-byte integer packed as HH×10000 + MM×100 + SS

TIMESTAMP: A four-byte integer representing seconds UTC since the epoch (‘1970-01-01 00:00:00’ UTC)

YEAR: A one-byte integer

字符串类型的存储需求

Data Type

Storage Required

CHAR(M)

M × w bytes, 0 <= M <= 255, where w is the number of bytes required for the maximum-length character in the character set

BINARY(M)

M bytes, 0 <= M <= 255

VARCHAR(M), VARBINARY(M)

L + 1 bytes if column values require 0 – 255 bytes, L + 2 bytes if values may require more than 255 bytes

TINYBLOB, TINYTEXT

L + 1 bytes, where L < 28

BLOB, TEXT

L + 2 bytes, where L < 216

MEDIUMBLOB, MEDIUMTEXT

L + 3 bytes, where L < 224

LONGBLOB, LONGTEXT

L + 4 bytes, where L < 232

ENUM(‘value1‘,’value2‘,…)

1 or 2 bytes, depending on the number of enumeration values (65,535 values maximum)

SET(‘value1‘,’value2‘,…)

1, 2, 3, 4, or 8 bytes, depending on the number of set members (64 members maximum)

VARCHAR,BLOB 和TEXT类是变长类型.每个类型的存储需求取决于列值的实际长度(用前面的表中的L表示),而不是该类型的最大可能的大小.例 如,VARCHAR(10)列可以容纳最大长度为10的字符串.实际存储需求是字符串(L)的长度,加上一个记录字符串长度的字节.对于字符串 ‘abcd’,L是4,存储需要5个字节.

对于CHAR,VARCHAR和TEXT类型,前面的表中的值L和M应解释为字符数目,并且列定 义中的这些类型的长度表示字符数目.例如,要想保存一个TINYTEXT值需要L字符+ 1个字节.

要想计算用于保存具体 CHAR,VARCHAR或者TEXT列值的字节数,需要考虑该列使用的字符集.在具体情况中,当使用Unicode时,必须记住所有Unicode字符 使用相同的字节数.为了细分用于不同类Unicode字符使用的存储,

注释:VARCHAR列的有效最大长度为65,532字符

NDBCLUSTER 引擎只支持固定宽度的列.这说明MySQL簇中的表中的VARCHAR列的行为如同类型CHAR(不同的是每个记录仍然有一个额外字节空间).例如,在 Cluster表中,声明为VARCHAR(100)的列中的每个记录存储时将占用101个字节,无论实际存储的记录中的字符串的长度为多少.

BLOB 和TEXT类需要 1,2,3或者4个字节来记录列值的长度,取决于该类的最大可能的长度.

NDB Cluster存储引擎中,TEXT和BLOB列的实施是不同的,其中TEXT列中的每个记录由两个单独部分组成.一个是固定大小(256字节),并且实 际上保存在原表中.另一个包括超出256字节的任何数据,保存在隐含的表中.第2个表中的记录总是2,000字节长.这说明如果size<= 256,TEXT列的大小为256(其中size表示记录的大小);否则,大小是256 +size+(2000–(size–256)%2000).

ENUM 对象的大小由不同的枚举值的数目确定.枚举用一个字节,可以有255个可能的值.当枚举的值位于256和65,535之间时,用两个字节.参见 11.4.4节,”ENUM类型”.

SET对象的大小由不同的set成员的数量确定.如果set大小是N,对象占(N+7)/8个字节,四 舍五入到1,2,3,4或者8个字节.SET最多可以有64个成员.

3.选择正确的列类型

为了优化存储,在任 何情况下均应使用最精确的类型.例如,如果列的值的范围为从1到99999,若使用整数,则MEDIUMINT UNSIGNED是好的类型.在所有可以表示该列值的类型中,该类型使用的存储最少.

用精度为65位十进制数(基于10)对DECIMAL 列进行所有基本计算(+,-,*,/).

使用双精度操作对DECIMAL值进行计算.如果准确度不是太重要或如果速度为最高优先 级,DOUBLE类型即足够了.为了达到高精度,可以转换到保存在BIGINT中的定点类型.这样可以用64位整数进行所有计算,根据需要将结果转换回浮 点值.

4.使用来自其他数据库引擎的列类型

为了使用由其它卖方编写的SQL执行代码,MySQL按 照下表所示对列类型进行映射.通过这些映射,可以很容易地从其它数据库引擎将表定义导入到MySQL中:

Other Vendor Type

MySQL Type

BOOL

TINYINT

BOOLEAN

TINYINT

CHARACTER VARYING(M)

VARCHAR(M)

FIXED

DECIMAL

FLOAT4

FLOAT

FLOAT8

DOUBLE

INT1

TINYINT

INT2

SMALLINT

INT3

MEDIUMINT

INT4

INT

INT8

BIGINT

LONG VARBINARY

MEDIUMBLOB

LONG VARCHAR

MEDIUMTEXT

LONG

MEDIUMTEXT

MIDDLEINT

MEDIUMINT

NUMERIC

DECIMAL

在 创建表时对列类型进行映射,然后原来的类型定义被丢弃.如果你使用其它卖方的类型创建一个表,然后执行DESCRIBE tbl_name语句,MySQL使用等效的MySQL类型来报告表的结构.例如:

mysql> CREATE TABLE t (a BOOL, b FLOAT8, c LONG, d NUMERIC);
Query OK, 0 rows affected (0.08 sec)

mysql> DESCRIBE t;
+——-+—————+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+——-+—————+——+—–+———+——-+
| a | tinyint(1) | YES | | NULL | |
| b | double | YES | | NULL | |
| c | mediumtext | YES | | NULL | |
| d | decimal(10,0) | YES | | NULL | |
+——-+—————+——+—–+———+——-+
4 rows in set (0.00 sec)

更多请参考对应版本的mysql手册阅读

MySQL 5.1参考手册

http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter/

英文版本见官方网站

http://dev.mysql.com/doc/


 

MySQL配置优化

mysql配置优化

网 站访问量越来越大,MySQL自然成为瓶颈,因此最近我一直在研究 MySQL 的优化,第一步自然想到的是 MySQL 系统参数的优化,作为一个访问量很大的网站(日20万人次以上)的数据库系统,不可能指望 MySQL 默认的系统参数能够让 MySQL运行得非常顺畅。

通过在网络上查找资料和自己的尝试,我认为以下系统参数是比较关键的:

(1)、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。

(2)、interactive_timeout:
服务器在关闭它前在一个交互连接上等待行动的秒数。一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户。 默认数值是28800,我把它改为7200。

(3)、key_buffer_size:
索 引块是缓冲的并且被所有的线程共享。key_buffer_size是用于索引块的缓冲区大小,增加它可得到更好处理的索引(对所有读和多重写),到你能 负担得起那样多。如果你使它太大,系统将开始换页并且真的变慢了。默认数值是8388600(8M),我的MySQL主机有2GB内存,所以我把它改为 402649088(400MB)。

(4)、max_connections:
允许的同时客户的数量。增加该值增加 mysqld 要求的文件描述符的数量。这个数字应该增加,否则,你将经常看到 Too many connections 错误。 默认数值是100,我把它改为1024 。

(5)、record_buffer:
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。默认数值是 131072(128K),我把它改为16773120 (16M)

(6)、sort_buffer:
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),我把它改为 16777208 (16M)。
(7)、table_cache:
为所有线程打开表的数量。增加该值能增加mysqld要求的文件描述符的数量。MySQL对每个唯一打开的表需要2个文件描述符。默认数值是64,我把它改为512。

(8)、thread_cache_size:
可 以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以 这个变量值。通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。我把它设置为 80。

(10)、wait_timeout:
服务器在关闭它之前在一个连接上等待行动的秒数。 默认数值是28800,我把它改为7200。

注:参数的调整可以通过修改 /etc/my.cnf 文件并重启 MySQL 实现。这是一个比较谨慎的工作,上面的结果也仅仅是我的一些看法,你可以根据你自己主机的硬件情况(特别是内存大小)进一步修改。

Linux代理服务全攻略——Squid篇

  代理提供两个方面的服务:一是让不能直接访问Internet的用户访问Internet,因为允许一台权限较大的计算机直接连接网络更安全也更易于管理;二是让那些已经能够访问Internet的用户可以更快或更广泛地访问,因为代理服务器可以将访问较为频繁的网页缓存到本地,当有人再次访问同一页面时,代理可以直接发送本地页面而无需浪费网络带宽。当然也可以二者兼而有之。
  
  我们以应用最广泛的Squid为例,讨论 Linux下的代理服务器。这里所指的仅仅是服务器端应用,不包括客户端配置。Squid的优点是功能强大、配置简单、文档丰富;缺点是目前支持的协议尚不够广泛,对超大型应用略感吃力,不过这些不足都在慢慢完善之中。对于初学者和普通的应用而言,Squid仍是最佳选择。
  
  在此,我们要配置一个只对内部网络提供代理服务的Proxy Server。它将用户分为高级用户和普通用户两种,对高级用户采用网卡物理地址识别的方法,普通用户则需要输入用户名和口令才能正常使用。高级用户没有访问时间和文件类型的限制,而普通用户只在上班时可以访问以及一些其它的限制。
  
  安装
  可以从Squid 站点www.squid-cache.org获取该软件的源代码安装包,包括gz和bz2两种压缩方式。也可以使用Linux的发行版,如Red Hat提供的RPM包。
  
  RPM方式安装很简单,命令如下:
  
  $ rpm -ivh Squid-2.x.STALBx.i386.rpm
  
  不过笔者认为,即便是系统中已经默认安装了Squid,也应当先删掉然后安装最新的源代码包。因为开源软件会不断修正问题、提供更新的功能,使用最新版本可以保证最高的性能及安全,而且源代码方式可以完全定制系统。不过 STABLE稳定版、DEVEL版通常是提供给开发人员测试程序的,假定下载了最新的稳定版squid-2.5.STABLE2.tar.gz,用以下命令解开压缩包:
  
  $ tar xvfz squid-2.5.STABLE.tar.gz
  
  用bz2方式压缩的包可能体积更小,相应的命令是:
  
  $ tar xvfj squid-2.5.STABLE.tar.bz2
  
  然后,进入相应目录对源代码进行配置和编译,命令如下:
  
  $ cd squid-2.5.STABLE2
  
  配置命令configure有很多选项,如果不清楚可先用“-help”查看。通常情况下,用到的选项有以下几个:
  
   –prefix=/web/squid
  #指定Squid的安装位置,如果只指定这一选项,那么该目录下会有bin、sbin、man、 conf等目录,而主要的配置文件此时在conf子目录中。为便于管理,最好用参数–sysconfdir=/etc把这个文件位置配置为/etc。
  –enable-storeio=ufs,null
  #使用的文件系统通常是默认的ufs,不过如果想要做一个不缓存任何文件的代理服务器,就需要加上null文件系统。
  –enable-arp-acl
  #这样可以在规则设置中直接通过客户端的MAC地址进行管理,防止客户使用IP欺骗。
  –enable-err-languages=”Simplify_Chinese”
   –enable-default-err-languages=”Simplify_Chinese”
  #上面两个选项告诉Squid编入并使用简体中文错误信息。
  –enable-linux-netfilter
  #允许使用Linux的透明代理功能。
   –enable-underscore
  #允许解析的URL中出现下划线,因为默认情况下Squid会认为带下划线的URL是非法的,并拒绝访问该地址。
  
  整个配置编译过程如下:
  
  ./configure –prefix=/var/squid
  –sysconfdir=/etc
  –enable-arp-acl
  –enable-linux-netfilter
  –enable-pthreads
  –enable-err-language=”Simplify_Chinese”
   –enable-storeio=ufs,null
   –enable-default-err-language=”Simplify_Chinese”
   –enable-auth=”basic”
  –enable-baisc-auth-helpers=”NCSA”
   –enable-underscore
  
  其中一些选项有特殊作用,将在下面介绍它们。
  
  最后执行make 和make install两条命令,将源代码编译为可执行文件,并拷贝到指定位置。
  
  基本配置
  安装完成后,接下来要对Squid的运行进行配置(不是前面安装时的配置)。所有项目都在squid.conf中完成。Squid自带的squid.conf包括非常详尽的说明,相当于一篇用户手册,对配置有任何疑问都可以参照解决。
  
  在这个例子中,代理服务器同时也是网关,内部网络接口 eth0的IP地址为192.168.0.1,外部网络接口eth1的IP地址为202.103.x.x。下面是一个基本的代理所需要配置选项:
  
  http_port 192.168.0.1:3128
  
  默认端口是3128,当然也可以是任何其它端口,只要不与其它服务发生冲突即可。为了安全起见,在前面加上IP地址,Squid就不会监听外部的网络接口。
  
  下面的配置选项是服务器管理者的电子邮件,当错误发生时,该地址会显示在错误页面上,便于用户联系:
  
  cache_mgr netsnake@963.net
  
  以下这些参数告诉Squid缓存的文件系统、位置和缓存策略:
  
  cache_dir ufs /var/squid
  cache_mem 32MB
  cache_swap_low 90
  cache_swap_high 95
  
  在这里,Squid会将/var/squid目录作为保存缓存数据的目录,每次处理的缓存大小是32兆字节,当缓存空间使用达到95%时,新的内容将取代旧的而不直接添加到目录中,直到空间又下降到90%才停止这一活动。如果不想Squid缓存任何文件,如某些存储空间有限的专有系统,可以使用null 文件系统(这样不需要那些缓存策略):
  
  cache_dir null /tmp
  
  下面的几个关于缓存的策略配置中,较主要的是第一行,即用户的访问记录,可以通过分析它来了解所有用户访问的详尽地址:
  
   cache_access_log /var/squid/access.log
  cache_log /var/squid/cache.log
  cache_store_log /var/squid/store.log
  
  下面这行配置是在较新版本中出现的参数,告诉Squid在错误页面中显示的服务器名称:
  
  visible_hostname No1.proxy
  
  以下配置告诉Squid如何处理用户,对每个请求的IP地址作为单独地址处理:
  
   client_mask 255.255.255.255
  
  如果是普通代理服务器,以上的配置已经足够。但是很多Squid都被用来做透明代理。所谓透明代理,就是客户端不知道有代理服务器的存在,当然也不需要进行任何与代理有关的设置,从而大大方便了系统管理员。相关的选项有以下几个:
  
  httpd_accel_host virtual
  httpd_accel_port 80
   httpd_accel_with_proxy on
  httpd_accel_user_host_header on
  
  在Linux上,可以用iptables/ipchains直接将对Web端口80的请求直接转发到Squid端口3128,由Squid接手,而用户浏览器仍然认为它访问的是对方的80端口。例如以下这条命令:
  
  iptables -t nat -A PREROUTING -s 192.168.0.200/32 -p tcp –dport 80 -j REDIRECT 3128
  
  就是将 192.168.0.200的所有针对80端口的访问重定向到3128端口。
  
  所有设置完成后,关键且重要的任务是访问控制。 Squid支持的管理方式很多,使用起来也非常简单(这也是有人宁愿使用不做任何缓存的Squid,也不愿意单独使用iptables的原因)。 Squid可以通过IP地址、主机名、MAC地址、用户/密码认证等识别用户,也可以通过域名、域后缀、文件类型、IP地址、端口、URL匹配等控制用户的访问,还可以使用时间区间对用户进行管理,所以访问控制是Squid配置中的重点。Squid用ACL(Access Control List,访问控制列表)对访问类型进行划分,用http_access deny 或allow进行控制。根据需求首先定义两组用户advance和normal,还有代表所有未指明的用户组all及不允许上网的baduser,配置代码如下:
  
  acl advance 192.168.0.2-192.168.0.10/32
  acl normal src 192.168.0.11-192.168.0.200/32
  acl baduser src 192.168.0.100/32
  acl baddst dst www.somebadsite.com
  acl all src 0.0.0.0/0
  
  http_access deny baduser
  http_access allow advance
   http_access allow normal
  
  可以看出,ACL的基本格式如下:
  
  acl 列表名称 控制方式 控制目标
  
  比如acl all src 0.0.0.0/0,其名称是all,控制方式是src源IP地址,控制目标是0.0.0.0/0的IP地址,即所有未定义的用户。出于安全考虑,总是在最后禁止这个列表。
  
  下面这个列表代表高级用户,包括IP地址从192.168.0.2到192.168.0.10的所有计算机:
  
  acl advance 192.168.0.2-192.168.0.20/32
  
  下面这个baduser列表只包含一台计算机,其IP地址是192.168.0.100:
  
  acl baduser 192.168.0.100/32
  
  ACL写完后,接下来要对它们分别进行管理,代码如下:
  
  http_access deny baduser
  http_access allow advance
  http_access allow normal
  
  上面几行代码告诉Squid不允许baduser组访问Internet,但advance、normal组允许(此时还没有指定详细的权限)。由于Squid是按照顺序读取规则,会首先禁止baduser,然后允许normal。如果将两条规则顺序颠倒,由于baduser在normal范围中,Squid先允许了所有的normal,那么再禁止baduser就不会起作用。
  
  特别要注意的是,Squid将使用allow-deny- allow-deny……这样的顺序套用规则。例如,当一个用户访问代理服务器时,Squid会顺序测试Squid中定义的所有规则列表,当所有规则都不匹配时,Squid会使用与最后一条相反的规则。就像上面这个例子,假设有一个用户的IP地址是192.168.0.201,他试图通过这台代理服务器访问Internet,会发生什么情况呢?我们会发现,他能够正常访问,因为Squid找遍所有访问列表也没有和192.168.0.201有关的定义,便开始应用规则,而最后一条是deny,那么Squid默认的下一条处理规则是allow,所以192.168.0.201反而能够访问Internet 了,这显然不是我们希望的。所以在所有squid.conf中,最后一条规则永远是http_access deny all,而all就是前面定义的“src 0.0.0.0”。
  
  高级控制
  前面说过,Squid的控制功能非常强大,只要理解Squid的行为方式,基本上就能够满足所有的控制要求。下面就一步一步来了解Squid是如何进行控制管理的。
  
  通过IP地址来识别用户很不可靠,比IP地址更好的是网卡的MAC物理地址。要在Squid中使用MAC地址识别,必须在编译时加上 “–enable-arp-acl”选项,然后可以通过以下的语句来识别用户:
  
  acl advance arp 00:01:02:1f:2c:3e 00:01:02:3c:1a:8b …
  
  它直接使用用户的MAC地址,而MAC地址一般是不易修改的,即使有普通用户将自己的IP地址改为高级用户也无法通过,所以这种方式比IP地址可靠得多。
  
  假如不想让用户访问某个网站应该怎么做呢?可以分为两种情况:一种是不允许访问某个站点的某个主机,比如sinapage的主机是 sinapage4.sina.com.cn,而其它的新浪资源却是允许访问的,那么ACL可以这样写:
  
  acl  sinapage dstdomain sinapage4.sina.com.cn
  … …
  http_access deny sinapage
  … …
  
  由此可以看到,除了sinapage4,其它如 www.sina.com.cn、news.sina.com.cn都可以正常访问。
  
  另一种情况是整个网站都不许访问,那么只需要写出这个网站共有的域名即可,配置如下:
  
  acl qq dstdomain .tencent.com.cn
  
  注意tencent前面的“.”,正是它指出以此域名结尾的所有主机都不可访问,否则就只有tencent.com.cn这一台主机不能访问。
  
  如果想禁止对某个IP地址的访问,如202.108.0.182,可以用dst来控制,代码如下:
  
  acl badaddr dst 202.108.0.182
  
  当然,这个dst也可以是域名,由Squid查询DNS服务器将其转换为 IP。
  
  还有一种比较广泛的控制是文件类型。如果不希望普通用户通过代理服务器下载MP3、AVI等文件,完全可以对他们进行限制,代码如下:
  
  acl mmxfile urlpath_regex \.mp3$ \.avi$ \.exe$
   http_access deny mmxfile
  
  看到regex,很多读者应该心领神会,因为这条语句使用了标准的规则表达式(又叫正则表达式)。它将匹配所有以.mp3、.avi等结尾的URL请求,还可以用-i参数忽略大小写,例如以下代码:
  
  acl mmxfile urlpath_regex -i \.mp3$
  
  这样,无论是.mp3还是.MP3都会被拒绝。当然,-i参数适用于任何可能需要区分大小写的地方,如前面的域名控制。
  
  如果想让普通用户只在上班时间可以上网,而且是每周的工作日,用 Squid应当如何处理呢?看看下面的ACL定义:
  
  acl worktime time MTWHF 8:30-12:00 14:00-18:00
  http_access deny !worktime
  
  首先定义允许上网的时间是每周工作日(星期一至星期五)的上午和下午的固定时段,然后用http_access 定义所有不在这个时间段内的请求都是不允许的。
  
  或者为了保证高级用户的带宽,希望每个用户的并发连接不能太多,以免影响他人,也可以通过Squid控制,代码如下:
  
  acl conncount maxconn 3
  http_access deny conncount normal
   http_access allow normal
  
  这样,普通用户在某个固定时刻只能同时发起三个连接,从第四个开始,连接将被拒绝。
  
  总之,Squid的ACL配置非常灵活、强大,更多的控制方式可以参考squid.conf.default。
  
  认证
  用户/密码认证为Squid管理提供了更多便利,最常用的认证方式是NCSA。从Squid 2.5版本开始,NCSA认证包含在了basic中,而非以前单独的认证模块。下面来看看实现认证的具体操作。
  
  首先在编译时配置选项应包括以下配置:
  
  –enable-auth=”basic” –enable-basic-auth-helpers=”NCSA”
  
  “make install”以后,需要将“helpers/basic_auth/NCSA/ncsa_auth”拷贝到用户可执行目录中,如/usr/bin(如果在该目录中找不到这个执行文件,在编译时请使用make all而不是make,或者直接在该目录中执行make),然后需要借助Apache的密码管理程序htpasswd来生成用户名/密码对应的文件,就像下面这行代码:
  
  htpasswd -c /var/squid/etc/password guest
  
  在输入两遍guest用户的密码后,一个guest用户就生成了。如果以后需要添加用户,把上面的命令去掉-c参数再运行即可。
  
   Squid 2.5在认证处理上有了较大的改变,这里就只讨论2.5版本的处理方法,2.4及以下版本请参考squid.conf.default。在2.5版的 squid.conf中,包括以下几个相关选项:
  
  #该选项指出了认证方式(basic)、需要的程序(ncsa_auth)和
  对应的密码文件(password)
  auth_param basic program /usr/bin/ncsa_auth /var/squid/etc/password
  # 指定认证程序的进程数
  auth_param basic children 5
  # 浏览器显示输入用户/密码对话框时的领域内容
  auth_param basic realm My Proxy Caching Domain
  # 基本的认证有效时间
  auth_param basic credentialsttl 2 hours
  # 普通用户需要通过认证才能访问Internet
  acl normal proxy_auth REQUIRED
  http_access allow normal
  
  通过以上的配置即可完成认证工作。有的读者可能要问:认证只针对普通用户,而高级用户是直接上网的,该怎么处理呢?其实,这两种用户是可以共存的。如前所述,Squid是顺序处理http_access的,所以在 http_access处理过程中,如果先处理normal用户,那么当前用户无论是否属于高级用户,都会被要求进行认证;相反如果先处理高级用户,剩下的就只有需要认证的普通用户了。例如以下配置代码:
  …
  http_access allow normal (需要认证)
  http_access allow advance (不需要认证)
  …
  
  不管是否为noauth用户,都要求进行用户名/密码验证。正确的方法是将二者位置交换,代码如下:
  …
  http_access allow advance
  http_access allow normal
  …
  
  这时,高级用户不会受到任何影响。
  
  总结
  下面把整个squid.conf总结一下:
  
  # 服务器配置
  http_port 192.168.0.1:3128
  cache_mgr netsnake@963.net
  cache_dir null /tmp
  cache_access_log /var/squid/access.log
  cache_log /var/squid/cache.log
  cache_store_log /var/squid/store.log
   visible_hostname No1.proxy
  client_mask 255.255.255.255
   httpd_accel_host virtual
  httpd_accel_port 80
   httpd_accel_with_proxy on
  httpd_accel_user_host_header on
  
  # 用户分类
  acl advance arp 00:01:02:1f:2c:3e 00:01:02:3c:1a:8b …
  acl normal proxy_auth REQUIED
  acl all src 0.0.0.0
  
  # 行为分类
  acl mmxfile urlpath_regex \.mp3$ \.avi$ \.exe$
  acl conncount maxconn 3
  acl worktime time MTWHF 8:30-12:00 14:00-18:00
  acl sinapage dstdomain sinapage4.sina.com.cn
  acl qq dstdomain .tencent.com.cn
  
  # 处理
  http_access allow advance
   http_access deny conncount normal
  http_access deny !worktime
   http_access deny mmxfile
  http_access deny sinapage
  http_access deny qq
  http_access allow normal
  
  配置后的状况是,advance组可以不受任何限制地访问Internet,而normal组则只能在工作时间上网,而且不能下载多媒体文件,不能访问某些特定的站点,而且发送请求不能超过3个。
  
  通过本文的介绍,它可以了解Squid的基本能力。当然,它的能力远不止此,可以建立强大的代理服务器阵列,可以帮助本地的Web服务器提高性能,可以提高本地网络的安全性等。要想发挥它的功效,还需要进一步控制。