【问题标题】:Why json.RawMessage enlarge mongoDb document size?为什么 json.RawMessage 会放大 mongoDb 文件大小?
【发布时间】:2020-02-09 14:20:47
【问题描述】:

以下代码尝试通过go.mongodb.org/mongo-driver向mongoDB插入新文档

    data := "this is test string blablablablablablabla"
    type Doc struct {
        Version int "json:version, bson:version"
        Data   string   "json:data, bson:data"
    }
    dd := Doc{Version: 21, Data: data}
    dObj, _ := json.Marshal(dd)

    queryFilter := bson.M{"version": 1}
    update1 := bson.M{"$set": bson.M{"version": 1, "data": json.RawMessage(dObj)}}

    // insert data with json.RawMessage
    _, err := db.Mongo("test").Collection("test_doc1").UpdateOne(context.Background(), queryFilter, update1, options.Update().SetUpsert(true))
    if err != nil {
        fmt.Println("failed to insert doc1")
    }

    update2 := bson.M{"$set": bson.M{"version": 1, "data": (dObj)}}

    // insert data without json.RawMessage
    _, err = db.Mongo("test").Collection("test_doc2").UpdateOne(context.Background(), queryFilter, update2, options.Update().SetUpsert(true))
    if err != nil {
        fmt.Println("failed to insert doc2")
    }

test_doc1的内容是"data": json.RawMessage(dObj),而test_doc2的内容是"data": (dObj)

文档内容如下

db.test_doc1.find()
{ "_id" : ObjectId("5da164a950d625a5b2e5d23e"), "version" : 1, "data" : [ 123, 34, 86, 101, 114, 115, 105, 111, 110, 34, 58, 50, 49, 44, 34, 68, 97, 116, 97, 34, 58, 34, 116, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 103, 32, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 98, 108, 97, 34, 125 ] }

db.test_doc2.find()
{ "_id" : ObjectId("5da164a950d625a5b2e5d249"), "version" : 1, "data" : BinData(0,"eyJWZXJzaW9uIjoyMSwiRGF0YSI6InRoaXMgaXMgdGVzdCBzdHJpbmcgYmxhYmxhYmxhYmxhYmxhYmxhYmxhIn0=") }

检查以上两个文件的大小后

Object.bsonsize(db.test_doc2.findOne())
111

Object.bsonsize(db.test_doc1.findOne())
556

test_doc1 的大小大于test_doc2。为什么?

bson doc

Array - 数组的文档是一个普通的 BSON 文档,键为整数值,从 0 开始并按顺序继续。例如,数组 ['red', 'blue'] 将被编码为文档 {'0': 'red', '1': 'blue'}。键必须按数字升序排列。

Bson 阵列会占用更多磁盘空间吗?我对吗?

MongoDB 版本:4.0

【问题讨论】:

  • 文件明显不同。如前所述,一种形式是 32 位的 “整数数组” (即使实际数值都小于 255 )。另一种形式只是完整的二进制数据。所以只是纯字节。当然,一个比另一个小得多。不是“苹果对苹果”的比较。

标签: mongodb go bson


【解决方案1】:

test_doc1 使用json.RawMessage,它本质上是[]byte,因此它被存储为表示字符串(文档的原始表示)的整数数组。

test_doc2 将数据存储为二进制数据,这是一种更紧凑的形式。

Go Mongo 驱动程序对 json 编码数据使用 WriteBinaryWithSubtype 方法,但对 RawMessage 使用 WriteArray

不同之处在于 mongo 端用于存储这些数据的数据类型。一种是将字节切片存储为整数数组,另一种是将数据存储为具有子类型的二进制文件。与整数相比,二进制形式占用的空间更少。

深入挖掘,我注意到 Go 驱动程序使用注册表来确定它应该如何将值编码为 BSON。有一种专用于字节片的方法。

// ByteSliceEncodeValue is the ValueEncoderFunc for []byte.
func (dve DefaultValueEncoders) ByteSliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {

此方法使用WriteBinary() 方法将字节切片编码为二进制数据。

如果有自定义类型(即使它下面是[]byte),它将被视为切片类型并触发切片的“默认编码器”。

// SliceEncodeValue is the ValueEncoderFunc for slice types.
func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {

此方法依次使用WriteArray() 方法。

总结: json.Marshal 调用直接使用 []byte 类型,因此它们被视为 bson 二进制类型并以紧凑二进制形式存储。 json.RawMessage 即使将数据存储为 []byte 在内部被视为一个切片,一个整数切片,因此作为整数数组存储在 mongo 中。

【讨论】:

  • 这并没有回答这个问题。 OP 询问为什么以两种不同的格式保存相同的数据会导致文档大小增加 5 倍
  • 因为格式不同?一个存储为整数数组,另一个存储为二进制形式。
  • 让我试着用更好的方式重新表述一下。
  • @AyushGupta 完全不同意您的观点,我建议您尝试了解答案的实际含义。作为一个“再见值数组”,但表示为 整数,这实际上存储在 MongoDB 中的许多 整数4-byte values*NOT ONE。所以后面的 BSON 大小差异确实大于 4 倍 的大小,加上每个整数值的额外填充和 BSON Array 的定义。 BSON 二进制字段只是按原样存储数据。
  • 我已经添加了更多解释,希望现在答案更好。
猜你喜欢
  • 1970-01-01
  • 2010-12-28
  • 1970-01-01
  • 2021-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-03
  • 2012-01-05
相关资源
最近更新 更多