在数据库中存储不同图像的最佳方法是什么?

时间:2012-04-03 13:20:06

标签: sql sql-server database database-design

为不同目的存储图像的最佳方法(关于数据库设计)是什么?

我有一堆用户照片,我还有另外5套不同的照片(例如用户照片,但没有连接到用户照片)。

最好将所有照片存储在一个数据库表中并尝试从该表中引用它们,还是最好为每组照片创建不同的表?

我可以看到创建多个表的一个好处,那就是删除主对象时删除照片的级联删除功能。

还需要考虑其他方面吗?

另一个例子可能是地址。用户可以拥有地址,但公司或位置也可以。 为所有地址创建一个表,并尝试使用某种索引表来引用哪个地址属于哪个对象或具有不同的表并消除问题。

5 个答案:

答案 0 :(得分:14)

如何在sql server

中存储大blob

在SQL Server中存储大量二进制数据并不是一个好方法。它使您的数据库非常庞大,备份和性能通常不是很好。存储文件通常在文件系统上完成。 Sql Server 2008具有对FILESTREAM的开箱即用支持。 Microsoft将案例记录为使用 FileStream ,如下所示

  • 平均存储的对象大于1 MB。
  • 快速读取访问非常重要。
  • 您正在开发使用中间层进行应用程序逻辑的应用程序。

在你的情况下,我认为所有要点都是有效的。

在服务器上启用

要在服务器上启用FILESTREAM支持,请使用以下语句。

EXEC sp_configure filestream_access_level, 2
RECONFIGURE

配置数据库

要获取链接到数据库的文件流文件组,请创建

ALTER DATABASE ImageDB ADD FILEGROUP ImageGroup CONTAINS FILESTREAM
ALTER DATABASE ImageDB 
  ADD FILE ( NAME = 'ImageStream', FILENAME = 'C:\Data\Images\ImageStream.ndf')
  TO FILEGROUP TodaysPhotoShoot

创建表

下一步是使用文件流存储将数据存储到数据库中:

CREATE TABLE Images
(
    [Id] [uniqueidentifier] ROWGUIDCOL NOT NULL PRIMARY KEY, 
    [CreationDate] DATETIME NOT NULL,
    [ImageFile] VARBINARY(MAX) FILESTREAM NULL
)

要让Filestream工作,您不仅需要表中字段的FILESTREAM属性,还需要具有ROWGUIDCOL属性的字段。

使用TSQL

插入数据

现在要在此表中插入数据,您可以使用TSQL:

using(var conn = new SqlConnection(connString))
using(var cmd = new SqlCommand("INSERT INTO Images VALUES (@id, @date, cast(@image as varbinary(max))", conn))
{
     cmd.Parameters.AddRange(new {
          new SqlParameter("id", SqlDbType.UniqueIdentifier).Value = uId,
          new SqlParameter("date", SqlDbType.DateTime).Value = creationDate,
          new SqlParameter("image", SqlDbType.varbinary).Value = imageFile,
      });
     conn.Open
     cmd.ExecuteScalar();
}

使用SqlFileStream

插入数据

还有一种方法可以直接使用Win32在磁盘上获取文件数据。这为您提供了从SqlFileStream继承的流媒体访问IO.Stream

使用win32插入数据可以通过以下代码完成:

    public void InsertImage(string connString, Guid uId, DateTime creationDate, byte[] fileContent)
    {
        using (var conn = new SqlConnection(connString))
        using (var cmd = new SqlCommand(@"INSERT INTO Images VALUES (@id, @date, cast(@image as varbinary(max)) output INSERTED.Image.PathName()" , conn))
        {
            conn.Open();

            using (var transaction = conn.BeginTransaction())
            {
                cmd.Transaction = transaction;
                cmd.Parameters.AddRange(
                    new[] {
                         new SqlParameter("id", SqlDbType.UniqueIdentifier).Value = uId,
                         new SqlParameter("date", SqlDbType.DateTime).Value = creationDate,
                         new SqlParameter("image", SqlDbType.VarBinary).Value = null
                        }
                    );

                var path = (string)cmd.ExecuteScalar();

                cmd.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";

                var context = (byte[])cmd.ExecuteScalar();

                using (var stream = new SqlFileStream(path, context, FileAccess.ReadWrite))
                {
                    stream.Write(fileContent, 0, fileContent.Length);
                }

                transaction.Commit();
            }
        }

如何为照片存储数据库建模

使用文件流方法存储图像时,表格非常窄,这有利于提高性能,因为每8K数据页面可以存储许多记录。我会使用以下模型:

    CREATE TABLE Images
    (
        Id uniqueidentifier ROWGUIDCOL NOT NULL PRIMARY KEY, 
        ImageSet INTEGER NOT NULL 
            REFERENCES ImageSets,
        ImageFile VARBINARY(MAX) FILESTREAM NULL
    )

    CREATE TABLE ImageSets
    (  
        ImageSet INTEGER NOT NULL PRIMARY KEY,
        SetName nvarchar(500) NOT NULL,
        Author INTEGER NOT NULL
            REFERENCES Users(USerId)
    )

   CREATE TABLE Users
   (
        UserId integer not null primary key,
        UserName nvarchar(500),
        AddressId integer not null
             REFERENCES Addresses
   )

   CREATE TABLE Organsations
   (
        OrganisationId integer not null primary key
        OrganisationName nvarchar(500),
        AddressId integer not null
             REFERENCES Addresses
   )

   CREATE TABLE Addresses
   (
       AddressId integer not null primary key,
       Type nvarchar(10), 
       Street nvarchar(500),
       ZipCode nvarchar(50),
       City nvarchar(500),
   )

   CREATE TABLE OrganisationMembers
   (
       OrganisationId integer not null
          REFERENCES Organisations,
       UserId integer not null
          REFERENCES Users,
       PRIMARY KEY (UserId, OrganisationId)
   )
   CREATE NONCLUSTERED INDEX ixOrganisationMembers on OrganisationMembers(OrganisationId)

这转换为以下实体关系图:

Entity RelationShip Diagram

  • 性能方面,窄图像表非常好,因为它每条记录只包含几个字节的数据。
  • 我们可以假设图像始终是图像集的成员,如果其中只有1个图像,则可以隐藏集合信息。
  • 我假设您要跟踪哪些用户是哪些组织的成员,因此我添加了一个表来链接它们(假设用户可以是多个组织的成员)。
  • OrganisationMembers表上的主键将UserId作为第一个字段,因为通常会有比组织更多的用户,并且您可能希望更频繁地显示用户所属的组织,而不是反向。
  • OrganisationMembers中的OrganisationId索引可用于满足需要显示特定组织成员列表的查询。

参考文献:

答案 1 :(得分:0)

拥有不同表格的唯一原因是您可以拥有FK。但这对于数据完整性非常重要。

如果您想对所有照片进行单一查询,那么拥有所有照片的单个表的一个原因就是。

另一个原因可能是它使您的应用程序编写更容易(即因为您不必更改在单个照片表中工作的代码)

由于第二和第三个原因很不可能,我建议您使用第一个选项。

答案 2 :(得分:0)

当我有某种实体在多个上下文中重复时,例如邮寄地址,我经常将它们全部收集在一张桌子上。这通常简化了验证(例如邮政编码),管理重复,......

在适当的情况下,我会有一个交叉参考表。例如,电话号码可能与一个音符(“家”,“移动”,......)一起存在于一个表中。供应商和电话号码之间的交叉参考表可以将一个人与所需的电话号码相匹配。它还提供了添加排名的机会,以便他们可以指定他们的首选电话号码。在某些情况下,您可能希望提示用户更新有关相关更改的信息,例如当您更新公司的800号码时,是否应该更新其他任何引用?

无论如何,删除都需要检查对实体的任何未完成引用。在大多数应用程序中,这种情况不会频繁发生,从而导致出现问我不是使用级联删除的忠实粉丝。我宁愿有一个存储过程来管理删除并处理任何“手动”级联,以避免真正的重大意外。

BLOB是另一个讨论。照片,PDF文档和其他庞大的二进制文件在数据库大小,命名约定,备份/恢复等方面存在问题。根据所使用的SQL Server的特定版本,这些会有所不同。

答案 3 :(得分:0)

从包含任何类型的大数据的表中检索行需要时间。图像往往非常大,如果我要设计一个数据库,在其结构中存储图像或其他大文件,那么我会:

  1. 尝试将图像分布在多个表格上,特别是如果您打算显示图像的缩略图,这些缩略图的检索速度要比全尺寸图像快得多。
  2. 图像表应独立于相关数据,例如。替代文字,名称,描述或标签。我对图像的唯一数据是主键和doctype,例如。 jpg,jpeg,png,gif,bmp等
  3. 避免使用linq的where函数。而不是自己构造sql查询,因为我还没有想到的原因,where函数比编写执行相同操作的sql查询慢得多。但并非在所有情况下,但是如果您使用linq并在调试时,您发现where方法需要很长时间才能完成,那么一定要编写自己的SQL查询。
  4. 尝试强制将上传的照片裁剪为固定比例,甚至缩小到标准尺寸。根据您的目的,可能没有必要,但根据我的经验,当在网格或列表中显示collectionOfImage时,它会节省很多痛苦。

答案 4 :(得分:0)

FileStream没问题,如上所述。但它很复杂。你知道什么是最好的存储文件?文件系统。这就是它的作用。您只需要设置一个所有Web服务器都可以写入的共享,并且您的保存过程是1)生成图像ID,2)使用该名称保存文件,3)插入指定文件共享网络的行路径或文件的URL。然后,您的db表保持小而快,客户端可以从文件系统中提取文件。在SSD上设置带有RAID的TB级文件服务器来存储文件并将访问路径存储在数据库服务器中更便宜,更快速,更可靠。 BLOB在sql server中有奇怪的效果,比如一旦删除就不会放弃它们的空间,还有很多其他问题(不能在线重建聚簇索引等)。