程序猿应该知道的数字
遇到选择数据结构的时候,一个非常常见的场景就是,“读远多于写的时候”,比如CopyOnWrite系列的,读写锁使用场景,但是什么时候是“远多于”?其实这个并没有一个具体的,通用的数值,很多时候在两个数据结构,甚至两个技术选型的时候,只有进行测试才能判断出是否合适。
虽然上述的这种只有实测才能得到结论的场景不少,但是还是有那么一些参数,做为一个程序猿还是应该知道的。比如,CPU读取内存时间的数量级?机械磁盘与固态磁盘进行顺序读取,顺序读取的速度的数量级?还有些系统级别的数据,在设计的时候也可以作为参考,比如淘宝双十一的最高交易创建峰值32.5万笔/秒,了解了这个参数,作为一个交易平台,就没有必要来设计一个支持超过50万笔/秒的系统了,因为木有必要了。
IO 问题
这里的IO不仅仅指磁盘,还包括网络,缓存等。现代计算机一个很大的问题就是磁盘的速度。目前SSD慢慢流行起来了,但是,也不算什么质的飞跃,对计算机很多情况下的也是瓶颈。
一张很好的图,可能数据不在准确,但是量级应该差不了多少:
图中的单位是ns,纳秒,纳秒上面还有个us,上面才是毫秒ms。
Disk seek的速度,已经是毫秒级别的了,而且是10ms,与内存的地址定位速度100ns比,相差了100w倍。但是,内存虽然寻址快,但是读取速度相对与寻址速度,还是慢的,顺序读取1M的数据,也要250um,顺序读取1G的数据,需要250ms,也就是说,内存读取,大概1s是4G。虽然磁盘的寻址慢,但是读取速度与寻址的比,没那么夸张,顺序读取1M,需要20ms,也就是1秒能有50M的速度,这速度与实际可能有点低(这里可能有点歧义,这里指的读取1M的速度可能包括寻址的时间,如果是这样的话,与目前的磁盘性能很像,1秒100M),7200转的磁盘,顺序读取能有100好几十兆。
也正是由于磁盘寻址的成本很高,而读取的成本相对不高,所以,操作系统会预读取一些数据缓冲区,每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k。
上图并没有说明现代的SSD的特性,固态硬盘(英语:Solid State Disk、Solid State Drive,简称SSD)。在wiki上的一个benchmark:
SSD benchmark, showing about 230 MB/s reading speed (blue), 210 MB/s writing speed (red) and about 0.1 ms seek time (green), all independent from the accessed disk location.
随机的读写有200MB以上的速度,寻址时间只有0.1ms,而机械硬盘一般有10ms。对于数据库而言,引用《高性能MySQL第三版》中p390中的一句话:
最重要的事情是提升随机I/O和并发性
两个综合下,与机械硬盘比,SSD能够提升随机读写的吞吐量。
磁盘最小组织扇区,一般为512byte(新的磁盘有更大的扇区比如4k),文件系统是block划分,比如4k(不同文件系统不同),mysql中的记录存储的最小单元是page(也叫block),为16k。HDFS默认的文件块大小是64M或128M。
系统级别
2017年双11中,阿里巴巴再一次更新了记录:每秒32.5w笔的交易创建峰值、25.6w笔的支付峰值。参考(双11备战核武器:全链路压测今年如何升级?)。
CPU
内存
memory copy,带宽通常在1GB/s级别以上。参考:Java NIO浅析
IO
操作系统
线程本身占用较大内存,像Java的线程栈,一般至少分配512K~1M的空间。这样就是说,如果系统内有1000个线程,那么线程自身的开销就可能有1G之多。参考:Java NIO浅析。过多线程的问题还有另外一个就是context switch的过车消耗的cpu过高,可能超过cpu用于执行线程的时间。
使用NIO != 高性能,当连接数<1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势。参考:Java NIO浅析
参考
https://dev.mysql.com/doc/internals/en/innodb-page-overview.html