Tanto en SQL Server 2000 como en 2005 uno de los inconvenientes que tenemos cuando utilizamos full-text search es que éste sistema no se encuentra integrado con el optimizador de consultas. Dicho de otra forma, una consulta que incluya parte relacional pura y parte full-text debe descomponerse en dos consultas independientes y luego operar con los resultados. Esto provoca ineficiencias que en algunos casos se solucionaban de forma artificial (por ejemplo incluyendo columnas relacionado en el texto a indexar).En SQL Server 2008 el motor se encuentra integrado y los índices full-text pasan a ser índices especializados (como los de los tipos de datos geográficos) pero que siguen estando dentro del motor relaciona. Además se ha optimizado el motor para máquinas con más procesadores y mayores cantidades de memoria intentando así obtener mejores rendimientos en las omnipresentes plataformas de 64 bits.

Para poder hacer algunas pruebas representativas decidí realizar una carga de textos a partir de unos cuantos libros gratuitos del proyecto Gutenberg (http://www.gutenberg.org). La carga incluye 33278 documentos con un total de 3.67GB de texto danto una media de 115KB por documento. Para que nos hagamos todos una idea de la longitud en texto plano que son 115KB hice una prueba de copiar sobre un documento Word uno de los registros. Con interlineado simple y letra de 11 puntos correspondió a unas 50 páginas con lo que interpolando al total estamos indexando aproximadamente más de un millón seiscientas mil páginas. A dichas tablas añadí un par de columnas fecha y país para hacer pruebas sobre consultas cruzadas con alta-baja selectividad respecto al filtro relacional. La fecha la he distribuido uniformemente de forma que cada entrada corresponde a 1 hora con lo que la diferencia entre el primer documento y el último es de casi 4 años. El país lo he distribuido entre 15 valores de forma no uniforme. La distribución que he seguido ha sido en base 2. Esto es, al primero de los países le he asignado 2 documentos, al segundo 4, al tercero 8, 16, 32, 64,… J Ambas columnas tienen un índice no cluster sobre esa única columna. Esto lo podemos conseguir fácilmente con un update en función de un identity o utilizando row_number. En mi caso he hecho un sencillo update como este basándome en una columna IDENTITY(1,1) que ya tenía definida:

update documentos

set fecha = dateadd(hour, id, ‘20050101’)

, pais = floor(log10(id+1)/log10(2))

Obteniendo este resultado:

ID    Fecha                País

1    2005-01-01 01:00:00.000    1

2    2005-01-01 02:00:00.000    1

3    2005-01-01 03:00:00.000    2

4    2005-01-01 04:00:00.000    2

5    2005-01-01 05:00:00.000    2

6    2005-01-01 06:00:00.000    2

7    2005-01-01 07:00:00.000    3

8    2005-01-01 08:00:00.000    3

9    2005-01-01 09:00:00.000    3

10    2005-01-01 10:00:00.000    3

11    2005-01-01 11:00:00.000    3

12    2005-01-01 12:00:00.000    3

13    2005-01-01 13:00:00.000    3

14    2005-01-01 14:00:00.000    3

15    2005-01-01 15:00:00.000    4

16    2005-01-01 16:00:00.000    4

….    ….                …

33278    2008-10-18 14:00:00.000    15

La primera diferencia obvia que encontramos al pasar a 2008 es el aumento de tamaño de nuestros ficheros de datos al incluirse los índices dentro de la propia base de datos. En ambos casos el tamaño ocupado por los datos es muy similar (~3.7 GB) pero en el caso de SQL Server 2008 el tamaño de los índices es de 7.3 GB. Si, no es una equivocación J Los índices full-text no son como los índices tradicionales con lo que es habitual que acaben teniendo un tamaño superior al de los datos indexados. Obviamente esto depende de muchos factores como la variabilidad de las palabras, el número de palabras de “ruido” que se descarten, etc. En SQL Server 2000 los índices se almacenan en ficheros independientes por defecto dentro de la carpeta FTDATA de la instancia en cuestión. En nuestro caso el tamaño de dichos índices es de 4.9 GB. Ciertamente la diferencia de casi un 50% en el tamaño de los índices es significativa y debe tenerse en cuenta de cara a afrontar los upgrades de versiones anteriores. Por otra parte el proceso de carga completa del índice (population) fue bastante lento en ambos casos debido principalmente a las limitaciones de la máquina en la que lo realicé (32 bits, 3Gb RAM).

En realidad la CPU no llegó a sobrepasar el 50% durante todo el proceso por lo que el subsistema de IO y la cantidad de memoria de la máquina se mostraron claramente insuficientes para esta labor. No puedo por tanto sino recomendar que implementaciones de fulltext search se realicen sobre máquinas de 64 bits con buena capacidad de entrada/salida así como memoria disponible. Casualmente, esto suele ser lo que todos deseamos para nuestros servidores de bases de datos por lo que no se requiere un tipo de servidor especializado diferente a los que utilicemos para bases de datos sin full-text search J Respecto al número de procesadores, diferentes pruebas muestran que el valor “dulce” está sobre 8 procesadores siendo complicado que a partir de esta cifra se obtenga un beneficio para la carga inicial. Obviamente, para resolver una gran cantidad de búsquedas por segundo tener más procesadores puede ayudarnos.

Por daros alguna referencia os indico que el tiempo de carga del índice en SQL Server 2000 fue de 1 hora y 40 minutos mientras que en SQL Server 2008 fue de 1 hora y 51 minutos. Aunque tenemos una diferencia de 11 minutos considero que no es significativa al tratarse de un portátil con características muy alejadas a las de un servidor. También destacar que la operación es muy intensiva en IO. Esto hizo que el portátil estuviera prácticamente “knoqueado” durante el proceso. Deberemos tener en cuenta esto para hacer estas operaciones masivas dentro de una ventana de mantenimiento o al menos en horas valle de poca actividad del sistema.

A continuación vamos a hacer algunas pruebas sobre algunas consultas puras full-text y algunas mixtas relacional + full-text para poder comparar el rendimiento del nuevo optimizador integrado. Para todas las pruebas haremos una limpieza de la caché previamente y lanzaremos 2 veces la consulta para comparar resultados en frio y en caliente.

Consultas Full-Text puras

Cuantos documentos con demonio tenemos:

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘daemon’)

Esta consulta nos devuelve 45 resultados en SQL Server 2008 y 44 en SQL Server 2000. El motivo de esto es que tanto los separadores de palabras así como el conjunto de sinónimos, etc. ha sido mejorado. Buscando el documento que existe de diferencia encontré que el problema es que en 2008 “daemon” se considera sinónimo de “demon” mientras que en SQL 2000 no. Muy probablemente ocurra algo parecido con “deamon”.

Cuantos documentos tenemos angelicales:

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel*’)

En este caso tenemos 820 resultados en ambos casos. Parece que nuestros textos son más angelicales que demoniacos J Veamos cuantos contienen ambos términos relativamente cerca.

Cuantos documentos tenemos de demonios y ángeles rondando cerca

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel NEAR daemon’)

En esta consulta obtenemos un resultado bastante sorprendente al encontrar SQL Server 2008 muchas más coincidencias de cercanía. No aparece nada en la documentación que indique que se ha aumentando la sensibilidad o el “alcance” del concepto de cercanía pero así parece ser. En SQL Server 2000 obtenemos únicamente 3 documentos mientras que en SQL Server 2008 obtenemos 26 documentos. Obviamente esto se verá reflejado en cierta forma en la entrada/salida necesaria para el conteo. Aunque podríamos extendernos utilizando muchos de las herramientas de las que tenemos disponibles (sinónimos, formas verbales, pesos, expresiones complejas, etc.) creo que este conjunto de consultas son suficientemente representativas. A continuación vamos a ver el rendimiento en consultas que combinan filtros sobre columnas de la tabla junto a filtros full-text. Para ello vamos a comprobarlo desde diferentes perspectivas de cardinalidad.

Consultas con filtros Full-Text combinados con relacional

Como filtro no selectivo para full-text utilizaremos “CONTAINS(texto,‘page’)” que devuelve 1242 filas mientras que para el relacional utilizaremos “fecha > ‘20060101’” que devuelve 24518 resultados. En el caso de filtro selectivo utilizaremosCONTAINS(texto,‘torque’)” que devuelve 12 filas y para el relacional “pais=2” que devuelve 4 filas. Para cada escenario las consultas a utilizar serán las siguientes:

Full-text selectiva, relacional selectiva

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND pais=2

Full-text selectiva, relacional no selectiva

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND fecha > ‘20060101’

Full-text no selectiva, relacional selectiva

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND pais=2

Full-text no selectiva, relacional no selectiva

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND fecha > ‘20060101’

Rendimiento 2000 vs 2008

En la siguiente tabla comparativa incluyo los valores medios de 4 ejecuciones en mi máquina de cada una de las consultas.

SQL SERVER 2000

SQL SERVER 2008

CONSULTA

CACHE

IO

CPU

Total

IO

CPU

Total

%          Mejora   

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘daemon’) FRIA

132

0

955

90

15

443

53,6%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘daemon’) CALIENTE

99

0

835

90

0

1

99,8%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel*’) FRIA

2235

0

1448

1640

32

622

57,1%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel*’) CALIENTE

1707

0

832

1640

10

14

98,3%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel NEAR daemon’) FRIA

6

0

1366

52

16

797

41,6%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘angel NEAR daemon’) CALIENTE

6

0

1015

52

4

5

99,5%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND pais=2 FRIA

30

10

879

86

31

563

35,9%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND pais=2 CALIENTE

30

0

50

86

0

54

-8,1%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND fecha > ‘20060101’ FRIA

36

16

121

86

10

495

-309,1%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘torque’) AND fecha > ‘20060101’ CALIENTE

30

0

118

86

0

57

51,6%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND pais=2 FRIA

2

16

869

3192

31

829

4,6%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND pais=2 CALIENTE

2

15

134

3192

16

64

52,2%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND fecha > ‘20060101’ FRIA

2846

31

455

3192

16

972

-113,6%

SELECT COUNT(*) FROM documentos WHERE CONTAINS(texto,‘page’) AND fecha > ‘20060101’ CALIENTE

2580

0

54

3192

0

150

-177,7%

Como podemos ver, en aquellas consultas puras full-text search el rendimiento es considerablemente mejor en todos los casos. Las mayores mejoras se encuentran cuando la consulta se ejecuta contra la caché en caliente en cuyo casi el coste de algunas de ellas es irrisorio mientras que en SQL Server 2000 necesita de un mínimo de 800ms en cada una de ellas. Cuando utilizamos escenarios mixtos donde se combinan tanto filtros relacionales como full-text la situación se vuelve mucho menos predecible. Cuando utilizamos un escenario donde ambos predicados son muy selectivos o lo es el predicado relacional el rendimiento de SQL Server 2008 está a la par o mejora bastante el de SQL Server 2000. El principal problema aparece cuando este predicado relacional no es selectivo (fecha > ‘20060101’). En ese caso, si el predicado fulltext es poco selectivo 2008 resulta ser el doble de lento que 2000. Si el predicado fulltext es selectivo, tenemos el escenario más radical de todos: con la caché fría 2008 es 3 veces más lento que 2000 y con la caché caliente es el doble de rápido.

En conclusión diremos que aunque estos resultados no son interpolables a un entorno de producción, hay ciertos indicios que la nueva funcionalidad de fulltext search muestra un mejor rendimiento en general cuando disponemos de los datos en caché o cuando la consulta es puramente sobre el índice full-text. Sin embargo, en escenarios mixtos y, especialmente en aquellas consultas únicas que puedan llegar con caché fría, el rendimiento puede ser significativamente más lento o algo mejor. Por tanto para conocer de forma fiable la ventaja de rendimiento que podemos obtener en nuestro entorno la única forma precisa es probando la carga actual de nuestro sistema SQL Server 2000 sobre una instalación de SQL Server 2008 y medir los resultados.

0 Shares:
Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like