在Zen And The Art Of Scaling - A Koan And Epigram Approach中,Russell Sullivan提出了一个非常有趣的总结:软件开发常见的20个传统的系统瓶颈,这听起来像是说有20个故事情节,并且依赖于你如何策划这些故事,或许都是真的,但唯有实践才知道它们带给我们的酸甜苦辣。 0 d, U5 o( r. r8 o0 c: _% U# B) B) H4 p) T# R
有一天,Aurelien Broszniowski给我发了一份电子邮件,把这些瓶颈用列表的方式展示出来。在接下来的交谈过程中,我又把该列表抄送给了Russell,Russell对此列表进行了整理。5 ` a/ ?8 }; u
: g% R/ o' }: _7 y8 @$ u% X" e
|3 ?3 v! |3 T [9 ] % R; L. f" S ?/ M: h. B, q7 ~' P
Russell说:“我真希望在年轻时看到这样的一份列表”。伴随着经验的增长、项目的增多、解决各种不同类型的问题和不断总结各种经验教训,你会在这份列表上添加更多的东西。所以,当你在阅读该份列表时就像是在回顾一个个故事片段。2 P# d: a& Z% ?3 x5 {
# G3 ~! \4 o, ?6 P
数据库9 O; c4 z$ R& P2 I
0 z; `0 v2 B2 w5 E6 C, U
工作任务内存超过可用的RAM内存 0 w2 l J. Q& U. N. Y1 g; H长/短查询9 T3 I+ _" s) l
写入冲突 - b( W. z& X( j( z' Y$ J5 z大连接(join)占用内存 # ^) G* y; } \$ ?7 m虚拟化! n* C w. {$ ~2 Y
! _# u+ V6 F A4 a共享一个HDD、磁盘寻死(disk seek death)1 F; v* {# @) H6 ?
在云端网络I/O波动& o2 y, k( K% d
编程3 e# Z: o4 B* w4 e
- }" {( K# m+ O7 u# A3 Y6 J6 g线程:死锁、调试、非线性扩展等 ( ?% u' n- B `& b; M事件驱动编程:callback()过于复杂、如何在函数调用中存储有状态等 ' ?5 F. I+ ~6 ~' G$ G缺乏调优、跟踪、日志等7 ]9 M+ P3 j6 G6 U
单模块不可扩展、单点故障(SPOF:Single Point Of Failure)、非横向扩展等 % M: i6 s. z- [$ @5 R9 u+ ^4 I有状态应用程序 2 L: \$ S$ j2 s1 W1 d* d设计问题:开发的应用程序只在自己的机器行运行正常,或者只是在几个人测试的时候正常(没有经历压力测试)。; r4 n7 w. ?2 X$ `6 E7 o
算法过于复杂, J: l# ]8 R2 B: E, S0 A1 p- N
相关服务,例如DNS查找以及其他可能屏蔽的服务8 Q) ]" s. j1 y7 [
堆栈空间 ( T; n" a0 U2 a3 c磁盘 - k4 v5 v; c; A( ^/ \* e' I4 E! c* _* z7 b) X0 z1 R1 H2 n5 o
访问本地磁盘0 W1 Z8 ^* e8 L; ^+ T
随机访问磁盘I/O) ^% _: ?) r& r5 n5 z
磁盘碎片 v8 m5 g, a8 r) P/ Z+ Y
当SSD写入的数据大于SSD容量时,性能会下降 / a8 u1 F5 U' B7 kOS 2 a* R( P! k2 u* l5 {/ ? 6 W0 b; I3 N9 H) p+ F: DFsync饱和,Linux缓冲区填塞(Fsync flushing, linux buffer cache filling up)! h1 Q" h' q1 I- U( K! v; g
TCP缓冲区太小" ?5 q `% B3 ?$ _ @
文件描述符限制 . i$ {& P* W: d5 l8 a- a功率分配(Power budget); F4 w( r& G" D+ K7 ]4 k. ~1 m
缓存$ m7 \' ?, U7 g) W9 I. ~' \. q
! l V* X& f3 N x6 X E1 E没使用memcached(数据库崩溃)/ f# v2 A9 s% d% @" o
HTTP中:headers、etags、没有使用gzip压缩等。! P) D* F* M( z- y- O l
没有充分利用浏览器缓存 + N6 P8 e f4 K. [ J5 ]字节码缓存(如PHP) i" F0 \9 X* b: d6 j2 i+ i9 x, Q6 ?1 }L1/L2缓存:这是个令人头疼的大瓶颈。把关键并且经常访问的数据存储在L1/L2中。这涉及到很多:snappy网络I/O,列数据库直接在压缩数据上运行算法等。利用一些技术不销毁你的TLB。最重要的思想是紧紧的抓住计算机的体系结构,涉及多核CPU,L1/L2,共享的L3,NUMA RAM,从DRAM到芯片数据传输带宽/延迟,DRAM缓存的DiskPages,DirtyPages,流经CPU<->DRAM<->NIC的TCP包。 3 T+ h, n ~$ H+ _* ZCPU ) s6 n% z$ _0 ~& a" d. u' b8 S9 z% ^ f3 q4 `, W g& ~1 J# B
CPU过载) X7 r1 F0 v& _3 S7 o- ?
内容切换—>单核上开启的线程过多、Linux调度器、系统调用太多等 ' L7 p5 h! t: C+ [. [3 }IO等待—>所有的CPU在同速等待 7 M" B X# q4 a' {$ |9 WCPU缓存:缓存数据是一个细粒度进程,为了在多个实例与不同的值数据之间找到正确的平衡,来保持缓存数据的一致性和繁重同步。 ( p2 F N% a/ J0 S: y6 X底板吞吐量(Backplane throughput) / m Z- @1 y/ I网络 0 J8 a$ Y$ Q8 ?; Z6 r4 c 7 Z+ b( W/ P2 ` e3 {8 h4 t8 N, W5 r) ?NIC刷爆、IRQ饱和、软中断占用掉了100%CPU- G5 M2 R5 ^0 T8 E1 q+ A E
DNS查询2 f$ y6 N U2 C
数据包丢失+ }. _; d; X. k T: Z
网络中存在预期外的路由 . ~8 T* h9 A" S# v& D! o访问网络磁盘 , |9 `; g9 X6 ?2 Z- p: q! v0 N% {共享SAN4 N! I! p$ `- ^7 u$ l1 L. o7 e6 n
服务器故障—>无法从服务处得到响应 0 m; k: i; f# r! u进程( c* Q4 i. a9 R$ S
2 l# G% a7 e8 c# s0 G# N测试时间+ p. M) p2 n% }% W( |& U
开发时间 + D" M# r( p6 e9 H" Y* F团队规模; X$ y8 g: B; {: C; N/ T! I
预算7 r) B" H; t; y& K
代码债务/ m! c3 [7 E6 r2 n5 g7 s
内存4 G# A/ | [ a6 _6 X2 s3 h
7 b! y- v0 `! N
内存不足—>杀死进程,切换到swap,挂起 , N. ?) W' H0 m: d& C# H/ q- }4 y6 [内存不足导致磁盘交换(与swap相关) : K$ P! N; k% e; N记忆库开销过大(Memory library overhead)2 \: u# _1 t) T9 b
内存分片(在java中需要会因为内存回收而停顿;在C中,malloc总是开始分配内存)$ k# n: V+ O- A8 O m% v% d8 N