提升百万级含双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
相关产品推荐
相关产品推荐

