关于ssd存储原理的介绍
众所周知SSD的读写速度远比hdd磁盘要快,理解ssd的工作原理使我们开发处高效储存方案。
linux 相关指令
fstrim --fstab --verbose ## 回收(discard)文件系统上对应磁盘未使用的块 blkdiscard /dev/nvme1n1 ## 回收并擦除(discard)整个SSD块设备 wipefs -a /dev/nvme1n1 ## 擦除(erase)文件系统的签名
fstrim
fstrim命令实际可以视为手动对SSD磁盘下发TRIM指令。使用-v参数可以直接查看TRIM回收擦除空间的大小。fstrim是针对已挂载的文件系统的SSD分区
root@xxxx:~# fstrim --help Usage: fstrim [options] <mount point>Discard unused blocks on a mounted filesystem. Options: -a, --all trim all supported mounted filesystems -A, --fstab trim all supported mounted filesystems from /etc/fstab -o, --offset <num> the offset in bytes to start discarding from -l, --length <num> the number of bytes to discard -m, --minimum <num> the minimum extent length to discard -v, --verbose print number of discarded bytes -n, --dry-run does everything, but trim -h, --help display this help -V, --version display version For more details see fstrim(8).
以下是执行后返回的结果,以NVMe 为列
/home: 32.5 GiB (313011310592 bytes) trimmed on /dev/mapper/gat204--vg-root /boot/efi: 102.2 MiB (607301632 bytes) trimmed on /dev/nvme1n1p1 /boot: 732.5 MiB (825778176 bytes) trimmed on /dev/nvme1n1p2 /: 60.7 GiB (65154805760 bytes) trimmed on /dev/mapper/gat204--vg-swap_1
ubuntu与debian的systemd 服务可以定时执行fstrim,省去手写crontab脚本工作。
systemctl status fstrim.timer ##查询服务状态 systemctl enable fstrim.timer ##开启定时TRIM功能
blkdiscard
blkdiscard用于擦除(discard)SSD设备扇区,和fstrim不同这条命令直接用在块设备上,默认擦除整个块设备的所有扇区。
root@xxxx:~# blkdiscard -help Usage: blkdiscard [options] <device>Discard the content of sectors on a device. Options: -o, --offset <num> offset in bytes to discard from -l, --length <num> length of bytes to discard from the offset -p, --step <num> size of the discard iterations within the offset -s, --secure perform secure discard -z, --zeroout zero-fill rather than discard -v, --verbose print aligned length and offset -h, --help display this help -V, --version display version For more details see blkdiscard(8). root@ECSab169d:~# man blkdiscard
擦除(discard)成功后,没有返回结果
root@xxxx:~# blkdiscard /dev/nvme1n1 root@xxxx:~#
wipefs
wipefs是linux自带的程序,用来擦除(erase)文件系统的签名,不会清空文件系统或设备中的任何其他数据。默认情况下, wipefs 不会擦除非整个磁盘设备上的嵌套分区表。为此,需要—force选项。
root@gat204:~# wipefs --help Usage: wipefs [options] <device>Wipe signatures from a device. Options: -a, --all wipe all magic strings (BE CAREFUL!) -b, --backup create a signature backup in $HOME -f, --force force erasure -i, --noheadings don't print headings -J, --json use JSON output format -n, --no-act do everything except the actual write() call -o, --offset <num> offset to erase, in bytes -O, --output <list> COLUMNS to display (see below) -p, --parsable print out in parsable instead of printable format -q, --quiet suppress output messages -t, --types <list> limit the set of filesystem, RAIDs or partition tables -h, --help display this help -V, --version display version Available output columns: UUID partition/filesystem UUID LABEL filesystem LABEL LENGTH magic string length TYPE superblok type OFFSET magic string offset USAGE type description DEVICE block device name For more details see wipefs(8).
检查SSD是否支持TRIM
##可以通过 /sys/block 下的信息来判断 SSD 支持 TRIM, discard_granularity 非 0 表示支持。 # cat /sys/block/sda/queue/discard_granularity 0 # cat /sys/block/nvme0n1/queue/discard_granularity 512
存储元器件(闪存颗粒类别)
SSD的储存是NAND-Flash闪存颗粒,分为SLC、MLC和QLC四种。可以粗略地把闪存颗粒理解成是一个电容加上电压计的组合。一个电容能存放一个比特的数据,电压计使电容能区分不同电压,不同的电压能存放更多的比特数据。
SLC(Single-Level Cell): 每个Cell单元存储1bit信息,也就是只有0、1两种电压变化,结构简单,电压控制也快速,反映出来的特点就是寿命长,性能强,P/E寿命在1万到10万次之间,但缺点就是容量低而成本高.
MLC(Multi-Level Cell): 每个cell单元存储2bit信息,需要更复杂的电压控制,有00,01,10,11四种变化,这也意味着写入性能、可靠性能降低了。其P/E寿命根据不同制程在3000-5000次不等.
TLC(Triple-Level Cell):每个cell单元存储3bit信息,电压从000到001有8种变化,容量比MLC再次增加1/3,成本更低,但是架构更复杂,P/E编程时间长,写入速度慢,P/E寿命也降至1000-3000次,部分情况会更低.寿命短只是相对而言的,通常来讲,经过重度测试的TLC颗粒正常使用5年以上是没有问题的.
QLC(Quad-Level Cell):QLC或者可以叫4bit MLC,电压有16种变化,但是容量能增加33%,就是写入性能、P/E寿命与TLC相比会进一步降低。具体的性能测试上,美光有做过实验。读取速度方面,SATA接口中的二者都可以达到540MB/S,QLC表现差在写入速度上,因为其P/E编程时间就比MLC、TLC更长,速度更慢,连续写入速度从520MB/s降至360MB/s,随机性能更是从9500 IOPS降至5000 IOPS,损失将近一半.
这四类当中,SLC的性能最优,价格超高;MLC性能够用,价格适中为消费级SSD应用主流;TLC综合性能最低,价格最便宜,但可以通过高性能主控、主控算法来弥补、提高TLC闪存的性能;QLC出现的时间很早,价格便宜,容量大。
P/E以及其SSD底层储存结构
P指的是Program(编程),E指的是Erease (擦除), 闪存完全擦写一次可以称为1次P/E,因此闪存的寿命以P/E为单位。和HDD机械硬盘不同,HDD的数据是可以覆写的(Overwrite),而SSD在写入数据前必须先进行擦除(erase),一般在格式化文件系统步骤或者SSD出厂配置的过程中,SSD已进行了全盘擦除(erase),因此SSD的首次写入数据是直接编程。
SSD 在闪存单元中存取数据时有 page 和 block 的概念。SSD 被划分成很多 block, 而 block 被划分成很多 page。
NAND-Flash读写流程
Page是NAND-Flash单次读写单位,大小一般为4K或者4K字节的倍数,写操作只能写到空的 page,而清除数据(Erase) 是以 块(block) 为单位的。块的擦除次数有寿命限制,超限制就会变成坏块。
用户对SSD的写入数据操作可以分为两种类型
1.原来SSD磁盘上没有数据,写入数据。
2.SSD磁盘上有数据,对该数据进行修改(包括删除)。
前者只需把数据直接写入到空白页即可,后者则是read-modify-write方式操作,读取原有page的内容到缓存中并进行更新,最后写入到其他空的page,原有的page置为无效页。
可以想象对文件的持续反复的修改,将会产生大量的无效页,这就需要“垃圾回收”(Garbage Collection-gc)机制来回收这些无效页,否则可以写入空间越来越小。
FTL 和磨损均衡
SSD的主控执行磨损均衡(Wear-Leveling)策略,使SSD磁盘各个块的擦除次数均匀分摊到各个块上。就像内存MMU一样,SSD内部使用闪存转换层(FTL)存放了逻辑块地址(Logical Block Address,简称 LBA)到物理块地址(Physical Block Address,简称 PBA)的映射。操作系统访问的硬盘地址,其实都是逻辑地址。只有通过 FTL 转换之后,才会变成实际的物理地址,找到对应的块进行访问。操作系统本身,不需要去考虑块的磨损程度,只要和操作机械硬盘一样来读写数据就好了。
“垃圾回收”机制
写入放大(write amplification)
上文提及过数据的反复修改会产生大量的无效页,一旦整个块(block)的空间不足以写入数据,SSD会将这个块(block)的数据读入到缓存中,擦除这个块(block)所中的页,然后再把缓存中已更新的数据写入进去。这种read-erase-modify-write过程,就好比写入的数据可能只有一个页4KB,但实际要擦除并且写N个页,称之为写入放大。
写入放大的倍数越大,写入的速度就越慢。
TRIM指令
TRIM是SSD的ATA-8指令,解决写入放大的关键。
文件系统在修改或者删除过程中,发送给通知给SSD记录产生的无效页,间隔一定时间再统一回收擦除所有无效页,擦除更新无效页所在的块(block)。
一方面预留足够多的空间,避免因空间不足产生写入放大的情况。另一方面,使用TRIM,在IO闲时回收擦除无效页,这样有效保证SSD的性能以及提高寿命。
discard与TRIM的区别
在linux术语中,discard指的就是TRIM
不建议使用linux系统默认的TRIM功能
TRIM功能有两种方式启动,一种是连续TRIM,就是直接在文件系统回收块的时候直接发TRIM命令,这种方式对性能影响比较大,在fstab挂载的时候把default修改成discard。另外一种是定期执行fstrim批量进行TRIM操作,这样避免平时的性能影响,不过fstrim的执行时机要选好,毕竟批量TRIM的时候会对其它任务性能影响较大。
根据文章《Ubuntu Doesn’t TRIM SSDs By Default: Why Not and How To Enable It Yourself》提及到
“The kernel implementation of realtime trim in 11.2, 11.3, and 11.4 is not optimized. The spec. calls for trim supporting a vectorized list of trim ranges, but as of kernel 3.0 trim is only invoked by the kernel with a single discard / trim range and with current mid 2011 SSDs this has proven to cause a performance degradation instead of a performance increase. There are few reasons to use the kernels realtime discard support with pre-3.1 kernels. It is not known when the kernels discard functionality will be optimized to work beneficially with current generation SSDs.” [Source]
利用内核方式的discard 方式无法感知对SSD当前性能的影响。
实践
使用fio测试nvme裸设备
使用fio对裸设备直接进行randwrite测试,在超过30分钟速度由400MiB/s降低至80MiB/s 分析原因得出SSD触发了放大写现象,并且由于没有挂载文件系统,无法使用fstrim手动回收空间(可以理解成,在没有文件系统标记下,SSD也不知道哪些是无效页),再次进行fio测试速度依然是80MiB/s。使用blkdiscard进行全盘擦除后,速度恢复正常。
参考文献
《Trim命令》 wiki百科
《浅谈分布式存储之SSD基本原理》滴滴云
《Linux 下启用 SSD TRIM 功能》Louis
结束语
当使用fio直接ssd磁盘进行写入测试后,对磁盘使用blkdiscard可恢复原来的速度。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
面试题:JVM在Java堆中对对象的创建、内存结构、访问方式
可柔可刚,点赞则柔,白嫖则刚!死鬼~~~看完记得给我来个三连哦! 一、对象创建过程 1、检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载、解析、初始化,若没有,则进行类加载 2、为新对象分配内存 类加载检查后,JVM为新对象在堆内存中分配空间,内存大小在类加载完成后便可确定。内存分配方式有以下几种: 1)指针碰撞(Bump the Pointer):若堆内存规整的,已用的和空闲的各占一边,分配内存就是把指针作为分界点,指针往空闲的一边移动对象大小的空间。 2)空闲列表(Free List):若堆内存不规整,JVM必须维护一个记录可用内存块的列表,分配内存时,把列表中一块空间分配给对象,并更新表记录。 以上两种在并发情况下,存在线程安全问题,在给对象A分配内存时,指针还没来得及修改,对象B又同时使用原来的指针来分配内存。解决方案有两种: 1)给分配内存的动作同步处理:JVM使用CAS+失败重试,保证更新操作的原子性。 2)本地线程分配缓冲(TLAB Thread Local Allocation...
- 下一篇
iOS开发逆向之循环选择指针(下)
本文主要讲解Switch的汇编代码 Switch 1、假设switch语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else 2、各个分支常量的差值较大时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else的结构 3、在分支比较多的时候,在编译的时候会生成一个表,不同的case通过跳转表的不同地址,每个地址占四个字节。 案例分析 首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130595548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!) 1、当case有3个时 voidfunc(inta){switch(a){case1:printf("打坐");break;case2:printf("加红");break;case3:printf("加蓝");break;default:printf("啥也不干");break; } }intmain(intargc,char*argv[]){ ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 2048小游戏-低调大师作品
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果