系统基础
吞吐率(qps, request per second)量化描述了服务器的并发处理能力
压力测试的前提条件: * 并发用户数 * 总请求数 * 请求资源描述(静态/动态)
数据链路层的流量控制是通过控制接收方来实现的
支持400MHz前端总线频率的32位处理器,理论上它的总线带宽为: 32bit*400MHz = 12.8Gbps = 1.6GB/s
进程优先级(top命令中的PR)表示进程调度器分配给进程的时间片个数。一个时间片的长度跟CPU的主频和操作系统有关,比如Linux上一般为10ms,那么PR值为15表示这个进程的时间片为150ms。
Linux对进程的动态调整,体现在进程的nice属性中(top命令中的NI)
内核级线程一一对应着轻量级进程,所以也有上下文切换开销
系统负载(top命令中LoadAvg)表示运行队列中就绪态的进程数平均值, top中显示的是最近1分钟、5分钟、15分钟分别计算的系统负载
进程有独立的内存空间,但只能共享CPU寄存器。进程被挂起的本质是将它在CPU寄存器中的数据拿出来暂存在内核态堆栈中,而恢复工作的本质是将它的数据重新装入CPU寄存器。这段装入装出的数据被称为“硬件上下文”,它是进程上下文的一部分。除此之外,进程上下文还包含进程运行时需要的一切状态信息。
top的RES值表示其占用的物理内存大小,SWAP值表示其使用的虚拟内存大小,VIRT值等于RES和SWAP的总和,默认单位是KB。
IOWait指CPU空闲等待IO的时间比例,描述了CPU的性能。CPU可能因为IO在wait,也可能因为频繁切换进程等其他原因在wait,所以IOWait不能代表IO操作的性能。要真正了解当前IO,可以进行磁盘IO测试或查看网络IO流量等。
同步非阻塞IO:轮询
多路IO就绪通知 * select/poll:本质上没多大差别,告知所有就绪的文件描述符(level triggered,水平触发),轮询 * sigio:告知刚刚变为就绪状态的文件描述符(edge triggered,边缘触发),轮询 * epoll:支持水平触发和边缘触发,事件通知
内存映射:将内存中某块地址空间和指定的磁盘文件相关联,从而把对这块内存的访问转换为对磁盘文件的访问 在大多数情况下,使用内存映射可以提高磁盘IO的性能,它无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样访问文件。 内存映射和直接访问文件没有本质上差别,因为数据从磁盘到用户态内存都要经过两次复制:磁盘<->内核缓冲区,内核缓冲区<->用户态内存。
sendfile()系统调用:处理静态文件请求时,磁盘文件的数据先经过内核缓冲区,到达用户内存空间;因为是不需要任何处理的静态数据,所以它们又被送到网卡对应的内核缓冲区,然后再被送入网卡进行发送。sendfile()系统调用可以将磁盘文件的特定部分直接传送到客户端的socket描述符,加快静态大文件的请求速度
缓存
询问服务器本地缓存是否可用(缓存协商) * Last-Modified 和 If-Modified-Since * ETag 和 If-None-Match
使用Last-Modified存在一些缺点,比如,每次文件的修改时间变化后,无论内容是否真的变化,浏览器都会重新获取全部内容;再比如,同一个文件存储在多台Web服务器上,用户的请求在这些服务器之间轮询,实现负载均衡,而这些服务器上同一个文件的最后修改时间很难保证完全相同,这会导致用户的请求每次切换到新的服务器时就需要重新获取全部内容。这时候,使用直接标记内容的某种ETag算法,就可以避免这些问题。
直接使用本地缓存 * Expires 和 Cache-Control
Expires使用绝对时间,若客户端和服务端的时间不一致,本地缓存的有效期检查就有问题。Cache-Control使用max-age这一相对时间弥补了Expires的不足。当响应头中同时含有Expires和Cache-Control时,浏览器优先使用Cache-Control。
要更新静态文件的缓存,在引用文件时让url发生变化即可,比如增加一些个性化的参数:the-static-file-url?v=1.2
长连接:客户端在发出的http请求头中包含Connection: Keep-Alive
,服务端一般默认支持长连接。当客户端和服务端对长连接的超时设置不一致时,以较短的超时时间为准
gzip压缩:请求头里面可用Accept-Encoding
告知浏览器支持的压缩方式,而服务端则用Content-Encoding
作为回应
浏览器三种刷新方式: * Ctrl+F5或Ctrl+刷新按钮:强制刷新,缓存都无效 * F5或刷新按钮:一般刷新,缓存协商有效(Last-Modified有效),但本地缓存无效(Expires无效) * 点击超链接或地址栏输入后跳转:以最少请求来获取网页,会对所有没过期内容直接使用本地缓存(Expires只对这种方式有效)
浏览器对同一域名有最大并发数限制。为了增加并发,尤其是对一些静态资源,可以使用多个域名。但由于域名解析本身也耗时,所以实践上2-4个为宜。需要注意是,加载js脚本时会暂停加载其他资源。
当我们扩展缓存系统的服务器数后,由于分区算法的改变,重建和预热缓存需要时间,但我们不需要考虑缓存数据在分区之间的迁移,因为这是缓存,不影响站点的正常运转。
数据库
在select语句前加explain
,可以看查询过程是否使用了索引
一次查询只能使用一个索引,多个索引无法共同使用,所以有时要创建组合索引
索引对where / order by / group by子句中使用的字段都可起作用,若查询子句只包含组合索引的最左前缀(创建组合索引时的多个key中最左的几个key),则查询会直接使用这个组合索引
mysql可以开启慢查询日志,和未使用索引查询的日志
大多数的慢查询都是因为索引使用不当,其他原因包括查询语句过于复杂(比如联合查询)或者数据表记录数过多,通过反范式化设计(引入冗余数据)和数据分区可以有效改善这一状况。
mysql可以开启查询缓存。缓存过期策略是:当一个数据表有更新操作后(如update或insert),涉及这个表的所有查询缓存都失效。
mysql可以开启线程池
两种数据库格式: * MyISAM:表锁定 * Innodb:行锁定、事务(预写日志)
简单地说,3NF(第三范式)要求在一个数据表中,非主键字段之间不存在依赖关系
负载均衡
http重定向:使用http响应头的Location域
DNS负载均衡
反向代理负载均衡
NAT
直接路由
IP隧道
后端怎么保存session: * 本地化保存,要对ip做hash或用cookie记后端服务器编号 * 分布式session服务器
NAT,内核态转发 * Netfilter/iptables。Netfilter在内核中维护着一些数据包过滤表,iptables这命令行工具可以对Netfilter的过滤表进行插入、修改或删除操作 * IPVS/ipvsadm。ipvsadm比iptables用起来更方便
当实际服务器的吞吐率小于3000qps时,反向代理和NAT负载均衡的整体吞吐率差距不大。这意味着对于一些开销较大的内容,使用简单的反向代理来搭建负载均衡系统非常值得考虑,至少在初期是个快速有效的方案,而且它非常容易迁移到NAT方式。
将基于NAT的集群和DNS轮询混合使用,你可以组建多个条件允许的NAT集群,比如5个100Mbps出口带宽的集群,然后通过DNS轮询方式来将用户请求均衡地指向这些集群,同时你还可以利用DNS智能解析实现地域就近访问。
直接路由(也使用ipvsadm): 工作在数据链路层(第二层),调度器通过修改数据包的目标MAC地址,将它转发到实际服务器。实际服务器要添加和调度器相同的IP别名,设置不寻找其他拥有这个IP别名的服务器,不响应网络中针对这个IP别名的ARP广播,这样才可以让转发到实际服务器的数据包找到归属。
IP别名:一个网络接口除了拥有一个IP地址,最多还可以为它设置256个IP别名
直接路由比起NAT转发的优势在于,实际服务器的响应数据包不经过调度器而直接发往用户端(实际服务器要直接接入外部网络)
RFC1918规定的私有IP地址范围是: 1 2 3 10.0 .0 .0 - 10.255 .255 .255 (10 /8 prefix)172.16 .0 .0 - 172.31 .255 .255 (172.16 /12 prefix)192.168 .0 .0 - 192.168 .255 .255 (192.168 /16 prefix)
IP隧道: 与直接路由类似,但实际服务器和调度器可以不在同一个WAN网段,将调度器收到的IP数据包封装在新数据包中转发给实际服务器,然后实际服务器的响应数据包可以直接发往用户端。
扩展数据库
主从复制(主服务器开启日志,写操作只能在主服务器)、读写分离(MySQL Proxy,数据库反向代理)
垂直分区:将不需要联合使用的数据库表分布到不同的服务器
水平分区:将同一数据表中的记录通过特定算法分离成不同的数据表,从而可以部署到不同的服务器
事实上,很多大规模的站点基本上都经历了从简单主从复制到垂直分区,再到水平分区的过程
分区算法: * 按哈希分:按照分区索引字段的哈希做分区,对分区扩展并不友好,一旦我们需要从10个分区扩展到20个分区,这便涉及所有数据的重新分区 * 按范围分:按照分区索引字段的范围做分区,较好扩展,但各个分区的工作量会存在较大差异 * 维持映射关系:维持每个记录的分区对应关系,可能十分庞大
分布式的并行计算:Map/Reduce
工具
用apache附带的ab
做压力测试
用Nmon可以监视服务器每秒上下文切换次数
反向代理缓存服务器:Squid太古老笨重,可用Varnish
用mysqlsla查看mysql慢查询日志
Spock Proxy,mysql多个水平分区的访问调度
参考