2009-06-19 18 views
20

En nuestro producto tenemos un motor de búsqueda genérico e intentamos optimizar el rendimiento de la búsqueda. Muchas de las tablas utilizadas en las consultas permiten valores nulos. ¿Deberíamos rediseñar nuestra tabla para no permitir valores nulos para la optimización o no?¿Cómo afectan los valores NULL al rendimiento en una búsqueda en la base de datos?

Nuestro producto se ejecuta en Oracle y MS SQL Server.

+0

Jakob, ¿qué tipo de problemas de rendimiento ha encontrado con NULLs? –

+0

bien - sin problemas hasta el momento. Pero recuerdo que leí un artículo sobre el rendimiento más lento al usar valores nulos. Entonces, la discusión comenzó en nuestro equipo, ya sea que permitiéramos valores nulos o no, y todavía no hemos llegado a ninguna conspiración. Tenemos algunas tablas muy grandes con millones de filas y una gran cantidad de clientes, por lo que es un cambio bastante grande para el proyecto. Pero los clientes plantearon un problema sobre el rendimiento en el motor de búsqueda. –

+2

SI tiene problemas de rendimiento en el motor de búsqueda, buscaría muchos otros lugares antes de eliminar los valores nulos. Comience con la indexación, mire los planes de ejecución para ver qué está sucediendo realmente. Mire dónde hay cláusulas para ver si son susceptibles de saqueo. Mira lo que estás devolviendo, ¿usaste select * (malo para el rendimiento si tienes un join como un campo por lo menos se repite así esperando nuevos recursos), usaste subconsultas en lugar de join? ¿Usaste un cursor? ¿Es la cláusula Where suficientemente exclusiva? ¿Usó un comodín para el primer personaje? Y sigue y sigue y sigue. – HLGEM

Respuesta

19

En Oracle, NULL valores no están indexados, i. mi. esta consulta:

SELECT * 
FROM table 
WHERE column IS NULL 

siempre utilizará la exploración de tabla completa ya que el índice no cubre los valores que necesita.

Más que eso, esta consulta:

SELECT column 
FROM table 
ORDER BY 
     column 

también utilizará escaneo completo de tabla y clasificar por la misma razón.

Si sus valores no permiten intrínsecamente NULL, marque la columna como NOT NULL.

+1

¿Cómo afectarán las mismas consultas a un MS SQL SERVER? –

+6

SQL Server indexa NULL – Quassnoi

+3

Puede solucionar esta limitación con un índice basado en funciones en el que incluye un valor literal, como CREAR ÍNDICE MY_INDEX EN MY_TABLE (MY_NULLABLE_COLUMN, 0) –

6

Respuesta corta: sí, condicionalmente!

El principal problema con los valores nulos y el rendimiento tiene que ver con las búsquedas directas.

Si inserta una fila en una tabla, con valores nulos, se coloca en la página natural a la que pertenece. Cualquier consulta que busque ese registro lo encontrará en el lugar apropiado. Fácil hasta ahora ...

... pero digamos que la página se llena, y ahora esa fila está acurrucada entre las otras filas. Sigue yendo bien ...

... hasta que se actualice la fila, y el valor nulo ahora contiene algo. El tamaño de la fila ha aumentado más allá del espacio disponible, por lo que el motor de DB tiene que hacer algo al respecto.

Lo más rápido que puede hacer el servidor es mover la fila de a otra página y reemplazar la entrada de la fila con un puntero hacia adelante. Desafortunadamente, esto requiere una búsqueda adicional cuando se realiza una consulta: una para encontrar la ubicación natural de la fila y otra para encontrar su ubicación actual.

Por lo tanto, la respuesta breve a su pregunta es afirmativa, por lo que los campos no anulables ayudarán al rendimiento de la búsqueda. Esto es especialmente cierto si a menudo sucede que los campos nulos en los registros que busca se actualizan a no nulo.

Por supuesto, hay otras sanciones (especialmente E/S, aunque en una pequeña medida profundidad de índice) asociadas con conjuntos de datos más grandes, y luego tiene problemas de aplicación al rechazar nulos en campos que conceptualmente lo requieren, pero bueno, eso es otro problema :)

+2

Establecer esas columnas NOT NULL no resolverá el problema de "migración de filas": si no se conoce la información en el momento de la inserción, se ingresará otro valor predeterminado (como '.') Y todavía tendrá filas migradas cuando sea real los datos reemplazarán el valor predeterminado. En Oracle, debe configurar PCTFREE de manera adecuada para evitar la migración de filas. –

4

Si su columna no contiene valores NULL, es mejor declarar esta columna NOT NULL, el optimizador puede tomar una ruta más eficiente.

Sin embargo, si tiene valores NULL en la columna, no tiene muchas opciones (un valor predeterminado no nulo puede crear más problemas de los que resuelve).

Como mencionado Quassnoi, valores NULL no se indexan en Oracle, o para ser más precisos, una fila no será indexado si todas las columnas indexadas son NULL, esto significa:

  • que NULL puede potencialmente velocidad su investigación porque el índice tendrá menos filas
  • aún puede indexar las filas NULAS si agrega otra columna NOT NULL al índice o incluso a una constante.

La siguiente secuencia de comandos muestra una forma de valores NULL índice:

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

En mi experiencia nulo es un valor válido y por lo general significa "no sé". Si no lo sabe, entonces no tiene sentido crear un valor predeterminado para la columna o intentar aplicar alguna restricción NOT NULL. NULL resulta ser un caso específico.

El verdadero desafío para NULLs es complicar la recuperación un poco. Por ejemplo, no puede decir WHERE column_name IN (NULL, 'value1', 'value2').

Personalmente si encuentra muchas de sus columnas, o ciertas columnas contienen una gran cantidad de NULL, creo que es posible que desee volver a visitar su modelo de datos. ¿Tal vez esas columnas nulas se pueden poner en una tabla secundaria? Por ejemplo: una tabla con números de teléfono donde aparezca su nombre, teléfono de casa, teléfono celular, faxno, número de trabajo, número de emergencia, etc. ... Solo puede completar uno o dos de ellos y sería mejor normalizarlo.

Lo que necesita hacer es dar un paso atrás y ver cómo se accederá a los datos. ¿Es esta una columna que debería tener un valor? ¿Es esta una columna que solo tiene un valor para ciertos casos? ¿Es esta una columna que será consultada mucho?

+1

Solo uso nulo para expresar una clave externa inexistente (por ej., una clave externa "Cupón de descuento" en una tabla de artículo de factura podría no existir). Sin embargo, no utilizo valores nulos en columnas clave no foráneas; como dices, "normalmente" significa que no sabes. El problema con los nulos es que pueden significar varias cosas: "desconocido", "no aplicable", "no existe" (mi caso), etc. En los casos que no son clave, siempre tendrá que asignar un nombre al NULO campo cuando finalmente consigas usarlo. Es mejor tener ese mapeo valuado definido en la columna misma como un valor real en lugar de duplicar el mapeo en todas partes. –

1

La cuestión de si utilizar valores nulos porque afectan el rendimiento es uno de esos actos de equilibrio del diseño de la base de datos. Debe equilibrar las necesidades comerciales con el rendimiento.

Se deben usar nulos si son necesarios. Por ejemplo, puede tener una fecha de inicio y una fecha de finalización en una tabla. A menudo no sabría la fecha de finalización en el momento en que se crea el registro. Por lo tanto, debe permitir los valores nulos si afectan el rendimiento o no, ya que los datos simplemente no están allí para colocarlos. Sin embargo, si los datos deben estar, según las reglas comerciales, en el momento en que se crea el registro, entonces no debe permitir nulos. Esto mejoraría el rendimiento, simplificaría la codificación y garantizaría la integridad de los datos.

Si tiene datos existentes que le gustaría cambiar para no permitir nulos, entonces debe considerar el impacto de ese cambio. Primero, ¿sabe qué valor necesita poner en los registros que actualmente son nulos? En segundo lugar, ¿tiene un montón de código que usa isnull o coalesce que necesita actualizar (estas cosas disminuyen el rendimiento, por lo que si ya no necesita verificarlas, debe cambiar el código)? ¿NECESITA UN VALOR POR DEFECTO? ¿Puedes realmente asignar uno? De lo contrario, algunos de los códigos de inserción o actualización se romperán si no se considera que el campo ya no puede ser nulo. A veces las personas pondrán información incorrecta para permitirles deshacerse de los nulos. Por lo tanto, ahora el campo de precios debe contener valores decimales y cosas como "desconocido" y, por lo tanto, no puede ser correctamente un tipo de datos decimales y luego hay que ir a todo tipo de longitudes para hacer cálculos. Esto a menudo crea problemas de rendimiento como malos o peores que los nulos creados. Además, necesita revisar todo el código y siempre que haya utilizado una referencia al archivo como nulo o no sea nulo, debe volver a escribir para excluir o incluir en función de los posibles valores incorrectos que alguien ingresará, ya que los datos no están permitidos. ser nulo

Realizo una gran cantidad de importaciones de datos a partir de datos de clientes y cada vez que obtenemos un archivo donde algún campo que debería permitir nulos no lo hace, obtenemos datos basura que deben limpiarse antes de importarlos a nuestro sistema. El correo electrónico es uno de estos. A menudo los datos se ingresan sin conocer este valor y generalmente es algún tipo de datos de cadena, por lo que el usuario puede escribir cualquier cosa aquí. Vamos a importar correos electrónicos y encontrar cosas que "no sé". Es difícil tratar de enviar un correo electrónico a "No sé". Si el sistema requiere una dirección de correo electrónico válida y comprueba algo así como la existencia de un signo @, obtendríamos '[email protected]' ¿Cómo es útil la información basura como esta para los usuarios de los datos?

Algunos de Los problemas de rendimiento con valores nulos son el resultado de escribir consultas no extraíbles. A veces, reorganizar la cláusula where en lugar de eliminar un valor nulo necesario puede mejorar el rendimiento.

3

Los campos anulables pueden tener un gran impacto en el rendimiento al hacer consultas "NO ENCENDIDAS" Debido a que las filas con todos los campos indexados configurados como nulos no están indexados en los índices de un árbol B, Oracle debe realizar un escaneo completo de la tabla para buscar entidadas nulas, incluso cuando existe un índice.

Por ejemplo:

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

La consulta tiene que comprobar si hay valores nulos por lo que tiene que hacer un escaneo completo de tabla de t2 para cada fila de T1.

Ahora, si hacemos que los campos no se puedan anular, puede usar el índice.

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

Una respuesta extra para llamar la atención sobre el comentario de David Aldridge sobre la respuesta aceptada por Quassnoi.

La declaración:

esta consulta:

SELECT * FROM tabla WHERE columna ES NULO

siempre utilizará escaneo completo de tabla

no es cierto. Aquí está el ejemplo de contador que usa un índice con un valor literal:

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. 

Como puede ver, el índice se está utilizando.

Saludos, Rob.

2

Yo diría que las pruebas son necesarias, pero es bueno conocer las experiencias de otras personas. En mi experiencia en ms sql server, los nulos pueden y causan problemas de rendimiento masivo (diferencias). En una prueba muy simple ahora he visto un retorno de consulta en 45 segundos cuando no se estableció nulo en los campos relacionados en la declaración de creación de la tabla y más de 25 minutos en los que no se configuró (abandoné la espera y simplemente hice un pico en el plan de consulta estimado).

Los datos de prueba son 1 millón de filas x 20 columnas que se construyen a partir de 62 caracteres alfabéticos aleatorios en minúscula en una HD normal i5-3320 y 8 GB de RAM (SQL Server con 2 GB)/SQL Server 2012 Enterprise Edition en Windows 8.1. Es importante usar datos aleatorios/datos irregulares para hacer que las pruebas sean un "caso peor" realista. En ambos casos, la tabla se volvió a crear y volvió a cargar con datos aleatorios que tardaron unos 30 segundos en archivos de bases de datos que ya tenían una cantidad adecuada de espacio libre.

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, 

por motivos de rendimiento, ambos tenían la opción de tabla data_compression = page set y todo lo demás estaba predeterminado. Sin índices

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

No tener valores nulos es un requisito en las tablas de memoria optimizado para los que no estoy usando específicamente obstante servidor SQL, obviamente, hará lo que es más rápido que en este caso concreto parece ser masivamente a favor de no tener valores nulos en datos y usando no nulo en la tabla crear.

Cualquier consulta posterior de la misma forma en esta tabla regresa en dos segundos, por lo que supondría que las estadísticas predeterminadas estándar y posiblemente tener la mesa (1.3GB) en la memoria funcionan bien. decir

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

En un aparte que no tengan valores nulos y no tener que lidiar con los casos nulos también hace consultas mucho más simple, más corto, menos propenso a errores y muy normalmente más rápido. De ser posible, mejor evitar los nulos generalmente en el servidor ms sql al menos, a menos que sean explícitamente necesarios y no se puedan resolver razonablemente con la solución.

Comenzando con una nueva tabla y dimensionando esto hasta 10m filas/13GB, la misma consulta toma 12 minutos, lo que es muy respetable teniendo en cuenta el hardware y no hay índices en uso. Para la consulta de información estaba completamente enlazado a IO con IO flotando entre 20MB/sa 60MB/s. Una repetición de la misma consulta tomó 9 minutos.

Cuestiones relacionadas