Как значения NULL влияют на производительность поиска в базе данных?

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

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

8 ответов


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

SELECT  *
FROM    table
WHERE   column IS NULL

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

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

SELECT  column
FROM    table
ORDER BY
        column

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

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


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

инструкции:

запрос:

выберите * из таблицы, где столбец ЯВЛЯЕТСЯ 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.

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

С уважением, Грабить.


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

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

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

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

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

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

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

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


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

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

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

  • что NULLs потенциально может ускорить ваши исследования, потому что индекс будет иметь меньше строк
  • вы все равно можете индексировать нулевые строки, Если вы добавите еще один столбец NOT 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;

Nullable поля могут иметь большое влияние на производительность при выполнении запросов "не в". Поскольку строки со всеми индексированными полями, установленными в null, не индексируются в индексах B-дерева, Oracle должна выполнить полное сканирование таблицы, чтобы проверить наличие нулевых entires, даже если индекс существует.

например:

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 |
---------------------------------------------------------------------------

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

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

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 |
-----------------------------------------------------------------------------

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

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

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

Я много данных импортируется из клиентских данных, и каждый раз, когда мы получаем файл, где какое-то поле, которое должно разрешать nulls, нет, мы получаем данные мусора, которые необходимо очистить, прежде чем импортировать в нашу систему. Электронная почта-одна из них. Часто данные вводятся, не зная этого значения, и это обычно какой-то тип строковых данных, поэтому пользователь может ввести здесь что угодно. Мы идем, чтобы импортировать электронные письма и найти вещи "я не знаю". Трудно попытаться отправить электронное письмо "я не знаю". Если система запрашивает допустимое адрес электронной почты и проверяет что-то вроде существования знака@, мы получим " I@dont.знать " как мусорные данные, как это полезно для пользователей данных?

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


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

тестовые данные - 1 миллион строк x 20 столбцов, которые построены из 62 случайных строчных Альфа-символов на i5-3320 normal HD и 8GB RAM (SQL Server с использованием 2GB) / 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 = page set, и все остальное было по умолчанию. Никаких указателей.

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

отсутствие нулей является требованием для оптимизированных таблиц в памяти, для которых я специально не использую, однако sql server, очевидно, будет делать то, что является самым быстрым, что в данном конкретном случае кажется массовым в пользу отсутствия нулей в данных и использования NOT null в таблице create.

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

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

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

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


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

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

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

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