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

时间:2016-12-29 07:38:03

标签: c# asp.net-mvc mongodb

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

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

1 个答案:

答案 0 :(得分:3)

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

与Liam指出的一样,MongoDB提供了一篇关于GridFS注意事项的博客文章here

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

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

在同一个测试程序中,还对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<BsonDocument>("binaryPics");
        var collectionString = mongoDb.GetCollection<BsonDocument>("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<BsonDocument>("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<BsonDocument>("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<BsonDocument>(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<BsonDocument>("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<BsonDocument>("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<BsonDocument>("pictures.files").Find(new BsonDocument()).ToEnumerable().Select(doc => doc.GetElement("_id").Value);

        foreach (var id in filesIds)
        {
            var fileBytes = bucket.DownloadAsBytes(id);
            fileBytes = null;
        }
    }
}