Первичный ключ Sql Server с проблемой раздела

#sql-server #sql-server-2008 #filestream

#sql-server #sql-server-2008 #filestream

Вопрос:

Я создаю таблицу, которая будет разделена на разделы и содержать FILESTREAM столбец. Проблема, с которой я сталкиваюсь, заключается в том, что, похоже, у меня должен быть составной первичный ключ ( FILE_ID и FILE_UPLOADED_DATE ), потому FILE_UPLOADED_DATE что это часть моей схемы разделов. Это правильно? Я бы предпочел, чтобы это был не составной ключ, а просто FILE_ID первичный ключ ….. может ли это быть просто ошибка пользователя?

Любые предложения будут оценены.

Версия: SQL Server 2008 R2

Схемы и функции разделов:

 CREATE PARTITION FUNCTION DocPartFunction (datetime)
AS RANGE RIGHT FOR VALUES ('20101220')
GO
CREATE PARTITION SCHEME DocPartScheme AS
PARTITION DocPartFunction TO (DATA_FG_20091231, DATA_FG_20101231);
GO
CREATE PARTITION SCHEME DocFSPartScheme AS
PARTITION DocPartFunction TO (FS_FG_20091231,FS_FG_20101231);
GO
  

Инструкция Create:

 CREATE TABLE [dbo].[FILE](
    [FILE_ID] [int] IDENTITY(1,1) NOT NULL,
    [DOCUMENT] [varbinary](max) FILESTREAM  NULL,
    [FILE_UPLOADED_DATE] [datetime] NOT NULL,
    [FILE_INT] [int] NOT NULL,
    [FILE_EXTENSION] [varchar](10) NULL,
    [DocGUID] [uniqueidentifier] ROWGUIDCOL  NOT NULL UNIQUE ON [PRIMARY],
CONSTRAINT [PK_File] PRIMARY KEY CLUSTERED
    (   [FILE_ID] ASC
    ) ON DocPartScheme ([FILE_UPLOADED_DATE])
)ON DocPartScheme ([FILE_UPLOADED_DATE])
FILESTREAM_ON DocFSPartScheme;
  

Ошибка, если я не включаю FILE_UPLOADED_DATE :

 Msg 1908, Level 16, State 1, Line 1
Column 'FILE_UPLOADED_DATE' is partitioning column of the index 'PK_File'. Partition columns for a unique index must be a subset of the index key.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
  

Спасибо!

Ответ №1:

Вы путаете первичный ключ и кластеризованный индекс. Нет никаких оснований для того, чтобы они были одним и тем же. У вас может быть включен кластеризованный индекс FILE_UPLOADED_DATE и отдельный некластеризованный первичный ключ FILE_ID . На самом деле вы уже делаете что-то подобное для столбца DocGUID:

 CREATE TABLE [dbo].[FILE](
    [FILE_ID] [int] IDENTITY(1,1) NOT NULL,
    [DOCUMENT] [varbinary](max) FILESTREAM  NULL,
    [FILE_UPLOADED_DATE] [datetime] NOT NULL,
    [FILE_INT] [int] NOT NULL,
    [FILE_EXTENSION] [varchar](10) NULL,
    [DocGUID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
    constraint UniqueDocGUID UNIQUE NONCLUSTERED ([DocGUID]) 
        ON [PRIMARY])
    ON DocPartScheme ([FILE_UPLOADED_DATE])
    FILESTREAM_ON DocFSPartScheme;

CREATE CLUSTERED INDEX cdx_File 
   ON [FILE] (FILE_UPLOADED_DATE)
   ON DocPartScheme ([FILE_UPLOADED_DATE])
   FILESTREAM_ON DocFSPartScheme;

ALTER TABLE [dbo].[FILE]
    ADD CONSTRAINT PK_File PRIMARY KEY NONCLUSTERED (FILE_ID)
    ON [PRIMARY];
  

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

Для построения каждой таблицы сортировки требуется минимальный объем памяти. Когда вы создаете секционированный индекс, который выровнен с его базовой таблицей, таблицы сортировки создаются по одной за раз, используя меньше памяти. Однако, когда вы создаете невыровненный секционированный индекс, таблицы сортировки создаются одновременно.

В результате должно быть достаточно памяти для обработки этих параллельных сортировок. Чем больше количество разделов, тем больше памяти требуется. Минимальный размер каждой таблицы сортировки для каждого раздела составляет 40 страниц, по 8 килобайт на страницу. Например, для невыровненного секционированного индекса со 100 разделами требуется достаточно памяти для одновременной последовательной сортировки 4000 (40 * 100) страниц. Если эта память доступна, операция сборки завершится успешно, но производительность может пострадать. Если эта память недоступна, операция сборки завершится с ошибкой

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

И, наконец, нужно спросить: зачем использовать секционированную таблицу? Они всегда работают медленнее, чем альтернатива без разделов. Если вам не нужны быстрые операции переключения разделов для ETL (которые вы уже используете из-за несогласованного индекса в DocGUID), в принципе, нет никакого стимула использовать секционированную таблицу. (Упреждающий комментарий: кластеризованный индекс в FILE_UPLOADED_DATE гарантированно является лучшей альтернативой, чем «удаление раздела»).

Комментарии:

1. Ремус, большое тебе спасибо за информацию. Мне нужен раздел, чтобы я мог перенести файловую группу filestream в другое место ….. но только самые новые, потому что у нас есть ограничения по пространству…. Долгая история.

2. указанное выше решение, которое вы предоставили, не работает? Частично это было из-за зарезервированного слова «FILE_ID», но после исправления оно по-прежнему выдает ошибки, когда я пытаюсь построить таблицу с вышеуказанной структурой.

3. Я исправил свой скрипт (протестировал его на этот раз). Использование секционирования для управления файловыми группами является веской причиной для развертывания секционирования. К сожалению, требование FILESTREAM для ограничения уникальности в ROWGUIDCOL является гарантированным невыровненным индексом, и это может вызвать проблемы в будущем (например. если вам когда-нибудь понадобится использовать fast partition switch для удаления документов, которые продлили максимальный срок действия политики обязательного хранения).

Ответ №2:

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

Ответ №3:

Я знаю, это старый вопрос, но, возможно, Google приводит кого-то еще к этому вопросу:

Возможным решением было бы разделение не по столбцу даты, а по File_ID. Каждый день / неделю / месяц (или любой другой период времени, который вы используете) вы должны запускать задание агента в полночь, которое принимает Max(File_ID) where file_uploadet_date < GetDate() , добавляет следующую файловую группу в схему разделов и выполняет разделение на MaxID 1 .

Конечно, у вас все равно будет проблема с не выровненным индексом в идентификаторе DocID, за исключением того, что вы также добавляете file_id к этому уникальному индексу (может привести к неуникальным идентификаторам DOCID) и / или проверяете его уникальность в триггере вставки / обновления.