为什么json.RawMessage会扩大mongoDb文档大小?

为什么json.RawMessage会扩大mongoDb文档大小?,mongodb,go,bson,Mongodb,Go,Bson,以下代码尝试通过go.mongoDB.org/mongo-driver data:=“这是测试字符串bla” 类型Doc结构{ Version int“json:Version,bson:Version” 数据字符串“json:Data,bson:Data” } dd:=Doc{Version:21,Data:Data} dObj,:=json.Marshal(dd) queryFilter:=bson.M{“版本”:1} update1:=bson.M{“$set”:bson.M{“版本”:1

以下代码尝试通过
go.mongoDB.org/mongo-driver

data:=“这是测试字符串bla”
类型Doc结构{
Version int“json:Version,bson:Version”
数据字符串“json:Data,bson:Data”
}
dd:=Doc{Version:21,Data:Data}
dObj,:=json.Marshal(dd)
queryFilter:=bson.M{“版本”:1}
update1:=bson.M{“$set”:bson.M{“版本”:1,“数据”:json.RawMessage(dObj)}
//使用json.RawMessage插入数据
_,err:=db.Mongo(“test”).Collection(“test_doc1”).UpdateOne(context.Background(),queryFilter,update1,options.Update().SetUpsert(true))
如果出错!=零{
fmt.Println(“插入文件1失败”)
}
update2:=bson.M{“$set”:bson.M{“版本”:1,“数据”:(dObj)}
//插入不带json.RawMessage的数据
_,err=db.Mongo(“test”).Collection(“test_doc2”).UpdateOne(context.Background(),queryFilter,update2,options.Update().SetUpsert(true))
如果出错!=零{
fmt.Println(“未能插入doc2”)
}
test_doc1
的内容是
“数据”:json.RawMessage(dObj)
,而
test_doc2
的内容是
“数据”:(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\u doc1
的大小大于
test\u doc2
。为什么?

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

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


MongoDB版本:4.0

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

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()
方法将字节片编码为二进制数据

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

// SliceEncodeValue is the ValueEncoderFunc for slice types.
func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
此方法依次使用
WriteArray()
方法


摘要:json.Marshal调用直接使用
[]字节
类型,因此它们被视为bson二进制类型并以压缩二进制形式存储
json.RawMessage
即使将数据存储为
[]字节
内部也被视为一个片,一个整数片,因此在mongo中存储为一个整数数组

但这并不能回答这个问题。OP询问为什么以两种不同的格式保存相同的数据会导致文档大小增加5倍,因为格式不同?一个存储为整数数组,另一个存储为二进制形式。让我试着用更好的方式重新表述一下。@AyushGupta完全不同意你的观点,我建议你试着理解答案的实际含义。作为一个“字节值数组”,但表示为整数,它实际上在MongoDB中存储为多个整数,这些整数不是一个。因此,随后的BSON大小差异实际上是大小的4倍以上,加上每个整数值上的额外填充和BSON数组的定义。BSON二进制字段只是按原样存储数据。我添加了更多解释,希望现在答案更好。文档明显不同。如前所述,一种形式是32位的“整数数组”(即使实际数值都小于255)。另一种形式就是完整的二进制数据。所以只需要普通字节。当然,一个比另一个小得多。不是“苹果对苹果”的比较。