如何在现代硬件架构下构建一个高性能的服务器软件
从方法论上,可以考虑自底向上(充分利用硬件的特性)和自顶向下(从应用本身的特点考虑)两个维度来思考
自底向上
- 硬件层
- 增加程序并行程度,充分利用多核效率
- 减少RISC架构下分支预测失败的概率(intel cpu也是采用了指令流水线和分支预测技术)
- cpu cache友好性,高性能的cache置换算法,cache遗忘算法(比如矩阵乘法等)
- 外部存储化随机为有序,高效的外部排序算法、高效的索引
- 浮点运算采用专有GPU或者FPGA硬件
- 经常做的简单运算固化到ASIC中
- 网络方面优化,根据应用的特点,裁剪和定制传输层的丢包重传和拥塞控制的策略,有必要可以考虑offload到硬件(IB、FC)
- 考虑NUMA架构下的一些优化,线程、中断bond核
- 操作系统
- 尽量采用多线程,减少操作系统级别的资源消耗,例如页表数量,TLB切换等
- 采用自旋锁、读写锁、顺序锁、RCU锁等技术,减少临界资源访问带来的上下文切换的损耗
- IO异步化,通信链路复用,libaio,epoll,spdy,http2.0
- zero copy发包,senfile,DPDK
- 物理资源的延时分配,copy on write,fork
- 减少fsync的次数
- 编程语言
- 多采用协程,特别是并发rpc的场景,应用程序自己控制上下文切换的时机
- 高效的容器技术,ring buffer,无锁的container
- 高效的Gargage Collector
- 高效的线程池管理
- 高效的IO lib/框架,涉及IO的操作全部要异步化
- 选择适合自己的内存/外部索引
自顶向下
- 应用场景
- IO密集or计算密集or混合型
- 数据在内存是否全部放的下
- 热点数据明显吗
- latency和through put之间的trade off
- 对数据的一致性有要求么
- 不同业务之间,是否需要隔离,逻辑数据的隔离,还是物理资源隔离
- 数据本身的特点,重复率高么(压缩),热点明显么(缓存)
总结
现代服务器的体系结构主要特点:
- 一种共享内存的分布式系统
- 内存很大,但是数据量更大,需要大量的网络IO和磁盘IO
现代服务器软件设计中需要考虑很多复杂的问题,用到了很多数据结构和算法,在本科生的课本中并未涉及,往往采用比较底层的开发语言,门槛比较高。
根据应用场景和难易程度,划分为以下几种
- level 1
- 网络IO密集型:
- 不需要持久化,无状态,对计算消耗不高,对一致性要求不高,比较典型的有web服务器nginx、web app容器等
- 不需要持久化,无状态,对计算消耗不高,对一致性要求不高,比较典型的有web服务器nginx、web app容器等
- 网络IO密集型:
- level 2
- 磁盘IO密集型
- 需要持久化,有状态,有一定的计算消耗,简单的一致性支持,比较典型的有消息队列,各种kv store,BDB,leveldb,redis
- 磁盘IO密集型
- level 3
- 混合密集型
- 需要持久化,有状态,计算密集,严格的事务要求,需要考虑逻辑隔离和物理隔离,例如传统的关系数据库oracle、postgresql、mysql等
- 混合密集型
排除开发语言和工具带来的额外困难,操作系统内核的现代文件系统和SDN部分,最多达到level2 级别;不喜勿喷,呵呵。
另外,说到这里,从软件工程的角度看,如果一个软件模块的复杂程度过高,就不应该放在操作系统内核来实现。笔者遇到过一个内核NFS模块的性能问题,完美的解决方案是NFS模块针对inode管理功能,需要实现一个现代版本的GC,但是内核中有必要实现GC么,内核的VM貌似有了,呵呵
题外话
可以出版一门大部头学术专著的技术话题:
- 索引
- 内存池管理
- 无锁化容器