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

StackOverflow https://stackoverflow.com/questions/1017239

Вопрос

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

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

Это было полезно?

Решение

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

SELECT  *
FROM    table
WHERE   column IS NULL

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

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

SELECT  column
FROM    table
ORDER BY
        column

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

Если ваши значения не позволяют использовать NULL , пометьте столбец как NOT 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.

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

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

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

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

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

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

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

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

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

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

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

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

Как упоминал Квассной, нули не индексируются в Oracle, или, если быть более точным, строка не будет индексироваться, если все индексированные столбцы равны 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;

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

Тестовые данные - это 1 миллион строк x 20 столбцов, которые составлены из 62 случайных строчных букв в алфавитном порядке на i5-3320 обычном HD и 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 = page были установлены, а все остальное было по умолчанию. Нет индексов.

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

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

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

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

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

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

Обнуляемые поля могут сильно влиять на производительность, когда вы выполняете команду "NOT 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 |
---------------------------------------------------------------------------

Запрос должен проверить наличие нулевых значений, поэтому он должен выполнить полное сканирование таблицы t2 для каждой строки в 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 |
-----------------------------------------------------------------------------

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top