SSD (固态硬盘)

此页由Linux Wiki用户Zzjzzpgg12于2023年11月4日 (星期六) 09:10的最后更改。 在郭枫Tom Lifunicorn和Linux Wiki用户Chenxing的工作基础上。

出自Linux Wiki

Note.gif
谨慎转载:
虽然您有在保留本文链接的条件下转载本文的权利,但考虑到转载不当可能破坏文档排版,且难以实时展现文档的最新版本,请在转载前慎重考虑。如果您发现文本内容有错误或不足,可以登录后直接更正或改进;如果不确认应如何改进,也可以使用 TODO 模板留下标记。

SSD(Solid State Drive,固态硬盘)以其读写速度快、寻址快、无机械移动部件、无噪声等突出优点日益受到人们的青睐。由于其工作原理与传统的磁盘完全不同,故在操作系统和应用程序层面都有对其做专门优化的空间。

值得一提的是,SSD硬盘无需任何特殊配置就可很好地应用在桌面环境上。本文介绍的配置可以改善SSD的性能和使用时间,但未必十分必须。

目录

原理分析

SSD硬盘之所以需要特别优化系统配置,主要是由其特性决定的:

写入方式
向SSD硬盘写入数据时,不能像写入普通硬盘那样直接覆盖,而是要先擦除、再写入。不幸的是,由于设计的原因,虽然写入操作可以以(page,常为 4KB)为单位,擦除操作的最小单位一般是(block,常为 512KB)。[1]如果想保留块中的其它数据,需要在擦除前要先读出该块的数据到SSD缓存中(这也是SSD缓存一般较传统磁盘大很多的原因之一),并在修改后写回。所以每一次写操作实际写到硬盘上的数据很可能原来计划比要写的数据多,这就是所谓的 Write amplification 效应。这是SSD的读取比写入快得多的原因之一。
存储单元损耗 (Wearing)
SSD的每一个存储单元被擦除、写入的次数是有限的。市面上常见的使用 MLC (Multi-Level Cell) 技术的SSD,单个存储单元只能被擦除、重写几千次,而采用相对昂贵的 SLC (Single-Level Cell) 的SSD也有几万到几十万次的擦除寿命[2]。这是很多人对 SSD 硬盘不放心的因素之一,但实际上得益于耗损平均技术,该问题对普通用户的影响并不显著。
耗损平均技术 (Wear Leveling)
虽然每个存储单元的寿命有限,但每个物理存储单元对应的逻辑地址没必要是一成不变的。通过将要写入的数据动态地映射到不同的物理存储单元,SSD的寿命可以得到明显的提升[3]。如一个 64GB 的 SSD 设备,如果每个单元可被擦除、重写3000次,而某桌面用户每天写入 10GB 的数据,该设备出现有存储单元报废需要约 64 * 3000 / 10 / 365 = 52年 。 由于一般来讲桌面应用的写入并没有这么多,所以因为擦除、写入次数过多而导致SSD丢失数据的顾虑是没有什么必要的。一些评测数据[4]和分析文章[5]都支持了这一点。

针对以上特性,在软件方面优化SSD的性能应主要集中在以下几个方面:

  • 提高物理写入的效率,减缓Write amplification
  • 充分利用SSD随机访问快的特性。
  • 减少不必要的硬盘写操作。

与此相关的,涉及到如下两个术语:

分区对齐 (Partition Alignment)
由于SSD向一个块写入数据前要先擦除整个块,如果要写入的一个逻辑块分布在两个物理块中,那么涉及到的两个物理块都要被擦除重写。而如果将分区表和文件系统向物理块对齐,就可以减少这种情况,延长硬盘的寿命。一般SSD的块大小都能整除 512KiB [6],所以将分区的启始逻辑位置设为 512KiB 或 1MiB 的整数倍通常是比较合理的。
TRIM
TRIM 本身是一个 SATA 指令。在删除文件时,文件系统一般只做必要的标记而不真正抹去存储介质上的数据。对于普通磁盘,这就足够了,被标记的部分以后在需要时会被覆盖。而对于 SSD 硬盘,告知硬盘有些块不再被需要是很有意义的,硬盘可以据此优化其垃圾回收过程,加快以后写入数据的速度。[1]

Linux环境配置

由于生产环境的需求通常各不相同,配置难以一概而论,这里主要讨论使用SSD的桌面Linux环境配置。

分区对齐

使用多数现代分区工具分区的硬盘,一般是已经对齐的。如在较新的GParted[7],新建分区的默认对齐方式就是 MiB(有些版本的GParted翻译作“排序”)。

检验是否分区已正确对齐,可以通过 fdisk -l 命令(仅适用于使用MBR格式分区表)或 GParted 获取分区的首扇区,由于逻辑扇区的大小一般是 512 字节,如果首扇区的扇区号是 1024 的倍数,则分区已向 512KiB 对齐,如果首扇区号是 2048 的倍数,则分区已向 1MiB 对齐。如:

$ sudo fdisk -l
Disk /dev/sdb: 96.0 GB, 96029466624 bytes
255 heads, 63 sectors/track, 11674 cylinders, total 187557552 sectors
Units = 扇区 of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00018744

   设备 Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    62629887    31313920   83  Linux
/dev/sdb2        62629888   103606271    20488192   83  Linux
/dev/sdb3       103606272   144742399    20568064   83  Linux
/dev/sdb4       144742400   167077887    11167744   83  Linux

上例中,加粗部分均为 2048 的倍数,说明分区已对齐。如果发现分区不幸未对齐,则可能需要删除分区重新来过了。

如果使用较新的GPT格式的分区表,则需要用gdisk查看。

挂载选项

Hint.gif
提示:
修改挂载选项的具体方法可参见fstab

F2FS (Flash Friendly File System) 是专门为基于 NAND 的存储设备设计的新型开源 flash 文件系统。特别针对NAND 闪存存储介质做了友好设计。F2FS 于2012年12月进入Linux 3.8 内核。

开发中的btrfs正在试图对 SSD 硬盘做专门的优化,用户可以通过-o ssd挂载选项启用[8]

另一个在很多教程中出现的选项是-o discard[6][9][10],该选项可用于 ext4btrfs 文件系统,作用是在删除文件时实时发出 TRIM 指令,以期加快以后写数据的速度。值得注意的是,该指令的效果是受到严重怀疑的[11]。2011年中旬的数据[12]表明, discard 选项非但没有提升 SSD 的效率,反而使其变慢了。Btrfs的-o ssd选项也不会启用discard[8]。 相比之下,定时发送 TRIM 指令可能是更好的选择(见后文)。

noatime(或relatime), nodiratime是另外两个可以考虑的挂载选项,它们防止在读文件时更改文件的“最近访问”时间戳,减少写入硬盘的次数。[6][9]数据显示,采用noatime选项后,在编译Linux内核的过程中,硬盘写入量比没使用时少了约 13% 。[13] 请依照数据自行权衡利弊。值得注意的是,noatime可能会导致某些依赖时间戳的程序产生问题,因此现在通常relatime代替。

I/O 调度方案

默认的I/O调度一般针对磁盘寻址慢的特性做了专门优化,但对于SSD而言,由于访问磁盘不同逻辑扇区的时间几乎是一样的,这个优化就没有什么作用了,反而耗费了CPU时间。[6][14]具体到 Linux 系统中,就是用 Noop 调度器 (直接把所有 I/O 请求送到一个队列中)代替内核默认的 CFQ 调度器。

查看某设备当前使用的调度器的方法是:

$ cat /sys/block/sdX/queue/scheduler
noop deadline [cfq]

而下面的命令可以设置sdX的调度器为noop

echo noop > /sys/block/sdX/queue/scheduler

如想让此成为默认设置,则需要将上面的命令加到自动启动程序的列表中。

定时 TRIM

虽然实时TRIM选项-o discard效果并不好,但定期对已删除的文件做TRIM还是有意义的[12],如:

# 告知硬盘分区"/"中的哪些块已不再被使用
fstrim -v /

推荐使用crontab定时执行该命令。

其它减少SSD写入的方案

减少写入固然可以延长SSD的寿命,但依前述分析,SSD 可写入次数对于桌面用户而言一般是足够的。所以,在考虑采用减少 SSD 写入方案时,要慎重分析其潜在的后果,再做决定。

在tmpfs里编译

有些需要编译安装的软件,在/tmp(通常挂载在内存上)中编译通常是个不错的选择。

挂载需要频繁写入的分区到普通磁盘

有人建议将/var、交换分区等挂载到普通磁盘以减少对 SSD 的写入,但这些操作也会同时导致系统性能的下降。对于桌面用户而言,SSD上的/var一般会大幅包管理工具的性能,而SSD上的交换分区会让系统休眠、唤醒的速度大大加快。

取消文件系统日志

日志文件系统是现代的文件系统的重要特征,在内核崩溃或突然停电等突发事件发生后,有日志的文件系统恢复起来要快得多。取消文件系统日志固然会减少硬盘写入,但也使文件系统的可靠性下降。有实验结果显示[13],关闭了日志的文件系统在编译内核的过程中硬盘写入比未关闭日志的大约少 3% - 6%,在删除文件时少约 40% - 50% (删除文件的写入操作相对而言基数很小)。

所以对于有些教程提议的取消SSD的文件系统日志以减少写入以加快速度[10]的方案,一定要在谨慎分析后决定是否采用。

将浏览器缓存移入内存

由于大量小文件频繁读写会损害SSD硬盘寿命,因此当tmpfs在当前的linux桌面上变得简单易用时,将这些需要频繁读写的文件挂载到一个tmpfs上进行日常操作,既加快了I/O速度,又可以大大降低硬盘读写次数,因此tmpfs成为了固态硬盘的绝佳助手。所谓tmpfs就是被挂载到当前根文件系统下的一段内存空间,它具有特定的访问路径和大小,可以读写,在系统断电后会自动清空。tmpfs在win下有个不错的中文翻译,叫做“内存盘”(ramdisk)。

在一个普通用户的linux桌面上,有一类应用软件几乎每天都要工作很长时间、并且有频繁读写大量小文件的习惯,这种软件被称为浏览器。任何一种浏览器(例如Firefox、Chrome或Opera)都保留了一种工作特性,它会在硬盘的某个路径下建立一份“档案”(profile),然后在运行过程中频繁而不间断的读写档案中的文件,这样的文件大多与浏览器访问过的网页有关,这些文件被称为浏览器缓存。在SSD上运行的浏览器,可能会因为大量读写缓存出现卡顿现象,并且可能影响SSD寿命。因此,如果能够将浏览器的“档案”移入一个tmpfs,让浏览器在内存中读写档案文件和缓存,就可以大大减轻SSD的负担。

原则上,将浏览器的profile移入tmpfs是一件很简单的任务,只需要先建立一个tmpfs分区,然后将浏览器的profile拷贝到tmpfs分区即可。然而tmpfs中的文件只驻留在内存中,系统断电后这些文件就会被清空。很显然,如果要保留对浏览器profile所做的修改,就必须适时对这些文件进行备份。挂载一个tmpfs、让profile在tmpfs中工作、适时备份profile,这三个步骤就可以完美实现上面的计划。而这三个步骤在一个很有名的脚本中已经完成了,这个脚本叫做profile-sync-daemon(psd)。

psd是一段让多种浏览器的profile自动工作在tmpfs中并且定期备份的bash脚本。psd的配置非常简单,它需要一个运行脚本(profile-sync-daemon),一个启动服务文件(psd)和一个服务配置文件(psd.conf)。此外,psd在archlinux和gentoo中被扩充我为完整的软件包,并且也存在debian和其衍生发行版的deb包。psd支持firefox、chrome和opera等主流的浏览器,安装启动后自动在后台运行,每隔一小时对profile进行备份,daemon运行方式可以保证在关机或重启电脑之前完成备份(daemon脚本的优点之一)。事实上psd并非只适合固态硬盘,由于可以加快浏览器运行速度,psd也运行于很多非SSD的linux桌面。

Note.gif
TODO:
普通桌面环境每天浏览器写入的缓存大概几何?是否值得将其移入内存?
Note.gif
TODO:
参考文献,命令行示例

状态检查

如果对自己的SSD使用方式不放心,可以使用gsmartcontrol等工具,通过S.M.A.R.T.查看硬盘的状况。一般ID 5 对应的 Retired Block Count (或作 Reallocated Sector Count)[15] 反映了SSD已损坏的存储介质的情况。 Intel的SSD还提供233 Media_Wearout_Indicator,用一个整数来直观反映SSD的老化状况;以及225 Host_Writes_32MiB,反映已经向盘中写入了多少数据。 [16]

 # smartctl -data -A /dev/sda
 smartctl 6.0 2012-10-10 r3643 [x86_64-linux-3.9.0-e-nvidia] (local build)
 Copyright (C) 2002-12, Bruce Allen, Christian Franke, www.smartmontools.org
 
 === START OF READ SMART DATA SECTION ===
 SMART Attributes Data Structure revision number: 5
 Vendor Specific SMART Attributes with Thresholds:
 ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
   3 Spin_Up_Time            0x0020   100   100   000    Old_age   Offline      -       0
   4 Start_Stop_Count        0x0030   100   100   000    Old_age   Offline      -       0
   5 Reallocated_Sector_Ct   0x0032   100   100   000    Old_age   Always       -       0
   9 Power_On_Hours          0x0032   100   100   000    Old_age   Always       -       4109
  12 Power_Cycle_Count       0x0032   100   100   000    Old_age   Always       -       422
 170 Reserve_Block_Count     0x0033   100   100   010    Pre-fail  Always       -       0
 171 Program_Fail_Count      0x0032   100   100   000    Old_age   Always       -       0
 172 Erase_Fail_Count        0x0032   100   100   000    Old_age   Always       -       0
 183 Runtime_Bad_Block       0x0030   100   100   000    Old_age   Offline      -       21
 184 End-to-End_Error        0x0032   100   100   090    Old_age   Always       -       0
 187 Reported_Uncorrect      0x0032   100   100   000    Old_age   Always       -       0
 192 Unsafe_Shutdown_Count   0x0032   100   100   000    Old_age   Always       -       216
 199 UDMA_CRC_Error_Count    0x0030   100   100   000    Old_age   Offline      -       0
 225 Host_Writes_32MiB       0x0032   100   100   000    Old_age   Always       -       203817
 226 Workld_Media_Wear_Indic 0x0032   100   100   000    Old_age   Always       -       1424
 227 Workld_Host_Reads_Perc  0x0032   100   100   000    Old_age   Always       -       60
 228 Workload_Minutes        0x0032   100   100   000    Old_age   Always       -       246549
 232 Available_Reservd_Space 0x0033   100   100   010    Pre-fail  Always       -       0
 233 Media_Wearout_Indicator 0x0032   099   099   000    Old_age   Always       -       0
 241 Host_Writes_32MiB       0x0032   100   100   000    Old_age   Always       -       203817
 242 Host_Reads_32MiB        0x0032   100   100   000    Old_age   Always       -       314998

参考资料

  1. 1.0 1.1 Wikipedia: TRIM
  2. Wikipedia: Write Amplification
  3. Wikipedia: Wear leveling
  4. SSD Write Endurance 25nm Vs 34nm
  5. SSD Myths and Legends - "write endurance"
  6. 6.0 6.1 6.2 6.3 ArchWiki: Solid State Drives
  7. GParted Manual: Specifying Partition Alignment
  8. 8.0 8.1 btrfs Wiki: FAQ
  9. 9.0 9.1 How to maximise SSD performance with Linux
  10. 10.0 10.1 开源中国社区:Linux 系统下使用 ssd 固态硬盘
  11. OpenSUSE Forum: SSD detection when creating first time fstab ?
  12. 12.0 12.1 OpenSUSE: SDB:SSD discard (trim) support
  13. 13.0 13.1 SSD’s, Journaling, and noatime/relatime
  14. Linux on Solid State Drives
  15. http://media.kingston.com/support/downloads/MKP_306_SMART_attribute.pdf
  16. ServerFault: How to determine number of write cycles or expected life for SSD under Linux?

本文对您有帮助?分享给更多朋友!

反馈与讨论

发现文档不全面、有错误却没时间编辑文档?想分享自己的经验或见解?欢迎在此留言、讨论。
个人工具
简体繁体转换