Какой лучший способ хранить разные изображения в базе данных? - PullRequest
10 голосов
/ 03 апреля 2012

Каков наилучший способ (в отношении дизайна базы данных) для хранения изображений для различных целей?

У меня есть куча фотографий пользователей, и я получил еще 5 различных наборов фотографий (например, фотографии пользователей, но без связи с фотографиями пользователей).

Лучше всего хранить все фотографии в одной таблице базы данных и пытаться ссылаться на них из этой таблицы, или лучше всего создавать разные таблицы для каждого набора фотографий?

Я вижу одно преимущество от создания нескольких таблиц - это функция каскадного удаления для удаления фотографии при удалении основного объекта.

Есть ли еще какие-то аспекты для рассмотрения?

Другим примером могут быть адреса. У пользователя может быть адрес, а также компания или местоположение. Создайте одну таблицу для всех адресов и попытайтесь создать какие-нибудь индексные таблицы, чтобы указать, какой адрес принадлежит какому-либо объекту или иметь разные таблицы, и устранить проблему.

Ответы [ 5 ]

14 голосов
/ 06 апреля 2012

Как хранить большие двоичные объекты на сервере sql

Хранение больших кусков двоичных данных в SQL Server - не лучший подход.Это делает вашу базу данных очень громоздкой для резервного копирования, а производительность, как правило, невелика.Хранение файлов обычно выполняется в системе file .Sql Server 2008 имеет встроенную поддержку FILESTREAM.Microsoft документирует случаи использования FileStream следующим образом

  • Размер хранимых объектов в среднем превышает 1 МБ.
  • Важным является быстрый доступ для чтения.
  • Вы разрабатываете приложения, которые используют средний уровень для логики приложения.

В вашем случае я думаю, что все пункты верны.

Включить на сервере

Чтобы включить поддержку FILESTREAM на сервере, используйте следующую инструкцию.

EXEC sp_configure filestream_access_level, 2
RECONFIGURE

Настройка базы данных

Чтобы получитьфайловая группа filestream, связанная с вашей базой данных, создает

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
)

For 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:

Entity RelationShip Diagram

  • Производительность, узкая таблица изображенийочень хорошо, так как содержит только несколько байтов данных на запись.
  • Мы можем предположить, что изображение всегда является членом набора изображений. Информация о наборе может быть скрыта, если в нем всего 1 изображение.
  • Я предполагаю, что вы хотите отследить, какие пользователичлен какой организации, поэтому я добавил таблицу, чтобы связать их (при условии, что пользователь может быть членом нескольких организаций).
  • Первичный ключ в таблице OrganisationMembers имеет UserId в качестве первого поля, поскольку обычно там гораздо больше пользователей.чем «Организации», и вы, вероятно, захотите показать, в каких организациях пользователь является участником чаще, чем обратное.
  • Индекс OrganisationId в OrganisationMembers предназначен для запросов, для которых нужен список членов для конкретной организации.быть показано.

Ссылки:

0 голосов
/ 06 апреля 2012

FileStream в порядке, как обсуждалось выше.Но это сложно.Вы знаете, что лучше хранить файл?Файловая система.Вот что он делает.Вам просто нужно настроить общий ресурс, на который могут записывать все ваши веб-серверы, и процесс сохранения: 1) создать идентификатор изображения, 2) сохранить файл, используя его в качестве имени, 3) вставить строку, указав сеть общего файлового ресурсапуть или URL к файлу.Тогда ваша таблица БД останется маленькой и быстрой, и ваш клиент сможет извлечь файл из файловой системы.Это дешевле, быстрее и надежнее настроить терабайтный файловый сервер с RAID на SSD для хранения ваших файлов и просто сохранить путь доступа на сервере базы данных.BLOB имеют странные эффекты на сервере sql, например, не освобождая их пространство после удаления, и много других проблем (не удается перестроить кластерный индекс онлайн и т. Д.).

0 голосов
/ 03 апреля 2012

Когда у меня есть какая-то сущность, которая повторяется в нескольких контекстах, например, почтовый адрес, я часто собираю их все в одну таблицу.Как правило, это упрощает проверку (например, почтовые индексы), управление дубликатами, ....

В соответствующих случаях у меня будет таблица перекрестных ссылок.Например, телефонные номера могут находиться в одной таблице вместе с примечанием («домашний», «мобильный», ...).Таблица перекрестных ссылок между поставщиками и телефонными номерами может сопоставить одному человеку столько телефонных номеров, сколько ему нужно.Это также дает возможность добавить рейтинг, чтобы они могли указать свой предпочтительный номер телефона.В некоторых случаях вы можете попросить пользователя обновить информацию о связанных изменениях, например, когда вы обновляете номер 800 для компании, должны ли обновляться какие-либо другие ссылки на него?

В любом случае удаление требует проверки на наличиелюбые выдающиеся ссылки на сущность.В большинстве приложений это не происходит достаточно часто, чтобы быть проблемой.Я не большой поклонник использования каскадного удаления.Я бы предпочел иметь хранимую процедуру, которая управляет удалением и обрабатывает любое каскадирование «вручную», чтобы избежать действительно больших сюрпризов.

BLOB - это еще одно обсуждение.Фотографии, PDF-документы и другие громоздкие двоичные файлы имеют проблемы, касающиеся размера базы данных, соглашений об именах, резервного копирования / восстановления, ....Они несколько различаются в зависимости от конкретной используемой версии SQL Server.

0 голосов
/ 06 апреля 2012

Извлечение строки из таблицы, содержащей любые большие данные, занимает много времени. В наши дни изображения имеют тенденцию быть очень большими, и если бы я проектировал базу данных, в которой в ее структуре хранятся изображения или другие большие файлы, я бы:

  1. Попытка распределить изображения по нескольким таблицам, особенно если вы намереваетесь отображать миниатюры изображений, которые можно получить значительно быстрее, чем полноразмерные изображения.
  2. таблицы изображений должны быть независимы от связанных данных, например. альтернативный текст, имя, описание или метки. Единственные данные, которые у меня были бы с изображением, это первичный ключ и тип документа, например. JPG, JPEG, PNG, GIF, BMP и т. д.
  3. избегайте использования функции linq's where. Вместо этого структурируйте SQL-запрос самостоятельно, поскольку по причинам, которые я еще не выяснил, функция where намного медленнее, чем написание SQL-запроса, который делает то же самое. Хотя не во всех случаях, но если вы используете linq и во время отладки обнаруживаете, что метод where занимает много времени, то обязательно напишите свой собственный sql-запрос.
  4. Постарайтесь обеспечить, чтобы загруженные фотографии либо обрезались до фиксированного соотношения, либо даже сокращались до стандартного размера. Это может быть необязательным в зависимости от ваших целей, но, по моему опыту, это избавляет от боли, когда дело доходит до отображения collectionOfImage в сетке или списке.
0 голосов
/ 03 апреля 2012

Единственная причина иметь разные таблицы в том, что вы можете иметь FKs.Но это очень важно для целостности данных.

Одна из причин иметь единую таблицу со всеми фотографиями была бы, если вы хотите сделать один запрос ко всем фотографиям.

Другая причинавозможно, это облегчает написание вашего приложения (т. е. потому что вам не нужно менять код, работающий в одной таблице фотографий)

Поскольку вторая и третья причины маловероятны, я бы порекомендовал вамиспользовать первый вариант.

...