常用的web服务器都支持一定的并发用户数,这个并发用户数可以通过配置文件参数来进行配置

前言:最近看了《构建高性能web站点》一书,觉得很有收获。这本书涉及了高性能web站点设计的方方面面,作为一本入门指南级的书籍非常的不错。这篇文章尝试对该书做一个摘要,便于查阅。 

  对于web服务器,不断得挖掘潜力和优化服务是工程师的一直孜孜不倦的目标。优化的方法和思路其实很简单,分析应用的瓶颈在哪里?然后用合适的策略来解决它。下面就分析一下服务器可能会出现什么问题?如何解决这些问题,解决这些问题的关键技术是哪些?有些什么样的结论? 

   1如何提高web服务器并发处理能力 

  
对于一台服务器,我们希望希望它在单位时间内能够处理的连接数越多越好,吞吐量越大越好。要提高web服务器吞吐量,其实要考虑这么几个因素: 

   并发连接数 

  
常用的web服务器都支持一定的并发用户数,这个并发用户数可以通过配置文件参数来进行配置。比如apache可以通过MaxClients来配置,tomcat也可以通过maxThreads来配置。这些参数用来设定服务器最多可能支持的连接数有多少。这个连接数并不是越大越好,因为连接数是有代价的,连接数越大,每个连接的平均响应时间就越慢。所谓最大连接数,指的是一个可接受的平均响应时间内的最大连接数,如果连接数再大,那么每个连接的平均响应时间就超过了可以接受的范围,这个连接数就没有意义了。我们利用ab这样的测试工具,来测试网页在一定并发数下n的响应时间,我们不断提高n使得响应时间达到我们的阈值,此时的n就是该系统所支持的最大连接数或并发用户数。一旦并发用户超过这个限制,我们就需要考虑扩展这台服务器,单台服务器已经到达极限了。 

   为web服务器选择合适的并发机制 

  
Web服务器能够并发得处理多个连接,其根本的原因在于web服务器利用操作系统的并行机制并发得处理来自客户端的并发请求。但Web服务会采用不同的并行机制,比如进程(进程也会有不同的模式,比如为每个连接fork一个进程,又比如预先fork一些进程,每个进程竞争得维护多个连接),轻量级进程,或者线程。这些并发机制会导致服务器性能有差异。因此尽量避免采用多进程模型,尽可能采取一些配置措施来采用线程模型或者选择采用线程模型的服务器(比如舍弃apache,采用Lightd)来减少上下文切换时的开销。 

   避免锁竞争 

  
共享资源需要通过加锁来访问,因此共享资源也会限制服务器吞吐量,尽可能得减小并发请求对共享资源的瓶颈效益。比如,关闭web服务器访问日志(访问日志需要锁来同步) 

   减少系统调用 

  
Web服务器涉及很多系统调用,系统调用会导致操作系统在用户态和内核态不断切换导致额外开销。因此尽可能减少web服务器的系统调用。比如关闭.htaccess来减少open()系统调用,比如关闭mod_status来减少gettimeofday()这样的系统调用。 

   内存分配策略 

  
内存分配策略和效率也会对web服务器的并发性能也会有影响。Apache基于多进程模型,内存消耗比较大,内存分配效率相对会比较低。一些基于单进程多线程模型的服务器比如ligd,内存消耗小,内存分配效率相对比较高,ngigx除了单进程之外,其优化的内存分配模型也很给力,因此其并发性能非常优异。 

   持久连接 

  
浏览器请求一次数据后就会关闭和服务器的连接。下次请求数据时还需要重复花费时间与服务器建立连接。一个可行的方法就是长连接,客户端请求完数据后,连接并不断开,服务器继续维护这个连接,等待下次客户端再次请求服务器时复用这个连接,避免建立连接的重复开销。通过配置可以打开apache的长连接。维持长连接会占用服务器的cpu资源,因此服务需要权衡是重复建立连接的开销大,还是维持长连接需要的开销大。大多数情况下,重复建立连接的开销是比较大的。 

   内存映射 

  
普遍认为,通过mmap来进行磁盘文件的访问优于通过open()系统调用来访问磁盘文件。因此在编写web服务器时,尽可能得使用mmap()。 

   直接IO 

  
访问磁盘文件,Linux需要对数据进行两次复制,第一次是从磁盘复制到内核缓冲区,第二次是从内核缓冲区复制到用户态内存空间。内核缓冲区实际上是磁盘文件的cache。对于一些复杂的应用,用户可能希望绕过内核缓冲区,自己来管理cache,比如实现更加有针对性的cache机制,实现更有针对性的查询,从而实现更高效的IO访问提高吞吐量。此时可以通过open()系统调用用O_DIRECT参数加以控制。 

   Sendfile 

  
前面讲到,对磁盘文件的访问需要经过内核态到用户态的转移。对于静态文件而言,数据从内核缓冲区复制到用户态,由于静态文件无需任何处理,于是又从用户态写入到内核缓冲区。这一来一回,数据从内核态到用户态的转移完全没有必要,因此通过Sendfile就避免了这种静态文件没有必要的内核态用户态切换。这对编写web服务器很有意义。 

   选择合适的服务器并发策略 

  
不同的web服务器有不同的服务器并发策略,有的web服务器甚至有多种并发策略可供配置。这就需要我们根据实际情况来选择。并发策略有这么几种: 

  第一, 一个进程处理一个连接,非阻塞IO 

  这种方式早期的处理方式是,主进程通过accept来监听本地连接,一旦收到客户端连接描述符,主进程马上fork一个worker子进程通过非阻塞IO轮询接受数据进行处理。这种方式称之为fork模式,fork成为影响性能的关键。 

  另一种方式是prefork模式,这种方式是主进程预先创立一定数量的进程,每个请求由一个子进程来处理,子进程通过非阻塞IO轮询来获得数据,每个子进程可以处理多个请求。主进程此时就相当于维护了一个进程池。对于accept的处理方式,有两种策略: 

  1) 主进程使用非阻塞accept()来监听本地连接,当客户端描述符就绪后,主进程将任务分配给空闲的子进程 

  2) 主进程不监听任何连接,而是由所有的子进程通过阻塞的accept()来竞争接受连接,由竞争成功的子进程来获得连接,处理任务。 

  Apache2的prefork采用的是2)accept策略。Apache采用的这种并发策略基于进程并发机制,进程内存开销大,上下文切换开销也大,效率相对较低,吞吐量往往受限。 

  第二, 一个线程处理一个连接,非阻塞IO 

  Apache的worker模型就是采用这种模型,这种模型基于前面的prefork模式,由主进程预先生成一些子进程,每个子进程拥有一定的子线程。主进程负责分配一个子进程来负责accept监听本地连接,一旦获得客户端连接描述符,该子进程再分配一个子线程用非阻塞IO轮询处理数据。 

  实际的测试表明,这种并发策略与前面第一种并发策略相比,优势并不明显,这可能是由于Linux内核线程的实现方式有关。因此worker模型并不实用。 

  第三, 一个进程处理多个连接,非阻塞IO 

  该方法利用多路IO就绪通知来达到一个进程处理多个连接的目的,所谓多路IO就绪通知实际上是一种异步IO通知的方式,当IO文件描述符准备好,通过callback在进程中获得准备好的文件描述符,再利用准备好的文件描述符调用accept监听端口连接,一旦客户端连接描述符就绪,就处理数据。这个处理的进程就似乎worker进程。一旦又有新的客户端连接上来,该进程又继续利用准备好的文件描述符调用accept监听本地端口,继续处理。这种方式和和前两种的区别在于,其一,前两种模式的进程或线程是利用accept一直不停得监听本地端口连接中的所有文件描述符,它不知道何时会有客户端连接,一旦获得就绪的客户端连接描述符后,他们分配给单独的进程或线程处理数据。而这种模式是利用异步IO通知的方式监本地IO端口,一旦异步通知发生,就意味着有客户端连接发生,接着才调用accept监听IO连接获得就绪的客户端描述符,此时的accept就是一个同步过程会马上返回,因为多路IO就绪通知的发生就意味着有客户端连接产生。一个是一直在查询等待IO设备中的文件描述符就绪,一个是等内核通知来了再查询。由此可知,这种模式下多路IO就绪通知就是系统吞吐量的关键,是内核实现的好,还是由用户一直不断查询的好?其二,本质上这种模式相当于一个进程通过异步通知的方式源源不断顺序得从IO就绪队列中取客户端文件描述符,顺序得取数据,处理数据。而前两种模式本质上也是通过轮询的方式顺序得取客户端描述符,但取数据和处理数据是在多个进程或线程中并发得进行。 

  Nginx和ligd就可以工作在这种模式下。在一些web服务器中,多路IO就绪通知的实现也有不同,比如ligd就可以设置三种多路IO就绪通知的实现方式,可以利用select,poll或epoll三种系统调用来实现。经过试验证明,epoll是最优的选择,在有大量空闲连接的情况下,它有很高的吞吐量。另外,请控制worker进程的数量,进程数目越多吞吐量是会越高,但响应速度会越慢,你需要权衡。 

  在多cpu的服务器上,个人觉得工作方式1,2比较合理,因为并行的进程或线程可以被分配到不同的cpu上。但在单cpu服务器上,工作方式3由于节省了进程切换开销,加上异步IO就绪通知的高效实现,它反而比较高效。 

  结论:提高web服务器并发能力,这其实是由你选择的web服务器所决定的,你所能做的可能并不多。但你需要了解web服务器内部实现的机制和影响因素,便于你进行设置上的调整或者更换未必服务器。 

   2动态页面内容缓存 

  
对于动态页面,很多信息需要从数据库查询,数据库查询是比较耗时的,能加快么?能,把动态页面内容缓存起来,放在磁盘或内存里。 

   2.1页面缓存 

  Smarty磁盘缓存 

  
Smarty是以缓存框架,它可以把页面内容存放在磁盘空间里。所谓缓存实际上就是一个HashMap,此时key可以是任何id,比如说是带参数的url,value就是页面html内容。在php页面中的脚本需要先判断缓存中是否有该url的页面,如果确实则从数据库中读取,并放到缓存中,否则直接从缓存中读取。磁盘中缓存可以支持按照url生成目录分级,避免大量缓存文件都在一个文件夹中不便于文件系统的索引和管理。动态内容由于更新频率很快,注意设置缓存中选项的过期时间,从而达到页面的定时更新。 

   实现自己的磁盘缓存 

  
Smarty缓存的使用需要加载一些php库之类的,有些功能并不需要,用户可以开发自己的轻量级缓存机制。 

   内存缓存 

  
把缓存放在内存中去这是很自然的想法,原理是一样的,内存中有一个key/value的查找数据结构。Php有APC缓存,Xcache缓存等插件可以实现这样的功能。 

   专用的缓存服务器 

  
前面的方法都是把缓存机制放在web服务器上,这必然会抢占web服务器资源。更激进一点,把内存缓存放在专用的服务器上,这些专用服务器的内存我们可以设置得大一些,cpu可以搞慢一些,硬盘小一些,而且便于系统的扩展。Memcached就是构建专用缓存服务器的利器,高效的key/value查询,灵活得过期时间设置。Memcached最大的优点是能够实现分布式缓存扩展。 

   有效期机制 

  
除了设置缓存过期时间,还可以在数据库发生更新的时候程序强行清理缓存,以达到动态实时更新的效果。 

   局部无缓存 

  
动态页面往往是只有一部分内容是动态更新的,大部分内容往往是静态的,可以设置部分内容不予缓存,其它的部分进行缓存,然后将两部分合并起来。Smarty框架,cakephp框架通过在动态内容上加上特定的标签来支持这种功能。 

   2.2动态内容静态化 

  
再激进一点,前面的缓存机制都依赖于php或jsp动态脚本的控制,这会使部分的计算资源花在缓存控制上,能不能把这部分时间省下来,我干脆把所有的动态页面定期缓存到磁盘上形成静态页面,然后让用户直接访问这些静态shtml文件。这是一个崭新的建站思路,通过CMS系统我们可以实现静态网页的生成。这个思路可以达到页面访问速度的最大化。 

   更新策略 

  
如何更新这些静态网页: 

  1) 数据更新时重新生成静态化内容 

  2) 数据更新时写入队列,等队列满时出发重新更新 3) 定时重新生成新的静态化内容 

   局部更新 

  
利用SSI(服务器端包含技术,在主流的web服务器中都支持),一个大的静态页面由多个不同的子静态页面组合而成,因此CMS更新页面时只要更新部分子页面就可以使整个静态页面得到更新,从而节省重建静态网页的计算开销和磁盘IO。但是打开SSI支持会导致磁盘IO读写增加,相比而言,吞吐量低于不带SSI的纯静态页面。一个静态页面没有使用SSI,如果打开SSI支持,会导致额外开销。一个做法可以使SSI扫描只针对哪些用了SSI的页面,对于其他页面关闭SSI扫描,可以通过XBithack或后缀名来进行SSI页面的区分。 

  结论:对于动态页面,可以通过磁盘缓存,内存缓存构造多级缓存体系来加速页面装载。动态页面通过CMS静态化也是一个加速的方法。 

   3动态脚本加速 

  
对于php这样的动态脚本的使用在一些场合下可能是无法避免的,那么我们接下来就需要考虑如何加速动态脚本的执行。 

   3.1 opcode缓存 

  
Php这样的语言在运行之前需要被编译成中间代码,这就是opcode。Opcode的编译生成需要CPU开销,如果能够把opcode缓存在web服务器的内存内,那么就可以大大减少opcode的编译开销,提高吞吐量。APC和Xcache都支持opcode缓存,只需要配置一下即可。由于动态代码变化不会很频繁,可以通过关闭过期检查来进一步提高效率。 

   3.2扩展模块加载 

  
避免无用模块的加载,如果一些模块确定不会用到,就不要包含在头文件中,提高吞吐量。 

   3.3 debug工具 

  
通过调试工具或插件来分析动态脚本各个函数的性能瓶颈,并加以优化。 

   4浏览器缓存 

  
用户浏览器是可以缓存数据的,把页面设计得对浏览器缓存友好可以大大改善体验。 

   4.1缓存协商 

  
通过在头中加入一些字段,可以在协商中通知浏览器缓存一些数据,使得服务器对缓存友好。 

   Last-Modified 

  
在第一次回应的头中加入Last-Modified字段可以显式命令浏览器使用浏览器缓存,浏览器此后再请求该页面时就会询问web服务器该页面在某一时间后是否发生变化,如果web服务器经过查询文件修改时间后发现该时间后页面没有发生变化,服务器会回复304 Not modified,此回复不带有任何负载数据,只有头部数据,从而避免了开销。这是一种基于时间的缓存协商。 

   E-tag协商 

  
基于时间的缓存协商会有一些不足,有些文件内容实际并未变化,只是时间变了,此时也需要重新响应,这不够智能。于是就提出了E-tag协商,在服务器第一次响应的头部加入E-tag: “111_22”,浏览器在第二次请求时会自动在头部加上If-None-Match:”111-22”,web服务器会计算响应内容的E-tag值是否与之相符,如果相符的话,web服务器不会重新生成内容,而是返回304状态。E-tag的生成算法可以完全由web服务器自己定义,比如对内容进行md5加密就是很好的一种方法。 

   SSI与Last-Modified 

  
Apache的SSI模式下是不支持Last-Modified的缓存协商的,需要通过XBitHack来支持,其他服务器如Ligd的SSI是默认支持Last-Modified协商的。当然,Apache可以使用E-tag来支持协商,如果有必要可以通过修改服务器代码来实现专用的E-tag协商。大型网站系统的设计中,修改web服务器代码是很常有的事情。 

   4.2 Expires标记 

  
通过前面的浏览器缓存协商,我们已经能够节省大量的数据流量了。但是缓存协商还是依赖于头部的协商,协商过程中尽管数据只是很短的头部的数据,没有任何负载数据,但头部数据来回这么交换协商也是有一定消耗的,如果web服务器在第一次响应的头部中加入Expires标签,那么浏览器在第二次请求的时候根本不会发送任何请求,而是直接在缓存中读取后渲染出来,与缓存协商相比,速度更快了。需要注意的是此标记只在页面跳转时有效,对于F5刷新时该标签无效。F5刷新意味着必要的缓存协商。 

  结论:浏览器缓存依赖于客户端浏览器实现,一般也包括内存缓存和磁盘缓存,通过about:cache 你可以查看缓存情况。浏览器缓存神奇的地方在于它可以节省带宽。 

   5 web服务器页面缓存 

  
前面的动态内容缓存是动态应用程序自己做的优化,Web服务器作为动态应用程序的容器,也有缓存机制,通过配置我们也可以挖掘它的潜力。对于静态内容或者更新不太频繁的动态页面,可以通过url和内容的对应实现缓存。Web服务器在url映射的时候检查缓存,如果缓存命中并且在有效期内,那么就从缓存中获取页面返回给客户端。Apache通过一些配置就可以设置缓冲区,打开缓冲模式。 

   5.1缓存过期 

  
如果你需要控制web服务器磁盘缓存中的有效期,你需要通过头中的标记来控制,这些标记在前面浏览器缓存中的缓存协商中都有讲到,用法类似。缓存有效期通过Expires来控制,记得么?在浏览器缓存中,设置了Expires在网页跳转时浏览会直接读取浏览器缓存而不进行缓存协商。动态程序如果设置了Last-Modified之后会引起浏览器的缓存协商, web服务器缓存在有效期内都会在浏览器的缓存协商中认为返回内容没有发生改变。动态内容中如果没有设置Expires,而设置了Last-Modified标签,则web服务器会设置一个默认缓存有效期。 

   5.2缓存文件描述符 

  
对于在整个应用中一直需要打开的文件,通过mod_file_cache可以实现把这些文件都打开而获得描述符,在整个服务过程中这些文件描述符会被一直缓存在内存中不关闭,从而节省开销。 

  结论:通过打开web服务器的缓存可以获得性能上的提升。记住,所有缓存都是url作为主键,请确保url的唯一性,这里的url是包含了参数的url。 

   6反向代理页面缓存 

  
反向代理是将真实的服务器集群(比如tomcat集群,比如apache静态服务器集群)隐藏在反向代理服务器之后,所有对服务器资源的请求都需要经过反向代理,用户只知道反向代理,而不必关心反向代理之后庞大的集群。由于服务器集群被影藏在反向代理之后,安全性也有保证。 

   6.1引入反向代理缓存 

  
反向代理根据一定的规则把用户的请求转发到后端服务器,由于多了一道转发过程,这肯定会导致吞吐量的降低。有必在方向代理服务器上设置缓存。要引入反向代理缓存需要借助一些专门的软件,比如Squid,比如Varnish。以Varnish为例,varnish相当于是Apache服务器的代理,所有的客户端服务请求首先被发送到Varnish上,然后varnish再决定是否转发请求给apache,或者直接从缓存中生成。Varnish工作在应用层协议之上,它针对头部协议通过配置缓存规则可以实现一些特定的缓存策略。缓存一般放在磁盘上,通过索引结构能很快查找出cache是否命中。反向代理可以是一台,也可以是多台。 

   缓存清除 

  
缓存清理可以通过命令行由管理员手动清理,也可以通过远程的请求来清理。 

   反向代理缓存服务器的监控 

  
监控缓存服务器的cache命中率,分析瓶颈原因。Varnish提供了强大的监控功能,Cacti等开源监控平台可以提供有用的帮助。通过监控信息,主要用来解决3类问题 

  1) 缓存空间满了 

  估计需要的存储空间大小,分配更大的缓存空间 

  2) 缓存有效期如何设置 

  这需要在实时性和吞吐量之间做一个权衡。 

  3) 有些内容没有被缓存 

  分析原因,比如说是设置了cookie。分析缓存策略是否有问题。 

   ESI 

  
ESI类似SSI技术,通过ESI标签,多个页面可以组成一个大的页面。有了ESI,反向服务器可以为不同的子页面提供不同缓存有效期,从而实现页面中不同模块具有不同的刷新频率。比如,希望最新新闻刷新更快,每日要闻刷新慢一些。注意的是不是所有反向代理缓存服务器都支持ESI。 

   6.2反向代理需要注意的问题 

  穿越反向代理 

  
由于请求经过了转发,因此一些信息在转发过程中有可能会丢失,比如客户端请求的IP,通过配置可以避免这一点。 

   网络结构 

  
反向代理是非常繁忙的,它对入口和出口带宽的要求很高,注意把反向代理服务器和后端服务器设置在一个内部网中,避免他们之间的带宽消耗。 

  结论:有了反向代理缓存,后端服务器的动态内容缓存也是有必要存在的,这样可以构成多级缓存,避免运维过程中的失误。 

   7 web组件分离 

  
将不同类型的web资源放在不同的服务器上,以便因材施教,使他们发挥更大的潜力。 

   7.1为不同的组件服务器分配不同的二级域名 

  
比如.mysite.是我的站点,那么: 

  Js.mysite.存放javascript Img.mysite.存放图片 Static.mysite. 存放静态页面 

   7.2分离的优点 

  
域名分离可以提高浏览器下载的并发数。利用HttpWatch或者site-perf.可以监测下载性能。 

   7.3因材施教 

  
不同的组件服务器我们可以采取不同的策略,提高效率。 

   动态内容服务器 

  
这些都是CPU密集型服务。开opcode缓存,cpu搞快一些,内存弄大些,使用多进程的web服务器模型,保持与数据库的高速连接。对于api服务器不用设置缓存。 

   静态网页 

  
这些网页都是IO密集型服务。Web服务器支持epoll,非阻塞IO,异步IO,使用sendfile()系统调用。Web服务器模型使用单进程模型,因为多进程对IO密集型服务意义不大。使用高速磁盘,使用RAID磁盘阵列,购买足够的带宽。 

   图片 

  
网页中图片比较多,使用长连接可以避免请求多次开关的开销。 

   样式表 

  
样式表更新频率比较低,通过设置较长的过期时间,使得浏览器使用浏览器缓存中的html 

   脚本 

  
脚本的过期时间一般设置为1个月 

   视频 

  
为视频服务器提供尽可能多的并发连接数,使得web服务器使用sendfile()这样的系统调用。一台服务读取视频的速度是一定的,根据平均磁盘读取速度可以规划出视频服务器可以支持多少人同时在线播放视频,如果希望够多的人能够同时在线播放视频,需要扩展视频服务器的数量。 

   8分布式缓存 

  
前面提到的缓存都是页面缓存,这种缓存都是缓存整个页面,都是针对读数据的优化。对于web2.0的网站,由于频繁的内容更新导致频繁的缓存重建,这也会导致开销,而且有些页面是无法作为页面缓存的,比如登陆用户的一些状态信息。这里讨论的是页面缓存之外的缓存。 

   8.1数据库的前端缓存 

  
为数据库开辟缓存,当页面缓存缺失不得不进行动态页面生成时,读取数据库也能很快。这部分缓存由动态内容来控制,分为读缓存和写缓存两部分。这部分缓存通过memcached来实现。 

   8.2 memcached数据库缓存 

  
Memcached缓存本质上是key-value对,你需要采取合适的策略把key-value映射成我们的关系数据库Mysql以便处理。需要注意设置缓存过期时间,以实现一定程度的实时更新。Memcached的优异实现可以支持网络高并发模型。 

   分布式缓存 

  
Memcahced缓存不一定要在一台服务器上,可以分布在多台服务器上进行扩展。按照一定的算法将key映射成合适的服务器,然后存入该服务器的memcached。 

   对象序列化 

  
Memcahed可以支持key-object这样的存储对。 

   8.3数据库读缓存 

  
将Mysql返回的对象作为一个对象放入memcached中去作为读缓存,key可以设置成对象的唯一id。 

   8.4数据库写缓存 

  
将一些需要频繁写入Mysql的数据写入memcached,当写入次数超过一定的阈值,再触发数据库操作。 

  结论:memcached是一个非常有力的内存缓存工具,但它只提供了一个最基本的类似hashmap的操作接口,他是一个基础的框架,所有缓存机制的实现都依赖于动态程序的控制,依赖动态程序如何使用cache。 

   9数据库性能优化 

  
迄今为止,前面所有讨论的焦点就是缓存,各种各样的缓存,基于磁盘的,基于内存的,针对各种对象的缓存,页面缓存,对象缓存,放在不同架构层次上的缓存,本地缓存,web服务缓存,分布式缓存。有了缓存,这还不够,因为在缓存miss的情况下,系统回过头来还是要访问数据库,所以提高数据库性能还是要必须面对的一个问题。 

   9.1报告 

  
要想优化数据库性能的先决条件就是有友好的报告,它能告诉你Mysql现在工作的如何?查看Mysql报告的命令有show status,和show infodb status。Mysqlreport是一个很棒的查看mysql状态的第三方工具。 

   9.2索引 

  
为你查询语句中的条件设置合适的索引可以提高查询效率。利用explain mysql_language命令可以帮助你分析你的mysql语句的执行状态,是索引查找还是遍历查找,这可以帮助你做出是否需要对某些列进行合适的索引的决定。注意有些mysql语句对组合索引很敏感,你需要调整你的mysql语句的写法。当索引文件太大导致超过数据本身规模时,索引的效率反而低于全表扫描。 

   9.3慢查询日志 

  
注意打开慢查询日志开关,利用mysqlsla可以分析出那些查询时间过慢的mysql语句。 

   9.4索引缓存 

  
Mysql为索引也引入了内存缓存,其大小可以根据key_buffer进行设置。 

   9.5索引的代价 

  
索引能够大大加速查询速度,但是会引入额外的索引磁盘空间消耗,同时插入或更新记录计算索引也会引入计算开销。如果你的应用中mysql查询的数量大于更新和插入的数量,不要犹豫,赶紧使用索引。 

   9.6锁定与等待 

  
 由于多个进程可能同时修改一个表,因此mysql会引入锁来保持更新数据同步,MyISAM引擎是表锁定,InfoDb引擎室行锁定。行锁定能够使多个线程同时进行更新操作。但是行锁定并非一定优于表锁定,在update和select交替进行的场景,行锁定表现优异。但在update密集型的操作中,表锁定优于行锁定。另外在进行全表扫描时,表锁定的读取速度也会更快。 

   9.7事务 

  
Mysql普遍使用InfoDb引擎的一个重要原因是其支持事务。为了提高事务表的性能,你可以配置事务日志同步到数据库的策略,你需要配置内存缓冲池大小,你可以通过配置绕过内核缓存区使用mysql自带的缓存机制。 

   9.8启用查询内存缓存 

  
简单的配置就可以启用这个功能,默认mysql不会开启这个选项。Update和select交替进行的操作这个功能反而添乱,这是因为update产生后mysql会强制所有的缓存失效。该功能适合密集查询性的服务。 

   9.9临时表 

  
临时表用来储存中间数据,它优先放在内存中,如果内存不够它就会放在磁盘上,通过扩大tmp_table_size避免临时表放到磁盘中去,合理得使用索引可以避免临时表的使用。 

   9.10持久化连接 

  
通过thread_cache_size来持久化数据库连接,避免频繁得创建/销毁连接线程。 

   9.11反范式化设计 

  
数据库设计的教材中告诉我们数据库设计应该遵守3范式,但是反范式的设计可以避免某些昂贵的join联合查询,比如facebook的feed功能(查看你关注的人发生了一些事情)。反范式的代价是额外的冗余数据,额外的写开销。 

   9.12 NoSql 

  
数据库本质上就是key-value对,返璞归真得回到数据库的本质上去,抛弃Mysq维护关系导致的额外开销。NoSql数据库本质上是把关系数据库的退化,它只提供数据库中关键的底层的Key-value机制,把数据的定义权和决定权交给用户。比如Memcached DB,比如HBase等等。其实关系数据库中的范式化对性能来讲是镣铐,利用NoSql完全反范式化设计,能够最大限度得追求读性能,代价就是冗余数据以及冗余数据产生的额外磁盘开销。 http://blog.sina.com.cn/s/blog_a3bfc740010126j1.html

你可能感兴趣的:(常用的web服务器都支持一定的并发用户数,这个并发用户数可以通过配置文件参数来进行配置)