做过性能调优的同学都知道, 怕的不是性能差, 而是费了半天劲在细节上死抠, 却忽视了另外一整个对性能有巨大影响的维度, 旁边放着一西瓜却使劲在芝麻上雕花. 针对这种情况, << Performance Tuning: A Comprehensive Guide >>的作者梳理了影响性能的几个维度, 具备一定的完整性, 新手可以按图索骥的去调优, 老手也可以拿来参考看看是否漏掉了某些事半功倍的方法.
  这里谈到的性能是一种统称, 包含响应时间/延迟latency, 吞吐量throughput等, 也暂时不涉及压力测试负载测试等手段类似但意图不同的测试(它们之间的联系和区别可参考Restatement: 性能,容量,负载,以及压力测试)
  该体系将影响性能的因素分了三大类, 分别是
  算法本身,
  算法运行环境与所需资源,
  以及算法和环境资源的交互.
  这三类基本囊括了所有因素. 下面我们来看一下里面的细节.
  优化算法本身
  这通常是我们一头扎进去开始局部优化的地方. 常用的手段包括使用各种 Profiler 来度量 CPU 时间, 内存占用率, 函数调用次数以进行问题定位, 然后实施各种调优方法, 比如优化循环, 空间换时间等.
  选择合适的数据结构自然算作此类优化, 而我们把不同存储模型等较大规模的优化也归为此类, 比如使用 Document 代替关系型存储等.
  这个方向的优化可以很快的帮助我们消除一些明显的编程细节引起的瓶颈, 但过了初期突飞猛进的阶段后, 每获得一点改善, 都需要付出巨大的智力上的努力, 事半功倍. 不要忘了我们还有另外的武器.
  优化运行环境与资源
  运行环境与资源包括各种软硬件平台: 操作系统, 数据库, 运行平台, CPU, 内存, 磁盘, 网络等等. 简单, 省事的调优方法其实是优化硬件资源, 使用快速计算资源代替慢速计算资源, 提升资源计算能力:
  更快的 CPU
  更快的本地 IO 设备: 内存代替硬盘, SSD 代替机械硬盘, 增加内存减少分页
  更快的网络 IO 设备: 光纤及专线增加网络带宽, 万兆千兆网卡代替千兆百兆网卡
  快速计算资源代替慢速计算资源不局限于同类资源, 比如快速存储代替慢速存储. 跨类型的资源优化也有很好的例子, 比如本地计算换网络传输: 压缩传输内容是一种常见的性能优化, 增加 CPU 压缩解压缩时间, 但可减少大量网络传输时间.
  而运行平台的软件部分, 比如操作系统, 数据库, 中间件等, 调优的成本要高一些, 并且有可能工作量大到不可接受. 比如从 Windows 平台迁移到 Linux 平台, 从数据库 A 切换到数据库 B, 从 EJB 切换到 Spring… 这些好在初期考虑好
  这类调优见效快, 但受制于预算, 及硬件本身的限制. 通常很快这类调优会在榨取后一点计算资源后, 遇到瓶颈.
  而我们通常谈论的焦点, 也是当前各种调优实践集中的领域, 是优化算法和资源间的交互, 包括:
  减少单台服务器(或单位计算资源)的处理量
  充分利用系统资源
  减少不必要的计算
  减少不必要的IO
  优化算法和资源间的交互
  减少单台服务器(或单位计算资源)的处理量
  调优一般发生在单台机器处理能力已达上限的情况. 一个思路是把压力分散到多台机器上, 从而使每台机器都能获得可接受的延迟或吞吐量.
  总的原则是分而治之. 分的维度包括业务, 组件边界, 访问频率或对系统资源的消耗程度, 瓶颈资源等
  业务: 把大应用按业务分成独立的互相合作的系统, 高层一点的SOA, 内层一点的数据库分库
  组件边界: web 服务器, 应用服务器, 数据库服务器, 文件服务器…
  对系统资源的消耗程度: 读写分离
  瓶颈资源: 分表分片
  一旦按上述维度分好了, 还可以在所有维度上应用负载均衡, 把访问量分散到不同服务器.
  充分利用系统资源
  多线程: 只要原先阻塞的延迟超过了线程切换的延迟, 多线程是值得的.
  多进程
  异步操作: 异步操作可以降低阻塞, 减少延迟, 代价是编程模型的复杂
  负载均衡: 在”减少单台服务器(或单位计算资源)的处理量”和”充分利用系统资源”都提到了负载均衡, 由此可以看出负载均衡有两个作用, 防止某台服务器过满, 防止某台服务器过闲. 这真是个 useless 的结论啊, 负载均衡按定义不是干这个的吗?
  减少不必要的计算次数
  缓存计算结果, 主要指服务端缓存
  减少不必要的 IO 次数
  网络 IO 次数: 客户端缓存, CDN 缓存. 合并资源以减少请求次数.
  磁盘 IO 次数: 缓存常用数据
  可以看出缓存是减少不必要计算和 IO 的重要手段. 缓存的设计主要是根据资源变化频率对资源进行分类, 比如动静分离等. 其前提是恰当的状态管理, 分离无状态的逻辑和有状态的逻辑, 并付出一定的对一致性的妥协, 运维的复杂为代价.
  缓存的适用场景:
  热点不均衡
  有效时间不过短
  一致性牺牲程度可接受
  以上所有手段可以组合使用, 有冲突时再做权衡.