磁盘I/O 优化
性能检测
应用程序通过访问磁盘来读取数据,而磁盘I/O 通常都是很耗时间的,所以一般我们来判断I/O是否有瓶颈的时候,就需要一些参数指标来参考。
- WAIT 指标 :压测应用程序,查看系统I/O wait 指标是否正常,如果测试机器有4个CPU ,那么理想的I/O wait 参数不应该超过25% ,如果超过了25% ,那么就很可能成为程序的性能瓶颈,在Linux 下,可以通过iostat 命令查看。
- IOPS(每秒读取次数) : 查看应用程序最低要求的IOPS 是多少,磁盘的IOPS 能不能达到要求。 每个磁盘的IOPS通常都在一定的范围内,当然这个和存储在磁盘上的数据块大小和访问方式相关。但主要是由磁盘的转速决定的。转速越高,则磁盘IOPS 越高。
提升I/O性能
通常提升I/O性能的方法有:
- 增加缓存,减少磁盘访问次数。
- 优化磁盘管理系统,设计最优磁盘方式策略,和磁盘的寻址策略,这是从底层操作系统层面的考虑。
- 设计合理的磁盘存储数据块,以及访问的策略。这是从应用层方面考虑的,比如给存放的数据加索引,通过寻址索引来加快和减少磁盘的访问量,以及异步和非阻塞的方式来加快磁盘访问速度。
- 当然我们现在通常采用一种叫做 RAID(磁盘阵列)的技术。 就是将不同的磁盘组合起来以提高I/O性能,现在有多种RAID 技术,每种RAID 技术对I/O性能的提升也不同。 可以用一个RAID 因子来代表, 通过磁盘的读写吞吐量 可以通过iostat 命令来获取,所以就可以计算出理论的IOPS 值 。 公式可以写作:
(磁盘数 * 每块磁盘的IOPS)/(磁盘的吞吐量 + RAID因子 * 磁盘读写的吞吐量)=IOPS 这个公式的详情请自行百度。
RAID 策略 以及说明
磁盘阵列 | 说明 |
RAID 0 | 数据被平均写到多个磁盘阵列中,写和读数据都是平行的,所以磁盘的IOPS可以提升一倍 |
RAID 1 | RAID 1 的主要作用是能够提高数据的安全性,它将一份数据分别复制到多个磁盘阵列中,并不能 提升IOPS ,但是相同的数据有多个备份。通常用于对数据安全性较高的场合中。 |
RAID 5 | 这种设计方式是前两种的折中方式,它将信息平均写到所有磁盘阵列总数减一的磁盘中,往另外一个磁盘写入这份数据的奇偶检验信息。如果其中有一个磁盘损坏,就可以通过其他磁盘的数据和这个数据的奇偶检验信息来恢复这份数据。 |
RAID 0+1 | 就如名字一样,根据数据的备份情况进行分组,一份数据同时写到多个备份磁盘中,同时多个磁盘也会进行读写。 |
TCP网络参数调优
我们知道要建立一个TCP连接,就必须要知道对方的IP 和一个未被使用的端口号,由于32位操作系统的端口号通常是由两个字节表示,所以就只有2^65535个端口号。所以说,一台主机能够建立的连接是有限的。还有 0~1024 端口是受保护的,像80,22,21 这些端口都不是能够被随意占用的。
在Linux 中 我们通过查看 /proc/sys/net/ipv4/ip_local_port_range 文件来查看当前能够使用的端口范围, 如果可分配的端口较少,在遇到大量的并发请求的时候就会成为瓶颈。由于端口有限导致大量的请求等待连接,这样性能就会压不上去。 另外 如果发现有大量的TIME_WAIT 的话,可以设置 /proc/sys/nettcp_fin_timeout 为更小的值来快速释放请求。
网络参数 | 说明 |
echo "1024 65535">/proc/sys/net/ipv4/ip_local_port_rang | 设置向外连接可用端口范围 |
echo 1 >/proc/sys/net/ipv4/tcp_tw_reuse | 设置 time_wait 连接重用 |
echo 1 >/proc/sys/net/ipv4/tcp_tw_recycle | 设置快速回收 time_wait 连接 |
echo 180000 >/proc/sys/net/ipv4/tcp_max_tw_buckets | 设置最大time_wait 连接长度 |
echo 0 > /proc/sys/net/ipv4/tcp_timestamps | 表示是否启用以一种比超时重发更精确的方法来启用对RTT的计算 |
echo 1>/proc/sys/net/ipv4/tcp_window_scaling | 设置TCP/IP会话的滑动窗口大小是否可变 |
echo 20000 >/proc/sys/net/ipv4/tcp_max_syn_backlog | 设置最大等待处于客户端还没有应答回来的连接数 |
echo 10000 >/proc/sys/net/core/somaxconn | 设置每一个处于监听状态的端口监听队列的长度 |
echo 10000 > /proc/sys/net/core/netdev_max_backlog | 设置最大等待CPU处理的包的数目 |
echo 2000000>/proc/sys/fs/file-max | 设置最大打开文件数 |
echo 15>/proc/sys/net/ipv4/tcp_fin_timeout | 设置FIN-WAIT-2状态等待回收时间 |
echo 16777216 >/proc/sys/net/core/rmem_max | 设置最大的系统套接字数据接收缓冲大小 |
echo 262144 > /proc/sys/net/core/rmem_default | 设置默认的系统套接字数据接收缓冲大小 |
echo 16777216 >/proc/sys/net/core/wmen_max | 设置最大的系统套接字数据发送缓冲大小 |
echo 262144 >/proc/sys/net/core/wmen_default | 设置默认的系统套接字数据发送缓冲大小 |
echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_rmem | 设置最大的TCP 数据发送缓冲大小,三个值分别是 最小,默认,和最大值 |
echo "4096 65535 16777216"/proc/sys/net/ipv4/tcp_wmem | 设置默认的TCP数据接收缓冲大小,三个值分别是 最小,默认,和最大值 |
调优参数
以上的设置都是临时的,系统重启之后会自动丢失, Linux 还提供其他查看当前TCP 统计的信息
- cat/proc/net/netstat : 查看TCP的统计信息。
- cat/proc/net/snmp : 查看当前系统的连接情况。
- netstat -s : 查看网络的统计信息。
网络I/O优化
网络I/O 优化的基本处理原则。
- 减少网络交互的次数。 要减少网络交互的次数,通常需要在网络交互的两端设置缓存。像Orcle的jdbc驱动程序就提供了对查询的SQL结果的缓存,在客户端和数据库端都有,可有效的减少对数据库的访问。 还有个方法,就是合并访问请求。将多个请求合并到一个包中,后面再打包返回。 比如访问页面时,通常会有多个JS或CSS 文件,我们可以将多个JS 文件合并在一个HTTP链接中,每个文件用逗号隔开,然后发送到后端的Web服务器,根据这个URL链接再拆分为各个文件,最后打包一并返回给前端浏览器。 这些都是减少网络I/O的方法。
- 减少网络传输数据量的大小。 通常是将数据压缩后再传输,以及通过设计简单的协议,尽量通过读取协议头来获取有用的价值信息
- 尽量减少编码。在网络I/O中,数据传输都是以字节形式进行的,所以通常要进行序列化。但是我们发送到数据都是以字符形式的,所以必须要经过编码,这个过程是非常耗时的。所以在要经过网络I/O传输时,尽量以字节形式发送,提前将字符转化为字节。减少传输过程中,从字符到字节的转化过程。
交互场景
同步与异步
同步:
同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。 同步是一种很可靠的任务序列,要么都成功,要么都失败。
异步:
将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。 异步 是一种不可靠的任务序列。
阻塞与非阻塞
阻塞与非阻塞主要是从CPU的消耗上来说的,阻塞就是CPU停下来等待一个慢的操作完成以后,CPU 才接着完成其他工作。 非阻塞就是在这个慢的操作执行时,CPU去做其他地方工作,等这个操作完成时CPU再接着完成后续操作。 虽然从表面上看,非阻塞的方式可以明显的提高CPU的利用率,但是也带来另一种结果,就是系统的线程切换增加。增加的CPU 使用时间能不能补偿系统的切换成本就需要好好的评估。
两种方式的组合
组合的方式有四种,分别是同步阻塞,同步非阻塞,异步阻塞,异步非阻塞,四种方式都对I/O性能有影响。
组合方式 | 性能分析 |
同步阻塞 | 最常用的一种用法,使用也是最简单的,但是I/O性能一般很差,CPU 大部分处于空闲状态 |
同步非阻塞 | 提升I/O性能的常用手段,就是将I/O阻塞改成非阻塞的方式,尤其是在网络I/O是长连接同时传输数据也不是很多的情况下,提升性能非常有效。这种方式通常能提升I/O性能,但是会增加CPU 消耗,要考虑增加的I/O性能能不能补偿CPU 的消耗,也就是系统的瓶颈是在CPU上还是I/O上。 |
异步阻塞 | 这种方式在分布式数据库上经常用到,比如,在一个分布式数据库中写一条记录,通常会有一份是同步阻塞的的记录 ,还有2~3份备份记录会写到其他机器上,这些备份记录通常都采用异步阻塞的方式写I/O异步阻塞对网络 I/O 能够提升效率,尤其是像上面说的,能够同时写多份相同的数据的情况。 |
异步非阻塞 | 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,集群之间的消息同步机制一般都采用这种I/O 组合形式,它适合同时要传很多份相同的数据到集群中不同的机器。同时数据的传输量虽然不大,却非常繁琐的情况,这种网络I/O用这种方式性能能达到最高。 |
四种组合方式以及性能分析
虽然异步和非阻塞能够提升I/O 的性能,但是也会带来一些额外的性能成本,比如:会增加线程数量从而增加CPU的消耗,同时也会导致程序设计复杂度的上升。如果设计得不好,反而会导致性能下降。所以在实际应用时要根据应用场景综合评估。