学编程,来源栈;先学习,再交钱
当前系列: 框架&架构 修改讲义
复习:性能优化

缓存

复习:什么是缓存,有什么作用?广泛应用在CPU浏览器、数据库……

适合缓存的数据

缓存是个好东西,但是,没有免费的晚餐要明白缓存的代价是“空间”,不能让缓存的内存空间挤压了正常的程序运行。

所以我们要不缓存,少缓存,或者只是短期缓存:

  • 本身不难获取
  • 不会被频繁使用
  • 频繁变动,而且有“即时性”要求

的数据,而是尽可能缓存和上述三项特征相反的数据。

而且要最大限度的利用缓存空间,还需要

最优的缓存策略

一般来说,就是配置/设定:如何清理缓存?

最常见的策略就是:设置过期(Expire)时间:

  • 可以是绝对时间:一般是从缓存建立起算,指定一个固定的时间段,比如5分钟10分钟,时间一到,缓存就被清理。
  • 也可以是相对时间:比如从缓存数据最后一次被访问起算,30秒后过期;但是,在这30秒内,一旦该数据又被访问,那么过期时间将会被重新计算,变成当前时间加30秒……(和session失效机制非常类似)

还可以指定空间不足时移除哪些缓存项,比如:

  • 优先级(Priority最低的最先被移除
  • 最少被请求/使用的最先被移除

还可以指定缓存依赖(dependency),当缓存所依赖的数据发生改变时,自动清理。

以及缓存数据的存储位置:在服务器,还是客户端?具体在服务器的哪个位置……

缓存策略的效果,可以用“命中率” (100次请求,70次是从缓存中获得,命中率:70%)来衡量,越高越好。

Web框架一般会为我们提供两种

使用缓存的方式

声明式

仅仅通过在方法/页面添加一些声明(Spring的@Cache/C#的[OutputCache])就直接缓存方法的返回值,或者整个页面。

编程式

有时候又被称之为API,就是开发人员要自己写if...else...,调用一些类和方法,完成缓存数据的存取。

一般来说,其流程是固定的:

  1. 先试着从缓存中获取数据,如果缓存中
  2. 有:直接返回
  3. 没有:从数据库中获取到数据
  4. 将数据存入缓存
  5. 将数据返回给客户

缓存的数据结构一般都是键值对

常见面试题:Cache vs Session

  • cache是全局的,所有用户都可以访问的;
  • session是基于用户的,A用户session中是数据B用户无法获得


线程和异步

缓存可以说是性能优化的通用/常用手段;多线程和异步的使用,仅在特定情形下有用。因为:

线程池

服务器本身就是多线程的,本身就是能响应“并发”请求的。

以IIS为例,在IIS中可以设立若干个“应用程序池(application pool)”,每个池中都维护着若干线程。

IIS为每一个Request分配一个线程,Request结束线程不销毁而是“放回”线程池,以便于以后使用。

所以说理论上,开发人员是不用操心我的业务逻辑代码如何应对“并发”请求的。

比如:“我这个Action如果两个请求同时访问,会不会……”,不会的,假设不成立,一个请求就只会在一个线程中处理!

性能瓶颈

新开一个线程执行某个任务,以获得更快响应,只对“CPU密集”型任务有用。

就是说这个任务的实现需要CPU参与的大量运算,一个线程要10秒钟才能算完,开两个线程可能就只需要6秒。

但是Web应用一般来说,是“I/O密集”型的,更通俗的说,就是要操作数据库,不需要什么CPU的大量运算。数据库操作(微服务还要加上网络传输)通常就是性能瓶颈,多线程不仅解决不了问题,反而有可能加剧数据库/网络的拥堵。

异步

无法减少单个请求的响应时间。

服务器上的“异步方法”和Ajax不一样!服务器是能把响应“分批分次”的传递给客户端的。

不管在服务器上怎么异步,最终只有一次机会,把所有结果一次性的发送给客户端。

有用的场景

任务和响应无关

比如,发送Email,写log,这是服务器自己要做的事情,和生成响应无关。

可以先给用户一个响应(比如:评论发布成功),然后再发送Email给被评论文章作者:所以email发送的工作,可以新开一个线程处理。


log也是一样的,但要注意很多log组件已经进行了优化,要考虑是否有必要新开线程。

提高服务器的吞吐量

为了便于说明,先假设服务器是单线程的(多个线程也可能被全部占用),所有的请求就需要排队。

其中某一些请求,是:

  1. 不需要服务器做啥事,但
  2. 需要花费大量时间进行等待的,比如等待第三方验证/支付结果

这时候,将等待工作进行异步。服务器就可以“腾出手来”处理下一个请求。

所以在单位时间内,服务器就能够响应更多的请求,这就叫做“提高吞吐量”。

想象:银行柜台,工作人员闲着等别人来盖章/系统核实……后面的人就会很不舒服,能不能先把我的办了?

本质上就是让服务器“不要闲着”。但是,如果所有的业务都要在服务器上运行,消耗服务器的资源。同学们@想一想@,如果服务器:

  • 本身就闲着,线程池够用,不用排队,异步有没有用?
  • 线程池都不够用了,再来异步,服务器扛不扛得住

最佳实践

使用异步/多线程的副作用(side effect):

  1. 增加代码的复杂性(尤其是bug调试)
  2. 异步/线程的切换需要消耗额外的资源:天下没有免费的午餐

简单理解:

  • 锁、死锁(Deadlock)、资源争夺(race condition)
  • 线程安全 (Thread Safty)
  • 越是复杂精巧的东西越不“耐操”(健壮性robust)

总是最后考虑异步/多线程:(个人建议)

  • 确定性能瓶颈
  • 确定该瓶颈可以通过异步/多线程的方法解决
  • 总是在最后考虑异步/并发(尤其是B/S架构)


队列

因为某些特定的业务,比如“秒杀”,会带来短时间极速攀升的访问量,让系统直接宕机。

这时候,使用队列,进行“削峰”,是一种普遍采用的手段。

其原理是:

  1. 所有请求,首先扔到队列当中,排队
  2. 然后系统按队列顺序,依次拉取当个请求,进行处理

队列可以是进程内的(类库),也可以是外部的(第三方组件/服务,比如RabbitMQ):一般都是现成的、开箱即用的。


压缩/合并

.js/.css/图片/html内容本身,都可以


  • 压缩.js和.css文件,即:
    • 去除注释、空格、回车等无用符号
    • 用短变量名取代常变量名等
  • 合并多个上述文件成一个文件

之后再发往前端。这样可以减少下载上述文件的:

  • 内容(因为压缩)
  • 个数(因为合并)

因为更小/少的文件下载得更快。^_^


一般来说,压缩和合并都是由工具自动化实现。




高并发大流量

这是“面试造火箭,工作拧螺丝”的典型:卷。

尤其是Java,初级程序员(2年工作经验)的,都要问这个问题。

上次老码农QQ群里聊天,一位老大哥说了句实话:

重庆能玩懂这个的,一只手数得过来!

为什么?不是它有多难,而是没有这种场景。

nginx/redis/cluster/队列/分库分表……这些不过都是工具,小白编程培训班的老师都可以讲;但是,你不能说你会这些工具你就有“高并发大流量”的经验了呀!

并发量怎么才算高,流量怎么才算大?没有统一标准的。大部分的应用,其实高不起来:

  1. 云服务器时代,堆硬件能够解决90%的性能问题
  2. 只要你的代码没有“乱来
    PS:为什么这么几台服务器就撑起了Stack Overflow?答案很简单,他们没有“乱来”。


学习笔记
源栈学历
键盘敲烂,月薪过万作业不做,等于没学

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 框架&架构 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码