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

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

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

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

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

Любые другие аспекты, которые следует учитывать?

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

Ответ 1

Как хранить большие капли в sql-сервере

Хранение больших кусков двоичных данных в SQL Server - отличный подход. Это делает вашу базу данных очень громоздкой для резервного копирования и производительность, как правило, невелика. Хранение файлов обычно выполняется в системе . 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
)

Для 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

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

Литература:

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

  • пытается распространять изображения по нескольким таблицам, особенно если вы собираетесь отображать эскизы изображений, которые будут значительно быстрее извлекать, чем полноразмерные изображения.
  • таблицы изображений должны быть независимыми от связанных данных, например. alt текст, имя, описание или метки. Единственными данными, которые у меня были бы с изображением, является первичный ключ и тип doctype, например. jpg, jpeg, png, gif, bmp и т.д.
  • избегайте использования функции linq where. Вместо этого структурируйте sql-запрос самостоятельно, поскольку по причинам, которые я еще не понял, функция where намного медленнее, чем запись SQL-запроса, который делает то же самое. Однако не во всех случаях, но если вы используете linq и во время отладки вы обнаруживаете, что метод where занимает много времени, а затем определенно пишет свой собственный SQL-запрос.
  • Попробуйте обеспечить, чтобы загруженные фотографии были обрезаны до фиксированного соотношения или даже сократились до стандартного размера. Это может быть необязательно в зависимости от ваших целей, но, по моему опыту, это избавляет от большой боли, когда дело доходит до отображения коллекцииOfImage в сетке или списке.

Ответ 5

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