You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

提升百万级含双UUID结构体的磁盘存储与读取效率

解决数百万含UUID结构体的高效磁盘存储问题

一、自定义二进制格式(最快最省空间)

UUID本质是16字节的固定长度二进制数据,你的MetadataItem包含两个UUID,每个实例固定占用32字节(16×2)。直接把所有实例的二进制数据拼接成文件,完全避开JSON序列化的冗余开销,读写速度能拉到磁盘极限。

批量编码(写入)

直接通过内存拷贝实现,预分配内存还能避免多次扩容的开销:

// 扩展MetadataItem,直接操作二进制
extension MetadataItem {
    // 单个实例转32字节Data
    var binaryData: Data {
        var idBytes = id.uuid
        var itemIdBytes = itemId.uuid
        return withUnsafeBytes(of: &idBytes) { idBuf in
            withUnsafeBytes(of: &itemIdBytes) { itemIdBuf in
                Data(idBuf) + Data(itemIdBuf)
            }
        }
    }
    
    // 从32字节Data还原实例
    init?(binaryData: Data) {
        guard binaryData.count == 32 else { return nil }
        let idSlice = binaryData[0..<16]
        let itemIdSlice = binaryData[16..<32]
        
        // 从二进制切片直接构造UUID
        let idUUID = UUID(uuid: idSlice.bindMemory(to: uuid_t.self).baseAddress!.pointee)
        let itemIdUUID = UUID(uuid: itemIdSlice.bindMemory(to: uuid_t.self).baseAddress!.pointee)
        
        self.id = idUUID
        self.itemId = itemIdUUID
    }
}

// 批量编码数组
func encodeItems(_ items: [MetadataItem]) -> Data {
    var data = Data()
    data.reserveCapacity(items.count * 32) // 预分配足够内存
    items.forEach { data.append($0.binaryData) }
    return data
}

// 写入磁盘示例
do {
    let data = encodeItems(metadataItems)
    if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appending(path: "Metadata.bin") {
        try data.write(to: url)
    }
} catch {
    print(error)
}

更极致的内存拷贝(直接绑定连续内存,跳过逐个拼接):

func encodeItemsFast(_ items: [MetadataItem]) -> Data {
    // 前提:MetadataItem是可连续内存布局的类型(你的结构体满足,因为只有两个UUID,都是值类型)
    return items.withUnsafeBytes { buffer in
        Data(buffer)
    }
}

批量解码(读取)

按32字节分片解析,同样用内存绑定提升速度:

func decodeItems(from data: Data) -> [MetadataItem] {
    guard data.count % 32 == 0 else { return [] }
    return data.withUnsafeBytes { buffer in
        buffer.bindMemory(to: MetadataItem.self).map { $0 }
    }
}

// 读取磁盘示例
do {
    if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appending(path: "Metadata.bin") {
        let data = try Data(contentsOf: url)
        let items = decodeItems(from: data)
    }
} catch {
    print(error)
}

二、用Protobuf等二进制序列化框架

如果以后需要扩展结构体字段或者兼容其他平台,Protocol Buffers(Protobuf)是不错的选择,它的二进制序列化效率远高于JSON,文件体积也更小。

先定义.proto文件:

syntax = "proto3";

message MetadataItem {
    bytes id = 1;
    bytes item_id = 2;
}

message MetadataList {
    repeated MetadataItem items = 1;
}

生成Swift代码后,序列化/反序列化速度会比JSON快数倍,同时还能灵活扩展字段。

三、数据库存储(适合需要查询的场景)

如果后续要对数据做查询、筛选等操作,二进制文件就不太方便了,推荐用GRDB(轻量级SQLite封装):

  • 把UUID存为BLOB类型,比存字符串更省空间、速度更快
  • GRDB的批量插入/查询性能优异,轻松应对百万级数据

性能对比

  • JSONEncoder:100万条数据序列化耗时3-30秒,文件体积大(每条至少80+字节,含JSON结构冗余)
  • 自定义二进制:序列化耗时毫秒级,100万条仅32MB,读写速度接近磁盘硬件极限

内容的提问来源于stack exchange,提问作者User95797654974

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 23:22:19