mongodb - 为什么 json.RawMessage 会放大 mongoDb 文件大小?
问题描述
以下代码尝试通过go.mongodb.org/mongo-driver
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 文档
数组 - 数组的文档是一个普通的 BSON 文档,键为整数值,从 0 开始并按顺序继续。例如,数组 ['red', 'blue'] 将被编码为文档 {'0': 'red', '1': 'blue'}。键必须按数字升序排列。
Bson阵列可以占用更多的磁盘空间吗?我对吗?
MongoDB版本:4.0
解决方案
test_doc1 使用json.RawMessage
which 本质上是[]byte
这样,它被存储为表示字符串(文档的原始表示)的整数数组。
test_doc2 将数据存储为二进制数据,这是一种更紧凑的形式。
Go Mongo 驱动程序使用WriteBinaryWithSubtype
json 编码数据的方法,但WriteArray
使用RawMessage
.
不同之处在于 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 中。
推荐阅读
- kotlin - 更新列表内的对象 Kotlin
- prometheus-operator - prometheus 没有看到来自 k8s 节点的 mountstats 指标
- c++ - 将 unicode 代码点转换为 utf-16
- c - 在 C 中声明指针的方法
- c# - 从转换后的字符串返回两个变量
- python-3.x - 箱须图,每点三个箱
- reactjs - 在 React 中延迟加载图像
- c++ - QAbstractItemModel::columnCount - 每行的可变列数
- api - 谷歌地图、方向、距离矩阵API
- selenium - 如何使用 Python Selenium 切换到嵌套框架集?