意外的大 Realm 文件大小

Posted

技术标签:

【中文标题】意外的大 Realm 文件大小【英文标题】:Unexpectedly large Realm file size 【发布时间】:2017-09-14 20:59:49 【问题描述】:

这个问题是关于使用两种不同的方式将对象插入领域。我注意到第一种方法要快得多, 但与第二种方法相比,尺寸结果是巨大的。两种方法之间的区别在于移动 在for 循环的外部和内部写入事务。

// Create realm file
let realm = try! Realm(fileURL: banco_url!)

当我像这样添加对象时,Realm 文件增长到 75.5MB:

try! realm.write 
    for i in 1...40000 
        let new_realm_obj = realm_obj(value: ["id" : incrementID(),
                                              "a": "123",
                                              "b": 12.12,
                                              "c": 66,
                                              "d": 13.13,
                                              "e": 0.6,
                                              "f": "01100110",
                                              "g": DateTime,
                                              "h": 3])

        realm.add(new_realm_obj)
        print("???? \(i) Added")
    

当我像这样添加对象时,Realm 文件只增长到 5.5MB:

for i in 1...40000 
    let new_realm_obj = realm_obj(value: ["id" : incrementID(),
                                          "a": "123",
                                          "b": 12.12,
                                          "c": 66,
                                          "d": 13.13,
                                          "e": 0.6,
                                          "f": "01100110",
                                          "g": DateTime,
                                          "h": 3])
    try! realm.write 
        realm.add(new_realm_obj)
        print("???? \(i) Added")
    

要添加到领域文件的我的班级

class realm_obj: Object 
    dynamic var id = Int()
    dynamic var a = ""
    dynamic var b = 0.0
    dynamic var c = Int8()
    dynamic var d = 0.0
    dynamic var e = 0.0
    dynamic var f = ""
    dynamic var g = Date()
    dynamic var h = Int8()

自动递增功能

func incrementID() -> Int 
    let realm = try! Realm(fileURL: banco_url!)
    return (realm.objects(realm_obj.self).max(ofProperty: "id") as Int? ?? 0) + 1

有没有更好或更正确的方法来做到这一点?为什么在这些情况下我会得到如此不同的文件大小?

【问题讨论】:

你能添加'incrementID()'的代码吗? @joern 刚刚添加 incrementID() 已编辑...对不起我的复制和粘贴习惯。 我猜你可以尝试分批添加 5000 个项目,看看会发生什么 如果您在“for”之前调用一次 incrementID(),然后在创建对象时使用 result + i 作为 id,则可以对其进行一些优化。除非您使用多线程并同时进行更多导入,否则这种方法应该可以为您节省大量对领域 dba 的调用。 【参考方案1】:

在单个事务中添加所有对象时的大文件大小是由于 Realm 的事务日志子系统和 Realm 的大型 blob 内存分配算法之间的不幸交互。 Realm 的内存布局算法要求文件大小至少是存储在 Realm 文件中的最大单个 blob 大小的 8 倍。事务日志条目,总结单个事务期间所做的修改,在 Realm 文件中存储为 blob。

当您在一个事务中添加 40,000 个对象时,您最终会得到一个大小约为 5MB 的事务日志条目。这意味着文件的大小必须至少为 40MB 才能存储。 (我不太确定它最终是如何再次变成几乎两倍的大小。可能是 blob 大小被四舍五入到沿线某处的 2 次方……)

当您在 40,000 个事务中添加一个对象时,您仍然会得到一个事务日志条目,只是这一次它的大小约为 100 字节。发生这种情况是因为当 Realm 提交事务时,它会先尝试回收未使用的事务日志条目,然后再为新条目分配空间。由于 Realm 文件未在其他地方打开,因此可以在每次执行新提交时回收先前的条目。

realm/realm-core#2343 跟踪改进 Realm 存储事务日志条目的方式,以避免您看到的严重过度分配。

目前我的建议是区分这两种方法之间的差异,并为每个写入事务添加对象组。这将通过增加提交次数来权衡一点性能,但会通过减少您创建的最大事务日志条目的大小来减少内存布局算法的影响。根据快速测试,每 2,000 个对象提交一次会产生大约 4MB 的文件大小,同时比在单独的写入事务中添加每个对象要快得多。

【讨论】:

【参考方案2】:

在大多数情况下,您应该尽量减少写入事务的数量。写入事务有很大的开销,因此如果您为要添加到领域的每个对象启动一个新的写入事务,您的代码将比使用单个写入事务添加所有对象时慢得多。

根据我的经验,向领域添加多个元素的最佳方法是创建元素,将它们添加到数组中,然后使用单个写入事务将整个数组添加到领域。

所以这就是你应该做的:

var objects = [realmObj]()
for i in 1...40000
    let newRealmObj = realmObj(value: ["id" : incrementID(), "a": "123","b": 12.12,"c": 66,"d": 13.13,"e": 0.6,"f": "01100110","g": DateTime, "h": 3])
    objects.append(newRealmObj)

try! realm.write 
    realm.add(objects)

至于大小问题,请参阅 Realm 文档的Limitations - File Size 部分。我不是 100% 确定问题的原因,但我会说问题是由在写事务中编写代码引起的,不需要在写事务中发生也不应该在写事务中发生。我想正因如此,Realm 会创建很多对象的中间版本,并且由于释放保留的存储容量是一项相当昂贵的操作,因此在您检查文件大小时不会发生。

请记住,对象的创建不需要在写入事务中进行。在Realm中修改持久化数据只需要创建一个写事务(包括向Realm添加新对象、删除持久化对象和直接修改持久化对象)。

【讨论】:

我完全按照你说的做了。又是75.5mb!但是写起来更快。我只是想知道为什么有这么多 mb 持有这个。【参考方案3】:

谢谢大家。我找到了一种使用您的提示完成任务的优化方法。我只是分批执行 .write,而不是在一次操作中发送所有内容。以下是一些数据进行比较:

批量大小(对象)|文件大小 (mb)

10.000 = 23.1mb 5.000 = 11.5mb 2.500 = 5.8mb 1.250 = 4.2mb 625 = 3.7mb 300 = 3.7mb 100 = 3.1mb 50 = 3.1mb 10 = 3.4mb 5 = 3.1mb

因此,以我的拙见,处理 1000 个批次是这种情况下的最佳大小/速度。

这是我用于此测试的代码。唯一改变的是 for 1...XXX 交互。

    let realm = try! Realm(fileURL: banco_url!)

    var objects = [realm_obj]()
    var ids = incrementID()

    while (ids < 40000) 

        for i in 1...5

            let new_realm_obj = realm_obj(value: ["id" : ids,
                                                "a": "123",
                                                "b": 12.12,
                                                "c": 66,
                                                "d": 13.13,
                                                "e": 0.6,
                                                "f": "01100110",
                                                "g": someDateTime,
                                                "h": 3])
            objects.append(new_realm_obj)
            ids += 1 
        

        try! realm.write 
            realm.add(objects)
        
    

【讨论】:

听起来我的回答解决了您的问题,所以请您将其标记为已接受。 @bdash.. 谢谢!

以上是关于意外的大 Realm 文件大小的主要内容,如果未能解决你的问题,请参考以下文章

iOS Swift UIImage 调整大小影响性能的问题;调整大小时出现意外尺寸[重复]

使用 PHP 的大文件上传错误

带有laravel混合的vuejs的大app.js文件大小(13 MB)

将带有 SCP 的大文件发送到某个服务器时,会以 2112 kB 的大小停止

使用 TestFlight 意外增加 iOS 应用程序大小

text 删除意外提交给git的大文件