计算机世界的时间和人类世界的时间做了对比,常常把CPU比喻成跑得很快,但是记不住事情的“阿甘”,
CPU一个时钟周期如果按1秒算:
内存访问就是6分钟
一次 CPU 上下文切换(系统调用)需要大约需要1小时
在 1Gbps 的网络上传输 2K 的数据需要10多个小时
从 SSD 读取 1MB 的顺序数据,大约需要 1ms,换算成人类时间是 1个月
从磁盘读取 1MB 连续数据需要 20ms,换算成人类时间是 20个月
如果说打开一个网页可以秒开的话,那也相当于100年
对于CPU来说,这个世界真是太慢了!
并发和多线程 充分利用CPU性能
CPU的速度超级快,不能老是让它闲着,要充分地压榨它!这里有两个强劲的理由:
1. 人类需要多个程序“同时”运行,我们要把CPU的时间进行分片,让各个程序在CPU上轮转,造成一
种多个程序同时在运行的假象,即并发。
2. 当CPU遇到IO操作(硬盘,网络)时,不能坐在那里干等“几个月”甚至“几年”,在等待的时候,一
定要切换,去执行别的程序。
说起来简单,但是程序的切换需要保存程序执行的现场,以便以后恢复执行,于是需要一个数据结构来
表示,这就是进程了。如果一个进程只有一个“执行流”, 如果进程去等待硬盘的操作,那这个程序就会
被阻塞,无法响应用户的输入了,所以必须得有多个“执行流”,即多线程。
缓存 短时记忆
需要持久化的数据一定要保存到硬盘中,但是硬盘超级慢,支持不了大量的并发访问,那怎么办呢?
可以把最常访问的热点数据放到CPU的缓存中嘛, 其实CPU也是这么做的,但是CPU的L1, L2, L3级缓
存实在是太小, 根本满足不了需求。于是只好退而求其次,把热点数据放到速度稍慢的内存中,于是应
用程序的缓存就出现了。缓存虽然是解决了问题,但是也带来了更多的问题,例如:缓存数据和数据库
数据怎么保持一致性?
缓存如果崩溃了该怎么处理?数据在一台机器的内存放不下了,要分布到多个机器上,怎么搞分布式
啊,用什么算法?.....
异步 排队
对于Tomcat这样的应用服务器,对于每个请求都要用一个线程来处理,如果现在有一万个请求进来,
Tomcat会建立1万个线程来处理吗? 不会的,因为线程多了开销会很大 ,线程切换起来也很慢,所以
它只好用个线程池来复用线程。现在假设线程池中有一千个可用线程(已经非常多了),它们都被派去
访问硬盘,数据库,或者发起网络调用,这是非常慢的操作,导致这一千个线程都在等待结果的返回
(阻塞了),那剩下的九千个请求就没法处理了,对吧?所以后来人们就发明了新的处理办法,仅使用
几个线程(例如和CPU核心数量一样),让他们疯狂运行,遇到I/O操作,程序就注册一个钩子函数放
在那里,然后线程就去处理别的请求,等到I/O操作完成了,系统会给这个线程发送一个事件, 线程就
回过头来调用之前的钩子函数(也叫回调函数)来处理。
这就是异步,非阻塞的处理方式。nginx,Node.js,Vert.x等采用的都是类似的思想。
单线程 基于内存的
Redis使用单线程的方式来处理请求的,为什么用单线程就可以呢? 它为什么不像Tomcat那样使用多线
程和线程池呢?因为它面对的仅仅是内存,内存的速度在计算机的体系中仅次于CPU,比那些网络操作
不知道要快到哪里去了所以这个唯一的线程就可以快速地执行内存的读写操作,完成从许多网络过来的
缓存请求了。单线程还有个巨大的优势,没有竞争,不需要加锁!