深入探讨Varnish缓存命中率

http://blog.csdn.net/21aspnet/article/details/6584793

也许你还在为刚才动态内容获得7336.76 reqs/s的吞吐率感到振奋,等等,理想和现实是有差距的,你要忍受现实的残酷,别忘了,我们压力测试中的动态内容都处于全缓存情况下,也就是每次请求都命中缓存,这在现实中往往是不可能的。
首先,缓存区空间大小是有限的,而我们的站点可能有大量的内容需要被缓存,而不像前边压力测试时只有一个内容。一旦缓存区被装满,那么缓存管理器便会淘汰 一些它认为不再需要的缓存内容,比如通过LRU(最近最少使用算法)将使用频率较低的缓存内容淘汰出去,但是,这里判断“不常使用”的标准是不严格的,也 许被淘汰的内容就是你将要访问的下一个内容,这便影响了它的命中率。
其次,缓存的过期时间也影响到它的命中率,假如有效期很短,为10秒,那么最少10秒便会有一次无法命中。
还有,有些内容可能根本没有被代理服务器缓存,比如这些内容包含了set-cookie等不可缓存的HTTP头信息,导致反向代理不会缓存它们,并且在浏览器请求它们的时候也不会去缓存区查找。这是影响命中率的一个重要因素,但往往被我们所忽略。
幸运的是,这些问题我们都可以轻松的解决,前提是,我们需要了解反向代理缓存的实时工作状态,比如Varnish便提供了一个命令行的状态监控程序varnishstat,我们打开它,便看到了当前时刻的状态,如下:

 

  
  
  
  
  1. client_conn           9908723        94.05 Client connections accepted 
  2. client_drop                 0         0.00 Connection dropped, no sess/wrk 
  3. client_req           16433490       155.99 Client requests received 
  4. cache_hit             8751732        83.07 Cache hits 
  5. cache_hitpass           42592         0.40 Cache hits for pass 
  6. cache_miss            7573389        71.89 Cache misses 
  7. backend_conn          3889845        36.92 Backend conn. success 
  8. backend_unhealthy          220         0.00 Backend conn. not attempted 
  9. backend_busy                0         0.00 Backend conn. too many 
  10. backend_fail             4536         0.04 Backend conn. failures 
  11. backend_reuse         3780212        35.88 Backend conn. reuses 
  12. backend_toolate       3866687        36.70 Backend conn. was closed 
  13. backend_recycle       7646677        72.58 Backend conn. recycles 
  14. backend_unused              0         0.00 Backend conn. unused 
  15. fetch_head                 57         0.00 Fetch head 
  16. fetch_length           155097         1.47 Fetch with Length 
  17. fetch_chunked         7508522        71.27 Fetch chunked 
  18. fetch_eof                   0         0.00 Fetch EOF 
  19. fetch_bad                   0         0.00 Fetch had bad headers 
  20. fetch_close              3982         0.04 Fetch wanted close 
  21. fetch_oldhttp               0         0.00 Fetch pre HTTP/1.1 closed 
  22. fetch_zero                  0         0.00 Fetch zero len 
  23. fetch_failed                0         0.00 Fetch failed 
  24. n_sess_mem               1033          .   N struct sess_mem 
  25. n_sess                    633          .   N struct sess 
  26. n_object              1016443          .   N struct object 
  27. n_vampireobject             0          .   N unresurrected objects 
  28. n_objectcore          1017564          .   N struct objectcore 
  29. n_objecthead           982903          .   N struct objecthead 
  30. n_smf                 2647421          .   N struct smf 
  31. n_smf_frag             622470          .   N small free smf 
  32. n_smf_large                 3          .   N large free smf 
  33. n_vbe_conn                 12          .   N struct vbe_conn 
  34. n_wrk                    8000          .   N worker threads 
  35. n_wrk_create             8000         0.08 N worker threads created 
  36. n_wrk_failed                0         0.00 N worker threads not created 
  37. n_wrk_max               11021         0.10 N worker threads limited 
  38. n_wrk_queue                 0         0.00 N queued work requests 
  39. n_wrk_overflow           2441         0.02 N overflowed work requests 
  40. n_wrk_drop                  0         0.00 N dropped work requests 
  41. n_backend                   4          .   N backends 
  42. n_expired             6344546          .   N expired objects 
  43. n_lru_nuked            183957          .   N LRU nuked objects 
  44. n_lru_saved                 0          .   N LRU saved objects 
  45. n_lru_moved           3692170          .   N LRU moved objects 
  46. n_deathrow                  0          .   N objects on deathrow 
  47. losthdr                    84         0.00 HTTP header overflows 
  48. n_objsendfile               0         0.00 Objects sent with sendfile 
  49. n_objwrite           15466812       146.81 Objects sent with write 
  50. n_objoverflow               0         0.00 Objects overflowing workspace 
  51. s_sess                9906155        94.03 Total Sessions 
  52. s_req                16433490       155.99 Total Requests 
  53. s_pipe                     37         0.00 Total pipe 
  54. s_pass                 108252         1.03 Total pass 
  55. s_fetch               7667658        72.78 Total fetch 
  56. s_hdrbytes         7187255662     68221.35 Total header bytes 
  57. s_bodybytes      111592032839   1059230.32 Total body bytes 
  58. sess_closed           1905544        18.09 Session Closed 
  59. sess_pipeline               0         0.00 Session Pipeline 
  60. sess_readahead              0         0.00 Session Read Ahead 
  61. sess_linger          15277717       145.02 Session Linger 
  62. sess_herd            13547370       128.59 Session herd 
  63. shm_records        1028855796      9765.89 SHM records 
  64. shm_writes           77957008       739.97 SHM writes 
  65. shm_flushes            131005         1.24 SHM flushes due to overflow 
  66. shm_cont               144281         1.37 SHM MTX contention 
  67. shm_cycles                427         0.00 SHM cycles through buffer 
  68. sm_nreq              15306717       145.29 allocator requests 
  69. sm_nobj               2024948          .   outstanding allocations 
  70. sm_balloc         13595295744          .   bytes allocated 
  71. sm_bfree          40091795456          .   bytes free 
  72. sma_nreq                    0         0.00 SMA allocator requests 
  73. sma_nobj                    0          .   SMA outstanding allocations 
  74. sma_nbytes                  0          .   SMA outstanding bytes 
  75. sma_balloc                  0          .   SMA bytes allocated 
  76. sma_bfree                   0          .   SMA bytes free 
  77. sms_nreq                14062         0.13 SMS allocator requests 
  78. sms_nobj                    0          .   SMS outstanding allocations 
  79. sms_nbytes                487          .   SMS outstanding bytes 
  80. sms_balloc            6844837          .   SMS bytes allocated 
  81. sms_bfree             6846298          .   SMS bytes freed 
  82. backend_req           7668789        72.79 Backend requests made 
  83. n_vcl                       1         0.00 N vcl total 
  84. n_vcl_avail                 1         0.00 N vcl available 
  85. n_vcl_discard               0         0.00 N vcl discarded 
  86. n_purge                740577          .   N total active purges 
  87. n_purge_add            905392         8.59 N new purges added 
  88. n_purge_retire         164815         1.56 N old purges deleted 
  89. n_purge_obj_test     13397373       127.17 N objects tested 
  90. n_purge_re_test  131875330367   1251759.15 N regexps tested against 
  91. n_purge_dups           240655         2.28 N duplicate purges removed 
  92. hcb_nolock                  0         0.00 HCB Lookups without lock 
  93. hcb_lock                    0         0.00 HCB Lookups with lock 
  94. hcb_insert                  0         0.00 HCB Inserts 
  95. esi_parse                   0         0.00 Objects ESI parsed (unlock) 
  96. esi_errors                  0         0.00 ESI parse errors (unlock) 
  97. accept_fail              2553         0.02 Accept failures 
  98. client_drop_late            0         0.00 Connection dropped late 
  99. uptime                 105352         1.00 Client uptime 

看起来很强大,的确,它帮我们获得了很多重要的信息,包括了三个重要的统计项:

N struct object

当前被cache的条目

Client requests received

代表到目前为止,浏览器向反向代理服务器发送的HTTP请求累积次数,由于可能使用长连接,所以它可能会大于上边的Client connections accepted。

Cache hits

代表在这些请求中,反向代理服务器在缓存区中查找并且命中缓存的次数。

Cache misses

代表在这些请求中,反向代理服务器在缓存区中查找但是没有命中缓存的次数。

N expired objects

代表过期的缓存内容个数

N LRU nuked objects

由于cache空间满而不得不扔掉的cache条目,如果这个数字是0,就没必要增加cache的大小了。

N LRU moved objects

代表被淘汰的缓存内容个数

Total header bytes

代表缓存区中所有缓存内容的HTTP头信息长度

Total body bytes

代表缓存区中所有缓存内容的正文长度
通过以上这些统计项,我们还可以知道更多,比如Cache hits和Cache misses的总和代表了反向代理服务器在缓存区中查找内容的总次数;而用Client requests received减去这个总次数,便大概等于没有查找缓存的请求数;Total body bytes和Total body bytes的总和则代表了缓存区当前的空间使用量。
了解了这些后,我们回头看前边的三个问题,都很容易分析和解决。
对于缓存区空间大小的问题,我们可以通过监视它的使用量以及缓存淘汰个数,在必要的时候分配更多的缓存区空间,但是这可能需要重新启动反向代理服务器。当 然,我们应该首先根据站点的规模,来估算出大概的缓存区空间,并且分配出大约为它5倍以上的缓存区空间,以适应一段时期内的内容数量增长。
其次,是缓存有效期取值的问题,但这本身不是一个问题,它取决于后端Web服务器的承受能力,以及你对站点内容实时性的要求,我们在前边介绍动态内容缓存 的章节中曾经探讨过缓存有效期的取值,实质上是一个寻找“过”与“不及”之间平衡的长期过程,但在动态程序实现的内容缓存机制中,我们还可以通过主动删除 缓存的方法来实现缓存到期之前的更新,而基于HTTP协议的反向代理缓存机制则不容易做到这一点,后端的动态程序无法主动删除某个缓存内容,除非我们清空 反向代理服务器上的缓存区。
在寻找平衡的过程中,有时候实时性需求可能要向缓存有效期适当让步,当我们的站点面临较大的压力时,我们不得不暂时以牺牲实时性作为代价,适当的延长缓存有效期,直到我们有了成熟的性能扩展方案。
在初步评估缓存有效期的时候,我们可以通过一些量化的计算来得出理论数值,举个例子,假如我们的站点有60000个动态内容处于频繁访问的状态,我们通过 Cache-Control将它们在反向代理服务器上的缓存有效期都设置为60秒,这样一样来,后端服务器将必须承受最多每秒处理1000个动态内容的工 作量,如果这些动态内容都进行完整的计算,比如访问数据库,那么显然后端服务器是无法承受的,这时候我们可以将缓存有效期延长到300秒,即5分钟,这使 得后端服务器只需要每秒处理200个动态内容,大大减少了工作量。
那么,如果从缓存命中率的角度来分析缓存有效期取值的时候,有一点需要明白,当我们看到缓存的命中率非常低时,有时并不代表我们需要马上延长缓存有效期, 这时候还要观察当前的站点实际吞吐率,举个极端的例子,假如你发现你的站点1个小时内只有1次访问,而缓存有效期为半个小时,那缓存命中率必然会非常低, 但即便是每次缓存都不命中,也不会对后端带来什么压力,实际上如果你的站点只有一台Web服务器,并且站点的实际吞吐率远远低于它的处理能力时,完全没有 必要使用反向代理服务器。
最后关于内容没有被缓存的问题,我们也可以在Varnish的状态监视中找到线索,比如有一个用Varnish加速第三方应用wordpress的例子,得到的Varnish状态如下:

  
  
  
  
  1. 4+10:37:44 
  2. my.server.com 
  3. Hitrate ratio: 1 1 1 
  4. Hitrate avg: 0.0364 0.0364 0.0364 
  5. 2324 0.00 0.01 Client connections accepted 
  6. 6191 0.00 0.02 Client requests received 
  7. 12 0.00 0.00 Cache hits 
  8. 7 0.00 0.00 Cache hits for pass 
  9. 318 0.00 0.00 Cache misses 
  10. 6179 0.00 0.02 Backend connections success 
  11. 0 0.00 0.00 Backend connections not 
  12. attempted 
  13. 0 0.00 0.00 Backend connections too many 
  14. 0 0.00 0.00 Backend connections failures 
  15. 4057 0.00 0.01 Backend connections reuses 
  16. 6151 0.00 0.02 Backend connections recycles 
  17. ... 

我们看到,Varnish一共处理了来自浏览器的6191个请求,其中命中缓存的有12个,真是太少了,而没有命中缓存的有318个,奇怪,剩下的 那么多请求根本就没有去缓存区检查,也就是说,Varnish认为那些内容不能被缓存。不能被缓存总是有原因的,你需要根据反向代理缓存的规则,来进一步 的检查。而我们这个例子中,都是cookies惹得祸,因为在wordpress中由于我们会安装一些各种各样的插件,有些插件会使得wordpress 的每个页面都带有写入cookies的set-cookie标记,而我们前边在vcl_fetch函数中禁止了这类内容的缓存,问题就在这里了,我们希望 将这些多余的cookie关闭掉,但是,wordpress自身的登录和管理页面是需要cookies才可以正常工作的,所以我们还需要让反向代理不缓存 这些页面,我们使用以下VCL配置:

  
  
  
  
  1. sub vcl_recv 
  2. if (!(req.url ~ "wp-(login|admin)")) 
  3. unset req.http.cookie; 
  4. sub vcl_fetch 
  5. if (!(req.url ~ "wp-(login|admin)")) 
  6. unset obj.http.set-cookie; 

可以看到,除了wordpress自身的登录和管理页面以外,我们将其它内容的HTTP头信息中有关cookie的标记全部都清除掉,这使得Varnish可以将大部分内容缓存起来,提高缓存命中率,同时不影响我们登录和管理wordpress。
这个例子给了我们一些启示,对于我们自己的站点,如果从长远考虑,那么在规划的时候就要费点心思,我们可以根据内容是否可以缓存在反向代理服务器上,将它们置于不同的主机,这样便可以在必要的时候将可以缓存的内容快速与反向代理服务器对接,获得较好的加速效果。

你可能感兴趣的:(varnish,命中率)