意想不到的大 Realm 文件大小

Unexpectedly large Realm file size

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

// 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 添加到领域文件

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
}

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

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

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

所以这就是你应该做的:

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添加新对象、删除持久化对象和直接修改持久化对象)。

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

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

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

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

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

谢谢大家。我找到了一种优化的方法来使用您的提示来完成任务。我只是分批执行 .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)
        }
    }