Las instrucciones de
procesamiento pueden ser una operación costosa, pero las bases de datos se
escriben ahora de tal manera que se minimiza esta sobrecarga. Sin embargo,
estas optimizaciones necesitan ayuda de los desarrolladores de aplicaciones si
queremos aprovecharlas. En este artículo se muestra cómo el uso correcto de las
instrucciones preparadas puede ayudar significativamente a una base de datos
realizar estas optimizaciones.
Las bases de datos tienen un trabajo
duro. Ellos aceptan consultas SQL de muchos clientes simultáneamente y ejecutan
las consultas de la manera más eficiente posible contra los datos. Las
instrucciones de procesamiento pueden ser una operación costosa, pero las bases
de datos se escriben ahora de tal manera que se minimiza esta sobrecarga. Sin
embargo, estas optimizaciones necesitan ayuda de los desarrolladores de
aplicaciones si queremos aprovecharlas. En este artículo se muestra cómo el uso
correcto de PreparedStatements puede ayudar significativamente a una base de
datos realizar estas optimizaciones.
¿Cómo ejecuta una base de datos una sentencia?
Obviamente, no esperes mucho
detalle aquí; sólo examinaremos los aspectos importantes para este artículo.
Cuando una base de datos recibe una sentencia, el motor de base de datos
analiza primero la sentencia y busca errores de sintaxis. Una vez analizada la
sentencia, la base de datos debe encontrar la forma más eficiente de ejecutar
la sentencia. Esto puede ser computacionalmente bastante caro. La base de datos
comprueba qué índices, si los hay, pueden ayudar o si debe realizar una lectura
completa de todas las filas de una tabla. Las bases de datos utilizan
estadísticas sobre los datos para averiguar cuál es la mejor manera. Una vez
creado el plan de consulta, el motor de base de datos puede ejecutarlo.
Toma la energía de la CPU para
hacer la generación del plan de acceso. Idealmente, si enviamos la misma
declaración a la base de datos dos veces, entonces nos gustaría que la base de
datos reutilizar el plan de acceso para la primera declaración. Esto utiliza
menos CPU que si regeneró el plan una segunda vez.
Cachés de sentencias
Las bases de datos se sintonizan
para hacer cachés de sentencias. Por lo general incluyen algún tipo de caché de
instrucciones. Esta caché utiliza la instrucción en sí como una clave y el plan
de acceso se almacena en la caché con la instrucción correspondiente. Esto
permite al motor de base de datos reutilizar los planes para las sentencias que
se han ejecutado anteriormente. Por ejemplo, si enviamos a la base de datos una
sentencia como "select a, b from t where
c = 2", entonces el plan de acceso calculado se almacena en caché. Si
enviamos la misma sentencia más adelante, la base de datos puede volver a
utilizar el plan de acceso anterior, ahorrándonos energía de la CPU.
Tenga en cuenta, sin embargo, que
toda la sentencia es la clave. Por ejemplo, si luego enviamos la sentencia
"seleccione a, b de t donde c = 3", no encontraría un plan de acceso.
Esto se debe a que el "c = 3" es diferente del plan en caché "c
= 2". Así por ejemplo:
For(int I = 0; I < 1000; ++I) { PreparedStatement ps = conn.prepareStatement("select a,b from t where c = " + I); ResultSet rs = Ps.executeQuery(); Rs.close(); Ps.close(); }
Aquí no se utilizará el caché.
Cada iteración del bucle envía una instrucción SQL diferente a la base de
datos. Se calcula un nuevo plan de acceso para cada iteración y básicamente
estamos lanzando ciclos de CPU usando este enfoque. Sin embargo, mira el
siguiente fragmento:
PreparedStatement
ps = conn.prepareStatement("select a,b from t where c = ?");
PreparedStatement ps = conn.prepareStatement("select a,b from t where c = ?"); For(int I = 0; I < 1000; ++I) { ps.setInt(1, I); ResultSet rs = ps.executeQuery(); Rs.close(); } ps.close();
Aquí será mucho más eficiente. La
sentencia enviada a la base de datos se parametriza usando el signo '?'
marcador en el sql. Esto significa que cada iteración está enviando la misma
sentencia a la base de datos con diferentes parámetros para el "c =?"
parte. Esto permite que la base de datos reutilice los planes de acceso para la
sentencia y haga que el programa se ejecute más eficientemente dentro de la
base de datos. Esto básicamente permite que su aplicación se ejecute más rápido
o haga más CPU disponible para los usuarios de la base de datos.
PreparedStatements y servidores J2EE
Las cosas pueden ser más
complicadas cuando usamos un servidor J2EE. Normalmente, una sentencia
preparada se asocia con una conexión de base de datos única. Cuando se cierra
la conexión, se descarta el estado de preparación. Normalmente, una aplicación
de cliente de grasa obtendría una conexión de base de datos y luego la
mantendría durante toda su vida útil. También crearía todas las declaraciones
preparadas con avidez o pereza. Ansiosamente significa que todos ellos se crean
al mismo tiempo cuando se inicia la aplicación. Lazily significa que se crean
como se utilizan. Un enfoque ansioso da un retraso cuando la aplicación se
inicia, pero una vez que se inicia, entonces se realiza de manera óptima. Un
enfoque lento proporciona un inicio rápido, pero a medida que se ejecuta la
aplicación, las sentencias preparadas se crean cuando son utilizadas por
primera vez por la aplicación. Esto da un rendimiento desigual hasta que se
preparan todas las sentencias, pero la aplicación finalmente se instala y se
ejecuta tan rápido como la aplicación ansiosa. Cuál es el mejor depende de si
usted necesita un comienzo rápido o aún funcionamiento.
El problema con una aplicación
J2EE es que no puede funcionar así. Sólo mantiene una conexión durante la
duración de la solicitud. Esto significa que debe crear las instrucciones
preparadas cada vez que se ejecuta la solicitud. Esto no es tan eficiente como
el enfoque de cliente de grasa donde las sentencias preparadas se crean una
vez, en lugar de en cada solicitud. Los proveedores de J2EE han notado esto y
han diseñado la agrupación de conexiones para evitar esta desventaja de
rendimiento.
Cuando el servidor J2EE da a su
aplicación una conexión, no le está dando la conexión real; usted está
consiguiendo un envoltorio. Puede verificar esto mirando el nombre de la clase
para la conexión que se le da. No será una conexión de base de datos JDBC, será
una clase creada por su servidor de aplicaciones. Normalmente, si llama cerca
de una conexión, entonces el controlador jdbc cierra la conexión. Queremos que
la conexión sea devuelta a la agrupación cuando close sea llamada por una
aplicación J2EE. Hacemos esto haciendo una clase de conexión de proxy jdbc que
parece una conexión real. Tiene una referencia a la conexión real. Cuando
invocamos cualquier método en la conexión entonces el proxy reenvía la llamada
a la conexión real. Pero, cuando llamamos a métodos como cerrar en lugar de
llamar cerca de la conexión real, simplemente devuelve la conexión al grupo de
conexiones y luego marca la conexión proxy como no válida, de modo que si la
utiliza de nuevo, obtendremos una excepción.
El empaquetado es muy útil, ya
que también ayuda a los implementadores de servidores de aplicaciones J2EE a
agregar soporte para sentencias preparadas de una manera sensible. Cuando una
aplicación llama Connection.prepareStatement, el controlador devuelve un objeto
PreparedStatement. La aplicación, a continuación, mantiene el identificador
mientras tiene la conexión y lo cierra antes de que cierra la conexión cuando
finaliza la solicitud. Sin embargo, después de que se devuelva la conexión a la
agrupación y más tarde reutilizada por la misma u otra aplicación, idealmente,
queremos que se devuelva el mismo PreparedStatement a la aplicación.
J2EE PreparedStatement Cache
J2EE PreparedStatement Cache se
implementa utilizando un caché dentro del gestor de la agrupación de conexiones
del servidor J2EE. El servidor J2EE mantiene una lista de instrucciones preparadas
para cada conexión de base de datos en el grupo. Cuando una aplicación llama a
prepareStatement en una conexión, el servidor de aplicaciones comprueba si
dicha sentencia fue preparada previamente. Si lo fuera, el objeto
PreparedStatement estará en el caché y éste será devuelto a la aplicación. Si
no, la llamada se pasa al controlador jdbc y se agrega el objeto query /
preparedstatement en esa caché de conexiones.
Necesitamos una caché por
conexión, porque así funcionan los controladores jdbc. Cualquier estado de
preparación devuelto es específico de esa conexión.
Si queremos aprovechar esta
caché, las mismas reglas se aplican como antes. Necesitamos usar consultas
parametrizadas para que coincidan con las ya preparadas en la caché. La mayoría
de los servidores de aplicaciones le permitirán ajustar el tamaño de este caché
de instrucciones preparado.
En resumen
En conclusión, debemos utilizar
consultas parametrizadas con declaraciones preparadas. Esto reduce la carga de
la base de datos permitiéndole reutilizar planes de acceso ya preparados. Esta
caché es de toda la base de datos, por lo que si puede organizar todas sus
aplicaciones para utilizar SQL parametrizado similar, mejorará la eficiencia de
este esquema de almacenamiento en caché como una aplicación puede aprovechar
las sentencias preparadas utilizadas por otra aplicación. Esta es una ventaja
de un servidor de aplicaciones porque la lógica que accede a la base de datos
debe centralizarse en una capa de acceso a datos (ya sea un mapeador OR, un
beans de entidad o un JDBC recto).
Por último, el uso correcto de
sentencias preparadas también le permite aprovechar la caché de instrucciones
preparada en el servidor de aplicaciones. Esto mejora el rendimiento de su
aplicación, ya que la aplicación puede reducir el número de llamadas al
controlador JDBC mediante la reutilización de una llamada de sentencia
previamente preparada. Esto hace que sea competitivo con los clientes de grasa
en función de la eficiencia y elimina la desventaja de no poder mantener una
conexión dedicada.
Si utiliza sentencias preparadas parametrizadas,
mejora la eficiencia de la base de datos y del código alojado de su servidor de
aplicaciones. Ambas
mejoras permitirán que su aplicación mejore su rendimiento.
No hay comentarios:
Publicar un comentario
Por favor deja tu comentario, es valioso.