Ⅰ、磁盘的访问模式
磁盘性能和磁盘的访问模式有关
-
Sequential access
-
Random access
上面说的顺序和随机是逻辑上的,大多时候做不到完全顺序
数据库不是连续块的申请空间,是一次申请固定块大小的空间,在这一个块里面的数据是连续的
Ⅱ、HDD VS SSD
随机访问的性能叫IOPS,顺序访问的性能叫吞吐率
2.1 简单了解HDD
- 机械磁盘,通过磁头旋转定位,单块盘一般100IOPS
- 数据库的优化都是根据机械磁盘特性,随机转顺序,通过磁头旋转进行数据定位
- 常见转速,笔记本5400r/min,台式机7200r/min,服务器硬盘1w/min,1.5w/min
- 顺序访问性能较好,100M/s/~200M/s,这叫磁盘吞吐率,也叫带宽,可以通过做raid来提升磁盘带宽,可以做到一两个G
- 随机访问性能较差,需要磁头旋转定位,IOPS较低
为什么数据库比较多的是随机访问?
B+ tree索引定位数据是比较随机的,所以数据库最看重的就是随机IO,也就是IOPS,顺序的话通常就是扫描了,两张表做join的话比的就是顺序的性能,MySQL通常用在大并发,小事务的OLTP场景
知道转速,对应的IOPS也出来了
IOPS=转速/60
带宽怎么转换为IOPS
每做一次io操作,io的大小是不一定的,比如4k的操作,通常厂商都给的数据都是4k大小的io,注意,MySQL中每个IO的大小是16k,即每个页的大小,所以和厂商给的性能数据相比会有所下降
48M的宽带算IOPS就是,1M/4k*48=12288
如何提升HDD的IOPS性能
-
做raid
- 功耗非常高
-
通过购买共享存储
- 价格昂贵
- 共享存储,做高可用很方便,但是单点故障太坑爹,就像坐飞机,一般没问题,出了问题基本上就是死
然而提升都是非常有限的
tips:
通常来说数据库做raid10,性能和可靠性都要有保障
2.2 SSD
- 固态硬盘,无磁头,纯电设备,控制器和闪存组成。单块盘7~8wIOPS
- 读写速度非对称,一般来说写比读慢一半,高端ssd是对称的,机械盘也是对称的
- 性能下降,以前的ssd用一段时间性能会急剧下降,现在基本解决了,强烈推荐英特尔的盘
- 设备短命,性能抖动的问题现在也不用担心
- 用MySQL一定要用SSD
SSD与数据库优化
1、磁盘调度算法设置为deadline或者noop,在ssd上,这个调整性能至少提升百分之七十,甚至多倍,现在CentOS7默认磁盘调度算法已经是deadline了而不是cfq
cat /sys/block/vda/queue/scheduler
echo deadline > /sys/block/vda/queue/scheduler
上面这个可以作一个模板,规定,HDD也因该这么设置,只是性能提升没这么大
2、InnoDB参数调整
InnoDB存储引擎参数设置
innodb_flush_neighbors = 0 -- 默认1,平缓刷新的,默认只刷脏页,不会将脏页所在的整个区刷新,把这个设为0,重做日志大小足够大,数据库测试时性能比较平稳,还有一定的提升
innodb_log_file_size = 4G -- 默认128M,高端设备设置8G和16G都不为过,MySQL5.5最大只能设1.9G,无限接近于2G
innodb_io_capacity 设置为存储性能的一半
修改之后重启,等待时间比较长,是数据库在做扩展
上面两个配置不对性能抖动比较厉害,设置后性能平稳且提升百分之15
3、SSD的选择——PCIE or SATA/SAS?
SATA/SAS与PCIE的性能差距逐渐减小,PCIE快非常多,甚至达到50w,100wIOPS,没什么上限
PCIE的性能很少有应用可以完全使用,优先选择SATA/SAS接口的SSD
数据库的瓶颈已经不在磁盘上了
SSD品牌推荐:Intel、FusinIO、宝存
Ⅲ、磁盘调度算法介绍
3.1 CFQ
- CFQ把I/O请求按照进程分别放入进程对应的队列中,所以A进程和B进程发出的I/O请求会在两个队列中。而各个队列内部仍然采用 合并和排序 的方法,区别仅在于,每一个提交I/O请求的进程都有自己的I/O队列
- CFQ的“公平”是针对进程而言的,它以时间片算法为前提,轮转调度队列,默认从当前队列中取4个请求处理,然后处理下一个队列的4个请求。这样就可以确保每个进程享有的I/O资源是均衡的
- CFQ的缺点是先来的IO请求不一定能被及时满足,可能出现饥饿的情况
3.2 Deadline
- 同CFQ一样,除了维护一个拥有合并和排序功能的请求队列以外,还额外维护了两个队列,分别是读请求队列和写请求队列 ,它们都是带有超时的FIFO队列,当新来一个I/O请求时,会被同时插入普通队列和读/写队列,然后处理普通队列中的请求,当调度器发现读/写请求队列中的请求超时的时候,会优先处理这些请求,保证尽可能不产生请求饥饿
- 在DeadLine算法中,每个I/O请求都有一个超时时间,默认读请求是500ms,写请求是5s
3.3 Noop
- Noop做的事情非常简单,它不会对I/O请求排序也不会进行任何其它优化(除了合并),Noop除了对请求合并以外,不再进行任何处理,直接以类似FIFO的顺序提交I/O请求
- Noop面向的不是普通的块设备,而是随机访问设备(例如SSD),对于这种设备,不存在传统的寻道时间,那么就没有必要去做那些多余的为了减少寻道时间而采取的事情了
Ⅳ、存储结构对应关系
+-------------+-------------+-------------+
Database | 16K | 16K | 16K |
+------+------+-------------+-------------+
|
+------------------------------------------------------------------------+
|
+------+
|
+------+------v------+------+
Filesystem | 4K | 4K | 4K | 4K |
+---+--+------+------+------+
|
+------------------------------------------------------------------------+
|
+--+
|
v
+------+------+ +------+
Disk | 512B | 512B | ... ... | 512B |
+------+------+ +------+
SSD扇区的大小一般为4K或者8K,但是为了兼容HDD,SSD通过Flash Translation Layer(FTL)的方式转换成512B
4.1 一个参数——innodb_flush_method
- fwrite:把数据写入文件系统层(Filesystem)(可能有cache),并不能保证写入Disk
- fsync:保证把数据写入到Disk(数据落盘)
- O_DIRECT:系统直接将数据写入磁盘,跳过文件系统的缓存,等同于使用裸设备的效果
+-------------------+ +-------------------+ +-------------------+
| | fwrite | | fsync | |
| Buffer Pool +---------------> Filesystem Cache +--------------> Disk |
| | | | | |
+--------+----------+ +-------------------+ +---------+---------+
| ^
| |
| innodb_flush_method = O_DIRECT |
+-----------------------------------------------------------------------+
只通过fwrite写入数据特别快(因为有缓存),但随后调用fsync就会很慢,这个速度取决于磁盘的 IOPS
如果不手工执行fysnc,当Filesystem的cache小于10%时,操作系统才会将数据刷入磁盘,所以可能存在数据丢失的风险,比如掉电
4.2 写数据的过程
OS pagecache(操作系统层的)
filesystem 一个块一个块,每个块大小4k
DISK 一个4k由8个扇区(sector)组成,一个sector大小512字节
fwrite/fread,最底层的system core中用fopen打开文件:f=fopen('ibdata',wb)
打开之后对应一个文件句柄f,对文件句柄f操作:fwrite(f,offset,data,len)
offset就是偏移量,data是写入的数据,len是字节数
数据库都是16k一个块,对于数据库来说,它的offset=16k*N
fwrite(f,0,xxx,16384);
fwrite(f,16384,xxx,16384);
写就是覆盖操作,如果追加在最后就是占用空间,不会有碎片,数据库里会先申请空间全部填0,对于数据库来说,offset大小肯定和块大小对齐,offset必然是块大小的倍数
仅仅调用fwrite函数,表示数据只写入到了pagecache(操作系统缓存中),并没落地到磁盘,这时候服务器挂了,就有问题了,所以还要调用一个fsync函数刷新句柄,fsync表示直接刷新到磁盘
只通过fwrite写入数据特别快(因为有缓存),但随后调用fsync就会很慢,这个速度取决于磁盘的IOPS,如果不手工执行fysnc,当Filesystem的cache小于10%时,操作系统才会将数据刷入磁盘,所以可能存在数据丢失的风险,比如掉电
4.3 写的比较乱,做个小结
首先明确一点,不管走不走os缓存,对于数据库来说fsync肯定是要一直刷的
fsync非1的话,性能是会好一些,但是性能会有抖动(隔段时间刷新,或者设为0的时候不主动fsync,os会控制这个刷新频率),反正是不用嘛
linux内核2.4版本开始,api函数提供了一个叫o_direct函数,让fwrite直接写到磁盘而不经过pagecache,就是fopen的时候用o_direct参数
这种情况比走os缓存性能稍微要有点差别,但是为了保证数据库的安全我们肯定还是选择直接刷到盘上,而且这个走os缓存性能好的一个原因是用了额外的内存,我们可以把这部分内存分配给buffer pool
其实,如果不直接刷盘,对于数据库来说多做了一次额外的缓存,bp+pagecahe,完全没必要
综上所述:
innodb_flush_method = O_DIRECT
如果没这个参数,一开始MySQL跑出来性能非常好,但是这肯定是不对的
tips:
哪些写入是o_direct,哪些是pagecache
- 数据文件----o_direct
- 日志文件----pagecache,fsync,一个日志文件可能很大,只有一个事务完全提交才fsync一次,不用每次写都fsync
4.4 另一个参数——innodb_io_capacity
数据库每秒写入能用到的IOPS是多少
调整这个值,先观察线上业务数据库IOPS比例,然后测试用--file-rw-ratio 测出这个IOPS,通常来说设置为整个IOPS的一半
有人说互联网业务数据库读写比是10:1,所以这个参数设置为IOPS的十分之一,然而并不是,我们说的数据库的读写比例,是指数据库内部是10:1,很多操作被cache起来了,真实的发生在磁盘上的大多数读写比例是1:1,甚至很多场景写比读还多
现在写设2w,读用2w,单块ssd就能达到了,设置不好就是花钱买冤枉