2009-06-19 4 views
20

В нашем продукте у нас есть общая поисковая система и попытка оптимизировать производительность поиска. Многие таблицы, используемые в запросах, допускают нулевые значения. Должны ли мы перепроектировать нашу таблицу, чтобы запретить нулевые значения для оптимизации или нет?Как значения NULL влияют на производительность в поиске базы данных?

Наш продукт работает как на Oracle, так и на MS SQL Server.

+0

Jakob, с какими проблемами с производительностью вы столкнулись с NULL? –

+0

хорошо - проблем пока нет. Но я помню, что я прочитал статью о более низкой производительности при использовании нулевых значений. Итак, в нашей команде началось обсуждение вопроса о том, нужно ли нам разрешать нулевые значения или нет, - и мы еще не пришли к какому-либо заключению. У нас есть несколько очень больших таблиц с миллионами строк в нем и с большим количеством клиентов, поэтому для проекта это довольно большое изменение. Но клиенты подняли вопрос о производительности в поисковой системе. –

+2

Если у вас есть проблемы с производительностью в поисковой системе, я бы посмотрел много других мест, прежде чем устранять нули. Начните с индексации. Посмотрите на планы выполнения, чтобы увидеть, что на самом деле происходит. Посмотрите на вас, где есть статьи, чтобы узнать, являются ли они доступными. Посмотрите, что вы возвращаете, вы использовали select * (плохо для производительности, если у вас есть соединение, поскольку одно поле, по крайней мере, повторяется, таким образом, wating nework), вы использовали подзапросы вместо соединений? Вы использовали курсор? Является ли предложение where достаточно эксклюзивным? Вы использовали подстановочный знак для первого персонажа? И дальше, и так далее. – HLGEM

ответ

19

В Oracle, NULL значения не индексируются, i. е. этот запрос:

SELECT * 
FROM table 
WHERE column IS NULL 

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

Более того, этот запрос:

SELECT column 
FROM table 
ORDER BY 
     column 

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

Если ваши значения не позволяют внутренне разрешить NULL, отметьте столбец как NOT NULL.

+1

Как будут выполняться одни и те же запросы MS SERVER? –

+6

SQL Server делает индекс NULL – Quassnoi

+3

Вы можете обойти это ограничение с помощью индекса на основе функций, в котором вы включаете литеральное значение, например CREATE INDEX MY_INDEX ON MY_TABLE (MY_NULLABLE_COLUMN, 0) –

6

Короткий ответ: да, условно!

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

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

... но, скажем, страница заполняется, и теперь эта строка обнимается среди других строк. Все еще идет хорошо ...

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

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

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

Конечно, есть и другие штрафы (в частности, объем ввода-вывода, хотя и на глубину индекса глубины), связанный с большими наборами данных, а затем у вас есть проблемы с приложениями с запретом нулей в полях, которые концептуально требуют их, но эй, это другая проблема:

+2

Установка этих столбцов NOT NULL не решит проблему «миграции строк»: если информация не известна во время вставки, будет введено другое значение по умолчанию (например, «.»), И вы все равно будете перемещать строки, когда данные заменят значение по умолчанию. В Oracle вы должны установить PCTFREE для предотвращения миграции строк. –

4

Если ваша колонка не содержит NULL, лучше всего объявить этот столбец NOT NULL, оптимизатор может иметь более эффективный путь.

Однако, если у вас есть NULL в столбце, у вас нет большого выбора (ненулевое значение по умолчанию может создавать больше проблем, чем оно решает).

Как упоминалось Quassnoi, значения NULL не индексируется в Oracle, или быть более точным, строка не будет индексироваться, если все индексированные столбцы NULL, то это означает, что:

  • , что потенциально может NULLS скорость что индекс будет содержать меньше строк.
  • вы все равно можете индексировать строки NULL, если добавить индекс NOT NULL в индекс или даже константу.

Следующий сценарий демонстрирует способ индексировать NULL значений:

CREATE TABLE TEST AS 
SELECT CASE 
      WHEN MOD(ROWNUM, 100) != 0 THEN 
      object_id 
      ELSE 
      NULL 
     END object_id 
    FROM all_objects; 

CREATE INDEX idx_null ON test(object_id, 1); 

SET AUTOTRACE ON EXPLAIN 

SELECT COUNT(*) FROM TEST WHERE object_id IS NULL; 
0

По моему опыту NULL является допустимым значением и, как правило, означает «не знаю». Если вы не знаете, то действительно бессмысленно составлять какое-то значение по умолчанию для столбца или пытаться принудительно установить ограничение NOT NULL. NULL просто является конкретным случаем.

Настоящий вызов для NULL - это усложнение поиска. Например, вы не можете сказать WHERE column_name IN (NULL, 'value1', 'value2').

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

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

+1

Я использую только null, чтобы выразить несуществующий внешний ключ (например, внешний ключ «Скидка купон» в таблице позиций счета может не существовать). Однако я не использую нули в столбцах без внешнего ключа; как вы говорите, это «обычно» означает не знать. Проблема с нулями состоит в том, что они могут означать несколько вещей - «неизвестно», «неприменимо», «не существует» (мой случай) и т. Д. В неключевых случаях вам всегда нужно сопоставить имя с NULL когда вы, наконец, обходите его. Лучше иметь такое отображение, определенное в самом столбце как реальное значение, а не дублировать отображение everytwhere. –

1

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

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

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

Я делаю много импорта данных из клиентских данных, и каждый раз, когда мы получаем файл, где какое-то поле, которое должно разрешать null, нет, мы получаем данные мусора, которые необходимо очистить, прежде чем импортировать в нашу систему. Email является одним из них. Часто данные вводятся, не зная этого значения, и обычно это какой-то тип строковых данных, поэтому пользователь может вводить что-либо здесь. Мы отправляемся на импорт электронных писем и находим вещи «Я не знаю». Трудно попытаться отправить электронное письмо на адрес «Я не знаю». Если система запрашивает действительный адрес электронной почты и проверяет что-то вроде существования знака @, мы получим «[email protected]». Как данные мусора, подобные этому, полезны для пользователей данных?

Некоторые из проблемы с нулевыми значениями являются результатом написания несходных запросов. Иногда просто переупорядочивание предложения where, а не устранение нулевого значения, может повысить производительность.

3

Неудачные поля могут иметь большое влияние на производительность при выполнении запросов «НЕ IN» Поскольку строки со всеми проиндексированными полями, установленными в null, не индексируются в индексах B-Tree, Oracle должна выполнить полное сканирование таблицы для проверки нулевого значения, даже если существует индекс.

Например:

create table t1 as select rownum rn from all_objects; 

create table t2 as select rownum rn from all_objects; 

create unique index t1_idx on t1(rn); 

create unique index t2_idx on t2(rn); 

delete from t2 where rn = 3; 

explain plan for 
select * 
    from t1 
where rn not in (select rn 
        from t2); 

--------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
--------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  | 50173 | 636K| 3162 (1)| 00:00:38 | 
|* 1 | FILTER   |  |  |  |   |   | 
| 2 | TABLE ACCESS FULL| T1 | 50205 | 637K| 24 (5)| 00:00:01 | 
|* 3 | TABLE ACCESS FULL| T2 | 45404 | 576K|  2 (0)| 00:00:01 | 
--------------------------------------------------------------------------- 

Запрос должен проверить наличие нулевых значений, так что он должен делать полное сканирование таблицы Т2 для каждой строки в t1.

Теперь, если мы сделаем поля недействительными, он может использовать индекс.

alter table t1 modify rn not null; 

alter table t2 modify rn not null; 

explain plan for 
select * 
    from t1 
where rn not in (select rn 
        from t2); 

----------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  | 2412 | 62712 | 24 (9)| 00:00:01 | 
| 1 | NESTED LOOPS ANTI |  | 2412 | 62712 | 24 (9)| 00:00:01 | 
| 2 | INDEX FULL SCAN | T1_IDX | 50205 | 637K| 21 (0)| 00:00:01 | 
|* 3 | INDEX UNIQUE SCAN| T2_IDX | 45498 | 577K|  1 (0)| 00:00:01 | 
----------------------------------------------------------------------------- 
12

Дополнительный ответ, чтобы привлечь дополнительное внимание к комментарию Дэвида Олдриджа о принятом ответе Кваснуи.

Оператор:

этот запрос:

SELECT * FROM таблица, в которой столбец IS NULL

всегда будет использовать полный просмотр таблицы

это не так. Вот пример счетчика с использованием индекса с буквальным значением:

SQL> create table mytable (mycolumn) 
    2 as 
    3 select nullif(level,10000) 
    4  from dual 
    5 connect by level <= 10000 
    6/

Table created. 

SQL> create index i1 on mytable(mycolumn,1) 
    2/

Index created. 

SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true) 

PL/SQL procedure successfully completed. 

SQL> set serveroutput off 
SQL> select /*+ gather_plan_statistics */ * 
    2 from mytable 
    3 where mycolumn is null 
    4/

    MYCOLUMN 
---------- 


1 row selected. 

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
----------------------------------------------------------------------------------------- 
SQL_ID daxdqjwaww1gr, child number 0 
------------------------------------- 
select /*+ gather_plan_statistics */ * from mytable where mycolumn 
is null 

Plan hash value: 1816312439 

----------------------------------------------------------------------------------- 
| Id | Operation  | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | 
----------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  |  1 |  |  1 |00:00:00.01 |  2 | 
|* 1 | INDEX RANGE SCAN| I1 |  1 |  1 |  1 |00:00:00.01 |  2 | 
----------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - access("MYCOLUMN" IS NULL) 


19 rows selected. 

Как вы можете видеть, этот индекс используется.

С уважением, Роб.

2

Я бы сказал, что тестирование требуется, но приятно знать опыт других народов. По моему опыту на сервере ms sql, значения NULL могут и могут вызвать серьезные проблемы с производительностью (различия). В очень простом тесте теперь я видел, как запрос возвращался через 45 секунд, когда не было установлено значение null в связанных полях в инструкции create table и более 25 минут, где она не была установлена ​​(я отказался от ожидания и просто взял пик на оценочный план запроса).

Данные испытаний - 1 миллион строк x 20 столбцов, которые построены из 62 случайных строчных альфа-символов на стандартном ядре i5-3320 и 8 ГБ оперативной памяти (SQL Server с использованием 2 ГБ)/SQL Server 2012 Enterprise Edition на Windows 8.1. Важно использовать случайные данные/нерегулярные данные, чтобы сделать тестирование реалистичным «худшим» случаем. В обоих случаях таблица воссоздавалась и перезагружалась случайными данными, которые занимали около 30 секунд в файлах базы данных, которые уже имели подходящее количество свободного места.

select count(field0) from myTable where field0 
        not in (select field1 from myTable) 1000000 

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) , ... 

vs 

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) not null, 

по соображениям эффективности обе имели параметр таблицы data_compression = набор страниц, а все остальное было по умолчанию. Нет индексов.

alter table myTable rebuild partition = all with (data_compression = page); 

Не имея аннулирует является требование в памяти оптимизировано таблиц, для которых я специально не используя однако SQL-сервер, очевидно, будет делать то, что является самым быстрым, который в данном конкретном случае, как представляется, в широком масштабе в пользу не имея провалов в данные и использование не null в таблице create.

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

select count(field19) from myTable where field19 
         not in (select field18 from myTable) 1000000 

На в стороне, не имеющие нули и не иметь дело с нулевыми случаями также делает запросы, много проще, короче, меньше ошибок и очень нормально быстрее. Если это вообще возможно, лучше избегать нулей, как правило, на сервере ms sql, по крайней мере, если они явно не требуются и не могут быть разумно разработаны из решения.

Начиная с новой таблицы и ее размера до 10 м строк/13 ГБ один и тот же запрос занимает 12 минут, что очень респектабельно, учитывая аппаратные средства и не используемые индексы. Для информационного запроса было полностью привязано IO с IO, зависающим от 20 МБ/с до 60 МБ/с. Повторение того же запроса заняло 9 минут.