Движущиеся изображения очень медленные - PullRequest
0 голосов
/ 17 января 2020

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

Я не буду go вдаваться в подробности метаданных, поскольку они не имеют отношения к этому вопросу. Но в основном это временная метка, varchar (255) и два логических значения (так что ничего страшного).

Чтобы исправить свою ошибку, я создал новую таблицу с именем ImageBlob , которая содержит только изображение и идентификатор, на который ссылаются сущности в таблице Image .

Затем я написал фрагмент кода (используя EF Core 3.0.0) для перемещения данных из * 1013. * Image таблица к таблице ImageBlob . Этот код, однако, невероятно медленный. (Он работал около 12 часов, и это только на 40%.)

Я пытаюсь переместить 120.000 изображений. Всего 200 000 записей, из которых 80 000 не содержат изображения. Вся база данных составляет около 150 ГБ.

Мой вопрос: я что-то упускаю или есть более эффективный способ сделать это?

Код для перемещения изображений:

using (var context = new MakeModelContext())
            {
                List<int> imageIds = context.Images
                   .Where(x => x.IsAcquired)
                   .Select(x => x.Id)
                   .ToList();

                foreach (int imageId in imageIds)
                {
                    Image image = context.Images.Where(x => x.Id == imageId).Single();

                    if (image.Picture != null)
                    {
                        image.ImageBlob = new ImageBlob(image.Picture);
                        image.Picture = null;
                        context.SaveChanges();
                    }
                }
            }

Image.cs:

public class Image
    {
        [Required]
        public int Id { get; set; }
        public byte[] Picture { get; set; }
        public ImageBlob ImageBlob { get; set; }

        // Metadata redacted
    }

ImageBlob.cs:

public class ImageBlob
    {
        [Required]
        public int Id { get; set; }
        public byte[] Blob { get; set; }

        public ImageBlob(byte[] blob)
        {
            this.Blob = blob;
        }
    }

РЕДАКТИРОВАТЬ: Решение, предложенное sdi

Спасибо за ваш ответ. Я изменил код для обработки изображений партиями по 100. Несмотря на то, что я ожидал некоторых дополнительных затрат от отдельных запросов, я не ожидал, что это окажет такое большое влияние на производительность!

Я рассматривал SaveChangesASyn c () но я думаю, что это может вызвать конфликты, так как программа может извлечь изображения, которые уже перемещены, но для которых изменения еще не были полностью сохранены.

Это, как говорится. Производительность все еще не велика, но определенно достаточна для одноразовой работы. В настоящее время я управляю около 6 МБ / с.

Модифицированный код:

using (var context = new MakeModelContext())
            {
                bool done = false;

                while (!done)
                {
                    List<Image> image_batch = context.Images
                        .Where(x => x.IsAcquired && x.Picture != null)
                        .Take(100)
                        .ToList();

                    if (image_batch.Count < 100)
                    {
                        done = true;
                    }

                    foreach (Image image in image_batch)
                    {
                        image.ImageBlob = new ImageBlob(image.Picture);
                        image.Picture = null;
                    }
                    context.SaveChanges();
                }
            }

Ответы [ 3 ]

1 голос
/ 17 января 2020

Затем я написал фрагмент кода (используя EF Core 3.0.0) для перемещения данных из таблицы Image в таблицу ImageBlob.

Таким образом, чтобы переместить вещи в базу данных, вы решили получить каждое изображение на свой клиентский компьютер, а затем отправить его обратно на сервер. Это не очень эффективно.

Вы можете просто использовать одну команду SQL на сервере, чтобы сделать все это на сервере и никогда не отправлять одно изображение назад и вперед.

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

1 голос
/ 17 января 2020

Похоже, вы хотите извлечь данные из столбца Picture в отдельную таблицу.

Изображение -> Blob

Если у вас есть следующие таблицы:

CREATE TABLE Images (
    ID int PRIMARY KEY IDENTITY, 
    ...,
    Picture varbinary(max)
)

CREATE TABLE Blobs (
    ID int PRIMARY KEY IDENTITY, 
    ImageID int NOT NULL FOREIGN KEY FK_Blob_Image REFERENCES Images(ID),
    Picture varbinary(max),
    ...
)

Вы можете скопировать данные Picture в таблицу, просто :

INSERT INTO Blobs(ImageId,Picture) 
SELECT ID,Picture 
from Images

После этого вы можете очистить существующие данные с помощью:

UPDATE Images
Set Picture=null

Использование ORM в этом случае совершенно неуместно. Объекты не задействованы, и нет никаких причин для передачи данных сервера клиенту только для отправки обратно

Blob-> Image

Когда заказ отменен, Предложение OUTPUT в операторе INPUT может использоваться для извлечения новых BlobID и установки их в таблицу изображений.

CREATE TABLE Images (
    ID int PRIMARY KEY IDENTITY, 
    ...,
    BlobID FOREIGN KEY FK_Image_Blob REFERENCES Blobs(ID),
    Picture varbinary(max)
)

CREATE TABLE Blobs (
    ID int PRIMARY KEY IDENTITY, 
    Picture varbinary(max),
    ...
)

С

declare @blobImages table(BlobID int,ImageID int)

INSERT INTO Blobs(ImageId,Picture) 
    OUTPUT inserted.ID ,inserted.ImageID
    INTO @blobImages
SELECT ID,Picture 
from Images

UPDATE Images
SET 
    Picture=null,
    BlobID=b.BlobID
FROM Images 
INNER JOIN @blobImages b on b.ImageID=Images.ID

FR

0 голосов
/ 17 января 2020

для каждого идентификатора imageIds вы открываете соединение с базой данных и загружаете изображение, вы можете попробовать пакетировать его (чтобы вы загружали сразу как 50), это уже должно увеличить скорость? То же самое касается saveasyn c (), не после каждого нового изображения, а после создания / вставки пакета

...