Evitando actualizaciones concurrentes del mismo registro

2 comentarios

Como ya hemos visto, hay técnicas para evitar conflictos entre transacciones, esos conflictos ocurren cuando dos o más de ellas quieren actualizar o borrar el mismo registro al mismo tiempo.

Evitando conflictos en las transacciones

Pero a veces no podemos usar esas técnicas porque debemos estar actualizando a los mismos registros concurrentemente, sí o sí. ¿Cómo resolvemos el problema en ese caso?

Bien, primero veamos de manera simplificada cuales son las tareas que realiza el motor del Firebird cuando una transacción READ WRITE READ COMMITTED quiere actualizar un registro:

  1. Lee el registro
  2. Evalúa los datos que se guardarán en el nuevo registro
  3. Escribe el nuevo registro en una página de datos

Recuerda que en Firebird cada vez que se usa el comando UPDATE o el comando DELETE se le está agregando un nuevo registro a la Base de Datos. Si la transacción finaliza con un COMMIT entonces el nuevo registro será el que vale, el que sirve, el que se usará de ahí en adelante y el registro viejo será basura. En cambio, si la transacción finaliza con un ROLLBACK el registro anterior seguirá siendo usado y el nuevo pasará a engrosar la basura. Como ves, en ambos casos se genera basura, y ese es el motivo por el cual periódicamente hay que eliminarla.

¿Y cuál es el problema con esta forma de proceder del Firebird?

Una transacción que se inició con los parámetros anteriores, en el paso 1. lee el último registro confirmado (o sea, que finalizó con un COMMIT) y al llegar al paso 3. vuelve a leerlo para asegurarse que se trata del mismo registro. Si no lo hiciera entonces podría estar silenciosamente sobreescribiendo el trabajo de otra persona.

Si el registro que leyó en el paso 1. es distinto del registro que leyó en el paso 3. entonces lanzará una excepción que nos informará que hay un error en la actualización. Un deadlock. Un bloqueo mortal. La actualización falló.

El tiempo que el Firebird demora en completar los pasos del 1. al 3. es pequeñísimo, se mide en milisegundos, así que usualmente no tendremos problemas con eso … salvo cuando varios usuarios estén actualizando a muchísimos registros al mismo tiempo, pues allí sí tales circunstancias podrían darse.

El Firebird solamente “bloquea” a un registro cuando lo está actualizando, mientras no lo esté actualizando el registro está desbloqueado. Esto significa que si queremos bloquear a un registro, debemos actualizarlo, aunque sea escribiendo en él lo mismo que ya tenía escrito.

Y eso es lo que hace el comando SELECT … WITH LOCK.

Y es ese comando el que deberíamos usar cuando queremos ejecutar un proceso de actualización y queremos tener la seguridad de que no tendremos conflictos con otras transacciones. Esta es una forma de transacción pesimista, la cual aunque no es recomendada en Firebird a veces puede ser necesario usar y por ese motivo es que existe.

El algoritmo entonces sería el siguiente:

SET TRANSACTION 
READ WRITE 
WAIT 
READ COMMITTED 
NO RECORD_VERSION 
LOCK TIMEOUT 5 ;
SELECT MisColumnas FROM MiTabla WHERE MiCondición WITH LOCK ;
UPDATE MiTabla SET MisColumnas = MisNuevosValores WHERE MiCondición ;

¿Qué se hizo aquí?

Primero: se establecieron los parámetros de la transacción:

READ WRITE: La transacción puede leer y también puede escribir en los registros

WAIT: Si otra transacción está actualizando el registro, esta transacción esperará

READ COMMITTED: Leerá la última versión confirmada del registro, sin importar cuando ese registro fue confirmado. Si antes de que esta transacción empezara, o después.

NO RECORD_VERSION: No podrá leer un registro que otra transacción está actualizando

LOCK TIMEOUT 5: Esperará un máximo de 5 segundos hasta que la otra transacción que está actualizando al registro finalice con un COMMIT o con un ROLLBACK. Desde luego que podrías poner más o menos segundos, como te parezca.

Segundo: se consultaron y se bloquearon los registros. Al usar la cláusula WITH LOCK le estamos diciendo que también haga un UPDATE silencioso, para que ninguna otra transacción pueda actualizar a este registro antes de que nosotros lo actualicemos. Fíjate que este segundo paso puede fallar, fallará si ya otra transacción tiene bloqueado a cualquiera de los registros de MiTabla que cumplen con MiCondición.

Tercero: se actualizaron los registros. Si el segundo paso finalizó con éxito entonces este tercer paso también finalizará con éxito. Para eso justamente sirve el SELECT … WITH LOCK, para asegurarnos de que el UPDATE finalizará con éxito.

Observaciones:

Este método es muy efectivo, aunque raramente necesario. En Firebird lo normal y lo correcto es que las transacciones sean optimistas y este es un ejemplo de transacción pesimista. Por ello lo recomendable es que los registros que son bloqueados por el SELECT … WITH LOCK sean muy pocos, idealmente solamente un registro y no más de uno.

La ventaja de este método es que si el SELECT … WITH LOCK finalizó con éxito entonces tendremos una seguridad del 100% de que el UPDATE también finalizará con éxito. Desde luego que para que eso sea verdad, MiCondición debe ser la misma tanto en el SELECT como en el UPDATE. Si ponemos condiciones distintas, nada podremos asegurar.

Conclusión:

En general, y como regla, deberíamos evitar usar los comandos UPDATE y DELETE ya que los conflictos entre transacciones solamente pueden ocurrir cuando se usan esos comandos.

Sin embargo, a veces es necesario usar esos comandos, no podemos evitarlo. En esos casos casi siempre es preferible que nuestras transacciones sean optimistas, porque el Firebird está optimizado para usar transacciones optimistas. Solamente cuando no tenemos alternativa deberíamos usar transacciones pesimistas, y en este artículo se vio una técnica de ello.

Artículos relacionados:

Entendiendo a las transacciones

Modos de bloqueo de las transacciones

Transacciones optimistas y transacciones pesimistas

Algo más sobre transacciones optimistas y transacciones pesimistas

¿Por qué en Firebird es preferible que las transacciones sean optimistas?

Evitando conflictos en las transacciones

Entendiendo las páginas de la Base de Datos

El índice del blog Firebird21

El foro del blog Firebird21

 

Evitando conflictos en las transacciones

2 comentarios

A las bases de datos de Firebird normalmente se conectan muchos usuarios y en ocasiones quieren actualizar o borrar la misma fila y al mismo tiempo y eso puede causar conflictos. ¿Cómo los evitamos?

Primero, debemos tener presente que es normal que existan conflictos y que debemos prepararnos para lidiar con ellos.

Segundo, en muchos casos es posible evitar esos conflictos si diseñamos inteligentemente la Base de Datos.

Evitando los conflictos

Los conflictos solamente pueden ocurrir cuando dos o más usuarios quieren actualizar o borrar la misma fila y al mismo tiempo. O sea, cuando se usa el comando UPDATE o el comando DELETE.

Por lo tanto, lo mejor es evitar usar esos comandos y solamente usar el comando INSERT.

¿Y cómo evitamos usar el comando UPDATE o el comando DELETE?

Hay dos técnicas:

  1. Guardar los movimientos. Sería el caso si por ejemplo tenemos una tabla de PRODUCTOS y se hacen compras y ventas de esos productos. Mientras se están haciendo las compras y las ventas jamás actualizamos la tabla de PRODUCTOS, solamente insertamos filas a nuestra tabla de movimientos.
  2. Usando una tabla auxiliar. Sería el caso cuando no guardamos los datos en una tabla de movimientos. Por ejemplo, cuando queremos cambiar el precio de venta de un producto.

En ambos casos, esperamos que nadie esté conectado a la Base de Datos (o al menos, que nadie esté usando la tabla de PRODUCTOS) y mediante un stored procedure hacemos las actualizaciones correspondientes.

En otras palabras, sí usaremos el comando UPDATE o el comando DELETE pero los usaremos cuando estamos 100% seguros de que no pueden ocurrir conflictos.

Ventaja de evitar los conflictos:

  • El trabajo de los usuarios que registran movimientos o actualizaciones es más rápido, porque nunca se encontrarán con una situación de tener que esperar o de deadlock (o sea, una actualización o borrado que falló).

Desventaja de evitar los conflictos:

  • El usuario que debe consultar los datos (mediante el comando SELECT) obtendrá más lentamente los resultados porque los datos se encontrarán en dos o más tablas, no solamente en una tabla.

Conclusión:

Lo normal es que sea preferible evitar los conflictos, ya que los usuarios que registran movimientos o actualizaciones constantemente están trabajando y los conflictos los afectarán negativamente. Además generalmente son muchos más que los usuarios que solamente consultan datos. Quienes necesitan consultar datos generalmente son muy pocas personas y además lo hacen esporádicamente, y con la gran velocidad de las computadoras actuales solamente podrían tardar unos segundos más en obtener un informe, así que no vale la pena preocuparse demasiado por ellos.

Por lo tanto, en el trabajo del día a día es conveniente usar solamente el comando INSERT, y usar el comando UPDATE o el comando DELETE en procesos de actualización que se realizan cuando nadie está usando la Base de Datos (o cuando nadie está usando la tabla que será actualizada).

Si un usuario no puede grabar una fila porque otro usuario tiene bloqueada la fila, eso es muy molesto y debemos hacer lo posible por evitar ese tipo de situaciones.

Artículos relacionados:

Bloqueos mortales

Modos de bloqueo de las transacciones

Transacciones optimistas y transacciones pesimistas

Usando transacciones con acceso exclusivo a tablas

Algo más sobre transacciones optimistas y transacciones pesimistas

¿Por qué en Firebird es preferible que las transacciones sean optimistas?

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Usando una tabla autoreferenciada para averiguar números consecutivos faltantes

Deja un comentario

Hay varios métodos para conocer si una serie de números está en secuencia. Es decir si están como 1, 2, 3, 4, 5, 6, 7, 8, etc. o si falta algún número.

Uno de esos métodos es mediante el uso de una tabla autoreferenciada, como en el siguiente ejemplo:

SELECT
   P1.PRD_IDENTI
FROM
   PRODUCTOS P1
LEFT JOIN
   PRODUCTOS P2
      ON P1.PRD_IDENTI = P2.PRD_IDENTI - 1
WHERE
   P2.PRD_IDENTI IS NULL

Como puedes ver, la tabla PRODUCTOS le hace un LEFT JOIN a la tabla … PRODUCTOS. Por eso se dice que la tabla se referencia a sí misma.

Entonces, para saber si los valores de la columna PRD_IDENTI son consecutivos o no, la referencia se hace al valor de PRD_IDENTI menos uno.

Si hay números faltantes entonces la condición no se cumplirá. ¿Y cómo podemos saber que la condición no se cumplió? porque en ese caso el valor de PRD_IDENTI de la tabla relacionada será NULL.

Si nuestra tabla de PRODUCTOS tiene estos datos:

AUTOREF1

Captura 1. Si haces clic en la imagen la verás más grande

Entonces el SELECT anterior nos mostrará esto:

AUTOREF2

Captura 2. Si haces clic en la imagen la verás más grande

 ¿Qué significa? Que después del 25 no está el 26; ni después del 35 está el 36. ¿Y por qué aparece el 38? Porque es el valor que tiene PRD_IDENTI en la última fila y evidentemente como está en la última fila no habrá un valor mayor que él. Entonces siempre el valor que se encuentra en la última fila aparecerá, ese es un pequeño detalle a tener en cuenta.

Desventajas de este método

Este método funcionará siempre pero tiene una gran desventaja: está usando un LEFT JOIN y eso hará que la cantidad de filas leídas sea muy grande (no olvides que un JOIN es la multiplicación de las filas de la tabla de la izquierda por las filas de la tabla de la derecha). En tablas grandes este método puede ser increíblemente lento, pero para tablas pequeñas puede ser útil.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Averiguando cual es la arquitectura que tienes instalada

2 comentarios

¿Cómo saber si el Servidor del Firebird es SuperServer, Classic, o SuperClassic?

Realmente sería interesante que mediante un SELECT pudiéramos tener esa información, lamentablemente no es así pero de todas maneras podemos averiguarlo fácilmente. Hay dos métodos que podemos usar:

Método 1.

  1. Abrir como Administrador la ventana “Símbolo del sistema
  2. Ubicarse en la subcarpeta \BIN de la carpeta donde está instalado el Firebird
  3. Escribir el comando INSTSVC Q

Firebird puede ser instalado muchas veces en una computadora, para diferenciar a cada una de esas instalaciones de las demás a cada una hay que darle un nombre. El nombre por defecto, o sea el que le asigna el instalador es “DefaultInstance”.

Si tenemos más de una instalación del Firebird en una computadora, las demás instalaciones deberán tener otros nombres.

ARQUITECTURA1Captura 1. Si haces clic en la imagen la verás más grande

(1). La ventanita se abrió como Administrador. Para abrir un programa como Administrador debes colocar el mouse sobre su nombre, presionar el botón derecho y en el menú contextual que aparece elegir la opción: “Ejecutar como Administrador”

(2). El nombre de esta instalación es “DefaultInstance”, que es el nombre por defecto. Si hay más de una instalación del Firebird entonces podría aparecer otro nombre aquí.

(3). Los nombres de los archivos ejecutables pueden ser:

fbserver.exe —> significa que se está ejecutando SuperServer

fb_inet_server.exe —> significa que se está ejecutando Classic o SuperClassic. Para saber cual de ellos hay que mirar cual es el parámetro que viene a continuación: -c o ningún parámetro, es Classic. -s o -m es SuperClassic.

Método 2

  1.  Iniciar el Administrador de Tareas del Windows
  2. Hacer clic en la pestaña Procesos
  3. Hacer clic en el botón Mostrar procesos de todos los usuarios
  4. Si aparece una ventanita de diálogo preguntando: ¿Desea permitir que este programa realice cambios en el equipo? responderle que sí
  5. En la lista de procesos verás a fbserver.exe o a fb_inet_server.exe.

Por defecto Firebird se instala en SYSTEM.

Conclusión:

Aunque lamentablemente no disponemos de un SELECT que nos pueda decir la arquitectura del Servidor, es muy fácil obtener esa información y tenemos dos métodos para obtenerla.

Mediante el método 1 abrimos como Administrador una ventanita de comandos, nos ubicamos en la subcarpeta \BIN de nuestra instalación del Firebird y escribimos el comando INSTSVC Q

Mediante el método 2 iniciamos el Administrador de tareas del Windows, le pedimos que nos muestre todos los procesos y listo.

En ambos casos, fbserver.exe significa SuperServer y fb_inet_server.exe significa Classic o SuperClassic, para saber cual de ellos hay que fijarse en el parámetro que aparece a continuación: -c o nada, Classic, -s o -m, SuperClassic.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Usando parámetros para devolver la cantidad de filas

1 comentario

Como recordarás, cuando queremos limitar la cantidad de filas que devuelve un SELECT podemos usar la cláusula FIRST … SKIP o la cláusula ROWS. Cuando escribimos un stored procedure a veces no conocemos de antemano cuantas filas necesitaremos, o en otras palabras la cantidad de filas que devolverá el stored procedure es variable.

Entonces, ¿cómo lo solucionamos?

Rodeando a nuestra variable con paréntesis, como en los siguientes ejemplos:

Ejemplo 1:

CREATE PROCEDURE CANTIDAD_VARIABLE_1(
   tnCantidadFilas INTEGER)
RETURNS(
   ftcNombreCliente TYPE OF COLUMN CLIENTES.CLI_NOMBRE)
AS
BEGIN

   FOR SELECT
      FIRST (:tnCantidadFilas)
      CLI_NOMBRE
   FROM
      CLIENTES
   INTO
      :ftcNombreCliente
   DO BEGIN
      SUSPEND;
   END

END;

Ejecutamos a nuestro stored procedure así:

SELECT
   *
FROM
   CANTIDAD_VARIABLE_1(3)

Ejemplo 2:

CREATE PROCEDURE CANTIDAD_VARIABLE_2(
   tnCantidadFilas INTEGER)
RETURNS(
   ftcNombreCliente TYPE OF COLUMN CLIENTES.CLI_NOMBRE)
AS
BEGIN

   FOR SELECT
      CLI_NOMBRE
   FROM
      CLIENTES
   ROWS
      (:tnCantidadFilas)
   INTO
      :ftcNombreCliente
   DO BEGIN
      SUSPEND;
   END

END;

Ahora, ejecutamos a este stored procedure así:

SELECT
   *
FROM
   CANTIDAD_VARIABLE_2(3)

Resultados:

En ambos casos obtendremos algo similar a esto: CANTIDADVARIABLE1

Captura 1. Si haces clic en la imagen la verás más grande

Conclusión:

Si en un stored procedure queremos usar las cláusulas FIRST o ROWS y el valor que vendrá a continuación es una variable entonces deberemos rodearla con paréntesis, como se vio en los ejemplos anteriores, o sea que la sintaxis es:

FIRST (:MiVariable)

ROWS (:MiVariable)

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Descargar gratis archivos de configuración optimizados

Deja un comentario

La empresa IBSurgeon desarrolla programas utilitarios para ser usados con Firebird (algunos son gratuitos) y entre sus colaboraciones gratis se encuentran archivos de configuración optimizados.

Estos archivos de configuración optimizados están diseñados para ser usados con cualquiera de las principales arquitecturas (SuperServer, Classic, SuperClassic).

Ahora ya está también disponible el archivo de configuración optimizado para Firebird 3.0, beta 1. Pero también hay archivos de configuración optimizados para versiones anteriores del Firebird.

Los pasos a seguir son los siguientes:

1. Asegúrate de que nadie esté conectado a una Base de Datos, o sea que nadie esté usando al Servidor del Firebird

2. Detén el Servidor del Firebird

3. Entra en la carpeta donde has instalado al Firebird, por ejemplo en:

C:\ARCHIVOS DE PROGRAMA\FIREBIRD\FIREBIRD_2_5\

4. Haz una copia de seguridad del archivo FIREBIRD.CONF, ¡¡¡no te olvides de este paso!!!.

5. Cámbiale el nombre al archivo FIREBIRD.CONF, por ejemplo como FIREBIRD.CONF.ANTERIOR

6. Descarga el archivo FIREBIRD.CONF optimizado que corresponde a tu computadora, desde:

http://ib-aid.com/en/optimized-firebird-configuration/

7. Copia el archivo descargado a la carpeta donde se encuentra tu Firebird, por ejemplo, en:

C:\ARCHIVOS DE PROGRAMA\FIREBIRD\FIREBIRD_2_5\

8. Cámbiale el nombre a tu archivo descargado, para que pase a llamarse FIREBIRD.CONF

9. Reinicia el Servidor del Firebird

10. Eso es todo, a partir de este momento el Firebird estará usando al nuevo archivo de configuración

IMPORTANTE:

Es muy probable que luego de empezar a usar el archivo de configuración del Firebird optimizado notes una mejoría en el rendimiento de tus bases de datos, pero si ese no es el caso siempre puedes seguir usando tu archivo de configuración anterior, para ello: a) detén el Servidor del Firebird, b) borra el archivo FIREBIRD.CONF, c) renombra a tu archivo de configuración anterior como FIREBIRD.CONF, d) reinicia el Servidor del Firebird

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

Firebird 3.0 beta 1 disponible para ser descargado

8 comentarios

Firebird 3.0 será la nueva y muy mejorada versión de Firebird. Su beta 1 puede ser descargado desde:

http://www.firebirdsql.org/en/firebird-3-0-0-beta1/

y un archivo .PDF con información sobre esa versión puede ser descargado desde:

http://web.firebirdsql.org/download/prerelease/rlsnotes/Firebird-3.0.0_Beta1-ReleaseNotes.pdf

IMPORTANTE:

Las versiones beta no deben ser usadas en producción ni en entornos donde se requiere de un sistema estable. Son liberadas para que quienes quieran colaborar con Firebird puedan hacerles pruebas, buscar errores, e informar de esos errores que encuentren a los desarrolladores, para que éstos puedan corregirlos.

No hay garantías de que todo funcione bien, así que no las instales en las computadoras de tus clientes.

Pero sí puedes instalarlas en tu propia computadora, hacerles pruebas, e informar de los problemas que encuentres, ya que eso acelerará la liberación de la versión Firebird 3.0 definitiva.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Older Entries