哪种方式更好? 将媒体文件以字节数组或字符串forms保存到MongoDB?

我将MongoDB中的媒体文件(图片,PDF等)保存为字节数组。 我看到人们通过编码和解码字节数组来保存它的例子。 有什么不同? 也许性能差异? 那么哪种方式更好?

我注意到当文件保存为字节数组时,Mongo Management Studio打开集合的时间更长,然后保存为字符串

我假设您要将文件存储在文档中。 但您是否考虑过使用GridFS vs将文件存储在文档中?

像Liam所指出的那样,MongoDB提供了一篇关于GridFS考虑因素的博客文章

我正在研究的项目的一个优点是不必检查文件大小,您只需在二进制流中编写和读取文件即可。

从性能角度来看,以二进制forms保存和检索文件比首先将其序列化为字符串要快。 在针对MongoDb 3.2数据库运行的测试程序中,将文件以二进制forms保存在文档中比以字符串序列化forms保存文件快3倍。 这是可以理解的,因为字符串序列化的forms只是“更多字节”来保存或读取。

在同一个测试程序中,还对GridFS进行了快速测试,但是你真的必须使用chunck-size进行一轮测试以获得最佳性能。

下面是一个非常粗略的测试程序的代码转储(请注意,您必须自己提供正确的example.jpg并且数据库连接已经过硬编码。)

 class Program { static bool keepRunning; static string fileName = "example.jpg"; static int numDocs = 571; static IMongoDatabase mongoDb; static void Main(string[] args) { Console.CancelKeyPress += delegate { Exit(); }; keepRunning = true; SetupMongoDb(); var fileBytes = File.ReadAllBytes(fileName); Console.WriteLine($"Picturesize in bytes: {fileBytes.Length}"); ClearCollections(); Console.WriteLine($"Saving {numDocs} pictures to the database."); Console.WriteLine("\nStart Saving in Binary Mode."); Stopwatch binaryStopWatch = Stopwatch.StartNew(); SaveBinaryBased(numDocs, fileBytes); binaryStopWatch.Stop(); Console.WriteLine("Done Saving in Binary Mode."); Console.WriteLine("\nStart Saving in String-based Mode."); Stopwatch stringStopWatch = Stopwatch.StartNew(); SaveStringBased(numDocs, fileBytes); stringStopWatch.Stop(); Console.WriteLine("Done Saving in String-based Mode."); Console.WriteLine("\nTime Report Saving"); Console.WriteLine($" * Total Time Binary for {numDocs} records: {binaryStopWatch.ElapsedMilliseconds} ms."); Console.WriteLine($" * Total Time String for {numDocs} records: {stringStopWatch.ElapsedMilliseconds} ms."); Console.WriteLine("\nCollection Statistics:"); Statistics("binaryPics"); Statistics("stringBasedPics"); Console.WriteLine("\nTest Retrieval:"); Console.WriteLine("\nStart Retrieving from binary collection."); binaryStopWatch.Restart(); RetrieveBinary(); binaryStopWatch.Stop(); Console.WriteLine("Done Retrieving from binary collection."); Console.WriteLine("\nStart Retrieving from string-based collection."); stringStopWatch.Restart(); RetrieveString(); stringStopWatch.Stop(); Console.WriteLine("Done Retrieving from string-based collection."); Console.WriteLine("\nTime Report Retrieving:"); Console.WriteLine($" * Total Time Binary for retrieving {numDocs} records: {binaryStopWatch.ElapsedMilliseconds} ms."); Console.WriteLine($" * Total Time String for retrieving {numDocs} records: {stringStopWatch.ElapsedMilliseconds} ms."); ClearGridFS(); Console.WriteLine($"\nStart saving {numDocs} files to GridFS:"); binaryStopWatch.Restart(); SaveFilesToGridFS(numDocs, fileBytes); binaryStopWatch.Stop(); Console.WriteLine($"Saved {numDocs} files to GridFS in {binaryStopWatch.ElapsedMilliseconds} ms."); Console.WriteLine($"\nStart retrieving {numDocs} files from GridFS:"); binaryStopWatch.Restart(); RetrieveFromGridFS(); binaryStopWatch.Stop(); Console.WriteLine($"Retrieved {numDocs} files from GridFS in {binaryStopWatch.ElapsedMilliseconds} ms."); while (keepRunning) { Thread.Sleep(500); } } private static void Exit() { keepRunning = false; } private static void ClearCollections() { var collectionBin = mongoDb.GetCollection("binaryPics"); var collectionString = mongoDb.GetCollection("stringBasedPics"); collectionBin.DeleteMany(new BsonDocument()); collectionString.DeleteMany(new BsonDocument()); } private static void SetupMongoDb() { string hostName = "localhost"; int portNumber = 27017; string databaseName = "exampleSerialization"; var clientSettings = new MongoClientSettings() { Server = new MongoServerAddress(hostName, portNumber), MinConnectionPoolSize = 1, MaxConnectionPoolSize = 1500, ConnectTimeout = new TimeSpan(0, 0, 30), SocketTimeout = new TimeSpan(0, 1, 30), WaitQueueTimeout = new TimeSpan(0, 1, 0) }; mongoDb = new MongoClient(clientSettings).GetDatabase(databaseName); } private static void SaveBinaryBased(int numDocuments, byte[] content) { var collection = mongoDb.GetCollection("binaryPics"); BsonDocument baseDoc = new BsonDocument(); baseDoc.SetElement(new BsonElement("jpgContent", content)); for (int i = 0; i < numDocs; ++i) { baseDoc.SetElement(new BsonElement("_id", Guid.NewGuid())); baseDoc.SetElement(new BsonElement("filename", fileName)); baseDoc.SetElement(new BsonElement("title", $"picture number {i}")); collection.InsertOne(baseDoc); } } private static void SaveStringBased(int numDocuments, byte[] content) { var collection = mongoDb.GetCollection("stringBasedPics"); BsonDocument baseDoc = new BsonDocument(); baseDoc.SetElement(new BsonElement("jpgStringContent", System.Text.Encoding.UTF8.GetString(content))); for (int i = 0; i < numDocs; ++i) { baseDoc.SetElement(new BsonElement("_id", Guid.NewGuid())); baseDoc.SetElement(new BsonElement("filename", fileName)); baseDoc.SetElement(new BsonElement("title", $"picture number {i}")); collection.InsertOne(baseDoc); } } private static void Statistics(string collectionName) { new BsonDocument { { "collstats", collectionName } }; var command = new BsonDocumentCommand(new BsonDocument { { "collstats", collectionName } }); var stats = mongoDb.RunCommand(command); Console.WriteLine($" * Collection : {collectionName}"); Console.WriteLine($" * Count : {stats["count"].AsInt32} documents"); Console.WriteLine($" * Average Doc Size: {stats["avgObjSize"].AsInt32} bytes"); Console.WriteLine($" * Total Storage : {stats["storageSize"].AsInt32} bytes"); Console.WriteLine("\n"); } private static void RetrieveBinary() { var collection = mongoDb.GetCollection("binaryPics"); var docs = collection.Find(new BsonDocument()).ToEnumerable(); foreach (var doc in docs) { byte[] fileArray = doc.GetElement("jpgContent").Value.AsByteArray; // we can simulate that we do something with the results but that's not the purpose of this experiment fileArray = null; } } private static void RetrieveString() { var collection = mongoDb.GetCollection("stringBasedPics"); var docs = collection.Find(new BsonDocument()).ToEnumerable(); foreach (var doc in docs) { // Simply get the string, we don't want to hit the performance test // with a conversion to a byte array string result = doc.GetElement("jpgStringContent").Value.AsString; } } private static void SaveFilesToGridFS(int numFiles, byte[] content) { var bucket = new GridFSBucket(mongoDb, new GridFSBucketOptions { BucketName = "pictures" }); for (int i = 0; i < numFiles; ++i) { string targetFileName = $"{fileName.Substring(0, fileName.Length - ".jpg".Length)}{i}.jpg"; int chunkSize = content.Length <= 1048576 ? 51200 : 1048576; bucket.UploadFromBytes(targetFileName, content, new GridFSUploadOptions { ChunkSizeBytes = chunkSize }); } } private static void ClearGridFS() { var bucket = new GridFSBucket(mongoDb, new GridFSBucketOptions { BucketName = "pictures" }); bucket.Drop(); } private static void RetrieveFromGridFS() { var bucket = new GridFSBucket(mongoDb, new GridFSBucketOptions { BucketName = "pictures" }); var filesIds = mongoDb.GetCollection("pictures.files").Find(new BsonDocument()).ToEnumerable().Select(doc => doc.GetElement("_id").Value); foreach (var id in filesIds) { var fileBytes = bucket.DownloadAsBytes(id); fileBytes = null; } } }