【发布时间】:2016-08-17 09:24:40
【问题描述】:
我想知道登录到 SSD 的最佳方式是什么。考虑一下数据库日志之类的东西,您在其中只编写追加,但您还必须对每个事务或少数事务进行 fsync() 以确保应用程序级数据的持久性。
我将提供一些有关 SSD 工作原理的背景知识,因此,如果您已经了解所有这些,请先略读一下,以防我对某些事情有误。一些值得进一步阅读的好东西是Emmanuel Goossaert 6-part guide to coding for SSDs 和论文Don't Stack your Log on my Log [pdf]。
SSD 只能在整个页面中写入和读取。页面大小因 SSD 而异,但通常是 4kb 的倍数。我的三星 EVO 840 使用 8kb 的页面大小(顺便说一下,Linus calls "unusable shit" 以他通常的多彩方式。)SSD 无法就地修改数据,它们只能写入空闲页面。所以结合这两个限制,更新我的 EVO 上的单个字节需要读取 8kb 页面,更改字节,并将其写入新的 8kb 页面并更新 FTL 页面映射(ssd 数据结构),因此该页面的逻辑地址正如操作系统所理解的那样,现在指向新的物理页面。因为文件数据在同一个擦除块(可以擦除的最小页面组)中也不再连续,我们也在建立一种碎片债务形式,这将使我们在未来的 SSD 垃圾收集中付出代价。效率极低。
顺便看看我的 PC 文件系统:
C:\WINDOWS\system32>fsutil fsinfo ntfsinfo c:它有 512 字节的扇区大小和 4kb 分配 (簇的大小。两者都没有映射到 SSD 页面大小 - 可能 效率不高。
仅使用例如写作存在一些问题。 pwrite() 到内核页面缓存并让操作系统处理写出的东西。首先,您需要在调用pwrite() 之后再发出一个sync_file_range() 调用才能真正启动IO,否则它将等到您调用fsync() 并引发IO 风暴。其次,fsync()seems to block 未来在同一文件上调用write()。最后,您无法控制内核如何将内容写入 SSD,它可能做得很好,也可能做得很差,导致大量写入放大。
由于上述原因,并且无论如何我都需要 AIO 来读取日志,所以我选择使用 O_DIRECT 和 O_DSYNC 写入日志并拥有完全控制权。
据我了解,O_DIRECT 要求所有写入都与扇区大小和扇区总数对齐。因此,每次我决定向日志发出附加信息时,我都需要在末尾添加一些填充以使其达到整数个扇区(如果所有写入始终是整数个扇区,它们也将正确对齐,至少在我的代码中。)好吧,这还不错。但我的问题是,将 SSD 页数而不是扇区数四舍五入不是更好吗?大概这会消除写放大?
这可能会消耗大量空间,特别是如果一次将少量数据写入日志(例如几百字节)。这也可能是不必要的。像三星 EVO 这样的 SSD 有一个写缓存,它们不会在 fsync() 上刷新它。相反,它们依靠电容器在断电时将缓存写入 SSD。在这种情况下,也许 SSD 做了正确的事情,一次只写入扇区的追加日志 - 它可能不会写出最终的部分页面,直到下一个追加到达并完成它(或者除非它被强制输出由于大量不相关的 IO 导致的缓存。)由于该问题的答案可能因设备和文件系统而异,有没有一种方法可以编码这两种可能性并测试我的理论?有什么方法可以测量 Linux 上的写入放大或更新/RMW 页面的数量?
【问题讨论】:
-
我对同样的问题感兴趣,但适用于 iOS 设备。
-
你的问题不清楚,你关心什么,节省空间,最大化日志?您对 SSD 有 root 访问权限吗?
标签: c++ linux filesystems solid-state-drive