swift-bson 库是一个可移植的、不依赖 Foundation 的库,用于处理 BSON。
swift-bson 库需要 Swift 6.0 或更高版本。
平台 | 状态 |
---|---|
🐧 Linux | |
🍏 Darwin | |
🍏 Darwin (iOS) | |
🍏 Darwin (tvOS) | |
🍏 Darwin (visionOS) | |
🍏 Darwin (watchOS) |
BSON 是一种通用的二进制序列化格式,是 JSON 的超集。 解析 BSON 比解析 JSON 需要更少的内存,并且该格式是可遍历的,这使得可以提取嵌套在 BSON 文档深处的单个字段,而无需实际解析整个文件。
BSON 最初由 MongoDB 开发,它也是 MongoDB 的原生数据格式。 但是,文件格式本身并不与 MongoDB 绑定,可以在任何需要高性能、低内存序列化格式的系统中使用。
如果您正在使用 MongoKitten,您的 MongoDB 驱动程序已经包含一个基于标准库的 Codable
系统的 BSON 解析器,该解析器的优点是可以自动生成大部分反序列化代码。 但是,Codable
具有众所周知的性能限制,不适用于高吞吐量用例。
使用此库的另一个原因是它具有可移植性并且依赖性很少。 MongoDB 驱动程序提供的 BSON 解析器依赖于诸如 ByteBuffer
之类的网络原语,这需要您链接 SwiftNIO 库。 对于仅将 BSON 用作存储格式的应用程序,这可能不是理想的。
该解码器比默认的 MongoKitten 解码器快大约 3 到 6 倍。 编码器的吞吐量与 Codable
相似。
(基准测试源代码)
Host 'vscode' with 12 'x86_64' processors with 30 GB memory, running:
#202408030740 SMP PREEMPT_DYNAMIC Sat Aug 3 07:53:03 UTC 2024
====================
VsMongoKittenDefault
====================
Decode BSON with MongoKitten Default
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric │ p0 │ p25 │ p50 │ p75 │ p90 │ p99 │ p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ * │ 1476 │ 1500 │ 1506 │ 1512 │ 1520 │ 1529 │ 1536 │ 376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs * │ 2334 │ 2459 │ 2493 │ 2521 │ 2545 │ 2605 │ 2634 │ 376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) * │ 21 │ 21 │ 22 │ 22 │ 22 │ 22 │ 22 │ 376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) * │ 17 │ 18 │ 18 │ 18 │ 18 │ 18 │ 18 │ 376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#) │ 617 │ 592 │ 583 │ 572 │ 562 │ 415 │ 234 │ 376 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) * │ 1620 │ 1690 │ 1714 │ 1747 │ 1781 │ 2408 │ 4276 │ 376 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛
Decode BSON with This Library
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric │ p0 │ p25 │ p50 │ p75 │ p90 │ p99 │ p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ * │ 7917 │ 8035 │ 8071 │ 8099 │ 8123 │ 8167 │ 8187 │ 1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs * │ 892 │ 969 │ 984 │ 997 │ 1011 │ 1034 │ 1061 │ 1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) * │ 13 │ 14 │ 14 │ 14 │ 14 │ 14 │ 14 │ 1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains * │ 4406 │ 4523 │ 4555 │ 4583 │ 4607 │ 4643 │ 4697 │ 1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#) │ 1915 │ 1820 │ 1791 │ 1748 │ 1669 │ 1175 │ 1061 │ 1000 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) * │ 522 │ 549 │ 559 │ 572 │ 599 │ 810 │ 943 │ 1000 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛
Encode BSON with MongoKitten Default
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric │ p0 │ p25 │ p50 │ p75 │ p90 │ p99 │ p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ * │ 5054 │ 5247 │ 5307 │ 5379 │ 5427 │ 5519 │ 5598 │ 513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs * │ 2993 │ 3113 │ 3153 │ 3199 │ 3229 │ 3279 │ 3323 │ 513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) * │ 39 │ 41 │ 41 │ 42 │ 42 │ 43 │ 44 │ 513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) * │ 31 │ 33 │ 33 │ 33 │ 34 │ 34 │ 35 │ 513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#) │ 198 │ 189 │ 185 │ 182 │ 177 │ 162 │ 132 │ 513 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) * │ 5039 │ 5300 │ 5394 │ 5501 │ 5661 │ 6181 │ 7598 │ 513 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛
Encode BSON with This Library
╒═════════════════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric │ p0 │ p25 │ p50 │ p75 │ p90 │ p99 │ p100 │ Samples │
╞═════════════════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ (Alloc + Retain) - Release Δ * │ 5066 │ 5251 │ 5311 │ 5375 │ 5427 │ 5531 │ 5614 │ 514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Object allocs * │ 3003 │ 3123 │ 3153 │ 3199 │ 3229 │ 3293 │ 3353 │ 514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Releases (K) * │ 39 │ 41 │ 41 │ 42 │ 42 │ 43 │ 44 │ 514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Retains (K) * │ 31 │ 33 │ 33 │ 33 │ 34 │ 34 │ 35 │ 514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (#) │ 198 │ 189 │ 186 │ 183 │ 178 │ 159 │ 95 │ 514 │
├─────────────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (wall clock) (μs) * │ 5061 │ 5280 │ 5366 │ 5464 │ 5612 │ 6275 │ 10509 │ 514 │
╘═════════════════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛
BSON 并不适合所有人。 下面的理由不是采用 BSON 的好理由,至少单独来说不是。
BSON 会在解析时节省内存,但在典型用例中,BSON 文件将占用与等效 JSON 文件相似的空间量,并提供相似的压缩率。
BSON 通常被认为是服务器端格式,并且几乎没有令人信服的理由仅仅为了向浏览器提供内容而合成它。
也就是说,存在用于解析 BSON 的 JavaScript 库,因此可以在客户端使用它。 这样做的一个好理由是,如果您将 BSON 对象存储为可从 CDN 访问的静态资源,并且希望客户端能够从 CDN 下载 BSON,而不是通过您的 HTTP 服务器动态将其转换为 JSON。
学习此库将使您能够在各种平台上使用高性能的二进制序列化格式。 该库很小,用纯 Swift 编写,并围绕一些关键模式组织,这些模式强调大型代码库中的可维护性。
尽管 swift-bson 不能为您合成序列化代码,但它的习惯用法是可预测的,并且可以很容易地被 GitHub Copilot 等 LLM“绘制”。
在“真实”的代码库中,BSON 模型类型看起来像这样
struct ExampleModel:BSONDocumentEncodable, BSONDocumentDecodable
{
let id:Int64
let name:String?
let rank:Rank
/// A custom enum type.
enum Rank:Int32, BSONEncodable, BSONDecodable
{
case newModel
case risingStar
case aspiringModel
case fashionista
case glamourista
case fashionMaven
case runwayQueen
case trendSetter
case runwayDiva
case topModel
}
/// The schema definition.
enum CodingKey:String, Sendable
{
case id = "_id" // Chosen for compatibility with MongoDB
case name = "D"
case rank = "R"
}
/// The serialization logic.
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
{
bson[.id] = self.id
bson[.name] = self.name
bson[.rank] = self.rank == .newModel ? nil : self.rank
}
/// The deserialization logic.
init(bson:BSON.DocumentDecoder<CodingKey>) throws
{
self.id = try bson[.id].decode()
self.name = try bson[.name]?.decode()
self.rank = try bson[.rank]?.decode() ?? .newModel
}
}
实际将其与原始数据之间进行往返的代码看起来像这样
let models:[ExampleModel] = [
.init(id: 0, name: "Gigi", rank: .topModel),
.init(id: 1, name: nil, rank: .newModel),
]
/// Round-trip one model
let document:BSON.Document = .init(encoding: models[0])
let _:ArraySlice<UInt8> = document.bytes
let model:ExampleModel = try .init(bson: document)
/// Round-trip a list of models
let list:BSON.List = .init(elements: models)
let _:ArraySlice<UInt8> = list.bytes
let array:[ExampleModel] = try .init(bson: list)
swift-bson 库采用 Apache 2.0 许可证。