Что такое удобный способ хранения больших текстовых полей без ущерба для производительности? - PullRequest
5 голосов
/ 18 февраля 2009

Я танцевал вокруг этой проблемы некоторое время, но она продолжает появляться. У нас есть система, и наша таблица наших таблиц начинается с описания, которое изначально хранится как NVARCHAR(150), а затем я получаю билет с просьбой увеличить размер поля до 250, затем до 1000 и т. Д. И т. Д.

Этот цикл повторяется для всех полей «примечание» и / или «описание», которые мы добавляем в большинство таблиц. Конечно, меня беспокоит производительность и превышение лимита в 8 тысяч страниц. Однако моя другая задача - сделать систему менее удобной для обслуживания, разбив эти поля из КАЖДОЙ таблицы в системе на лениво загруженную ссылку.

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

  1. Измените все заметки и / или описания на NVARCHAR(MAX) и убедитесь, что мы исключаем эти поля из всех списков. По сути, никогда не выполняйте: SELECT * FROM [TableName], если только он не извлекает только одну запись.

  2. Удалите все поля примечаний и / или описания и замените их ссылками на чужие ключи для таблицы [Notes].

    CREATE TABLE [dbo].[Notes] (<br> [NoteId] [int] NOT NULL,<br> [NoteText] [NVARCHAR] (MAX) NOT NULL )

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


UPDATE: Я провел несколько тестов на примере базы данных с 100 000 записей в ней. Я обнаружил, что из-за сканирования индекса кластера IO, требуемый для варианта 1, «примерно» вдвое больше, чем для варианта 2. Если я выбираю большое количество записей (1000 или более), параметр 1 будет в два раза медленнее, даже если я это сделаю не включать большое текстовое поле в выбор. Поскольку я запрашиваю меньше строк, линии размываются больше. Я веб-приложение, где размеры страниц около 50 являются нормой, поэтому вариант 1 будет работать, но я буду преобразовывать все экземпляры в вариант 2 в (очень) ближайшем будущем для масштабируемости.

Ответы [ 5 ]

5 голосов
/ 18 февраля 2009

Вариант 2 лучше по нескольким причинам:

  1. При запросе ваших таблиц, большой текстовые поля быстро заполняют страницы, заставляя базу данных сканировать больше страницы для получения данных. Это особенно облагаются налогом, когда вы этого не сделаете на самом деле нужно вернуть текст данные.
  2. Как вы упомянули, это дает вам чистый перерыв для изменения данных введите одним махом. Microsoft имеет устарел ТЕКСТ в SQL Server 2008, так что вы должны придерживаться VARCHAR / VARBINARY.
  3. Отдельные файловые группы. имеющий все ваши текстовые данные медленнее, более дешевое место хранения может быть то, что вы решили продолжить в будущее. Если нет, нет вреда, нет фол.

Хотя вариант 1 на данный момент проще, вариант 2 даст вам большую гибкость в долгосрочной перспективе. Мое предложение состояло бы в том, чтобы реализовать простое подтверждение концепции с информацией о «примечаниях», отделенной от основной таблицы, и выполнить некоторые из ваших запросов в обоих примерах. Сравните планы выполнения, статистику клиента и чтения логического ввода / вывода (SET STATISTICS IO ON) для некоторых ваших запросов с этими таблицами.

Краткое примечание для тех, кто предлагает использовать TEXT / NTEXT от MSDN:

Эта функция будет удалена в будущая версия Microsoft SQL Сервер. Избегайте использования этой функции в новые разработки и планируют изменить приложения, которые в настоящее время используют эта особенность. Используйте varchar (max), nvarchar (max) и varbinary (max) данные типы вместо. Для дополнительной информации, см. Использование типов данных большого значения.

2 голосов
/ 18 февраля 2009

Вы хотите использовать поле ТЕКСТ. Текстовые поля не сохраняются непосредственно в строке; вместо этого он хранит указатель на текстовые данные. Однако это прозрачно для запросов - если вы запросите поле TEXT, оно вернет фактический текст, а не указатель.

По сути, использование поля TEXT - это нечто среднее между вашими двумя решениями. Это делает ваши строки таблицы намного меньше, чем при использовании varchar, но вы все равно не будете запрашивать их в своих запросах, если это возможно.

2 голосов
/ 18 февраля 2009

Я бы выбрал вариант 2.

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

1 голос
/ 18 февраля 2009

Просто расширить вариант 2

Вы могли бы:

Переименуйте существующую MyTable в MyTable_V2

Перемещение столбца «Примечания» в объединенную таблицу «Примечания» (с идентификатором объединения 1: 1)

Создайте VIEW с именем MyTable, который объединяет таблицы MyTable_V2 и Notes

Создайте триггер INSTEAD OF в представлении MyTable, в котором столбец Notes сохраняется в таблице Notes (если NULL, то удаляет любую существующую строку Notes, если NOT NULL, то Insert, если не найден, в противном случае Update). Выполните соответствующее действие для таблицы MyTable_V2

Примечание: у нас были проблемы с этим, когда в MyTable_V2 есть столбец Computed (я думаю, что это была проблема, в любом случае мы сталкивались с трудностями при работе с "необычными" таблицами)

Весь новый код вставки / обновления / удаления должен быть написан так, чтобы он работал непосредственно с таблицами MyTable_V2 и Notes

Дополнительно: иметь триггер INSERT OF в журнале MyTable, факт, что он был вызван (он может сделать это минимально, ОБНОВИТЬ ранее существующую строку таблицы журнала с помощью GetDate (), только если дата существующей строки> 24 часов - так будет обновлять только один раз в день).

Когда вы больше не получаете никаких записей журнала, вы можете сбросить триггер INSTEAD OF в представлении MyTable, и теперь вы полностью совместимы с MyTable_V2!

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

В качестве альтернативы тралите код для всех ссылок на MyTable и измените их на MyTable_V2, поместите VIEW вместо MyTable только для SELECT и не создавайте триггер INSTEAD OF.

Мой план состоит в том, чтобы исправить все операторы Insert / Update / Delete, ссылающиеся на устаревшую MyTable. Для меня это было бы несколько проще, потому что мы используем уникальные имена для всех таблиц и столбцов в базе данных, и мы используем одни и те же имена во всем коде приложения, поэтому уверенность в том, что я нашел все экземпляры простым FIND, будет высокой.

P.S. Вариант 2 также предпочтительнее, если у вас есть какие-либо SELECT * валяется. У нас были клиенты, чья производительность приложений быстро снижалась, когда они добавляли большие столбцы Text / Blob в существующие таблицы - из-за «ленивых» операторов SELECT *. Надеюсь, что дело не в вашем магазине!

1 голос
/ 18 февраля 2009

Тип данных TEXT / NTEXT имеет практически неограниченную длину, но почти ничего не занимает в вашей записи.

Он поставляется с несколькими присоединенными строками, например специальное поведение со строковыми функциями, но для вторичного поля типа «примечания / описание» это может быть меньшей проблемой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...