Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Interpretación del informe de Bruce Momjian de 2020 "Desbloqueando el Administrador de Bloques de Postgres".

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

(Nota: Todas las consultas SQL de las diapositivas las puede obtener en este enlace: http://momjian.us/main/writings/pgsql/locking.sql)

¡Hola! Es maravilloso estar aquí nuevamente en Rusia. Pido disculpas por no haber podido venir el año pasado, pero este año Ivan y yo tenemos grandes planes. Espero poder estar aquí mucho más seguido. Me encanta venir a Rusia. Visitaré Tyumen y Tver. Estoy muy emocionado de poder conocer estas ciudades.

Me llamo Bruce Momjian. Trabajo en EnterpriseDB y he estado trabajando con Postgres por más de 23 años. Vivo en Filadelfia, EE. UU. Viajo aproximadamente 90 días al año y asisto a alrededor de 40 conferencias. Mi sitio web, que contiene las diapositivas que les mostraré ahora. Así que después de la conferencia podrás descargarlas desde mi sitio personal. También hay alrededor de 30 presentaciones, así como videos y una gran cantidad de publicaciones en el blog, más de 500. Es un recurso bastante sustancial. Y si te interesa este material, te invito a que lo aproveches.

Anteriormente fue profesor, profesor antes de comenzar a trabajar con Postgres. Y estoy muy contento de poder contarles ahora lo que estoy a punto de presentar. Es una de mis presentaciones más interesantes. Y esta presentación contiene 110 diapositivas. Comenzaremos con cosas simples, y hacia el final, la charla se volverá cada vez más compleja y se hará bastante difícil.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Es una charla bastante incómoda. La bloqueo no es un tema muy popular. Queremos que desaparezca. Es como ir al dentista.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

  1. La bloqueo es un problema para muchas personas que trabajan con bases de datos y que tienen varios procesos ejecutándose simultáneamente. Necesitan la bloqueo. Es decir, hoy les daré los conocimientos básicos sobre bloqueo.
  2. Identificadores de transacciones. Esta es una parte bastante aburrida de la presentación, pero es necesario entenderla.
  3. A continuación, hablaremos sobre los tipos de bloqueo. Esta es una parte bastante mecánica.
  4. Y luego daremos algunos ejemplos de bloqueos. Y esto será bastante complicado de entender.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Hablemos sobre bloqueos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

La terminología es bastante compleja. ¿Cuántos de ustedes saben de dónde proviene este fragmento? Dos personas. Es de un juego llamado 'Aventura Colosal en la Cueva'. Era un videojuego de texto en los años 80, creo. Tenías que entrar en una cueva, en un laberinto y el texto cambiaba, pero el contenido era más o menos el mismo cada vez. Así es como recuerdo ese juego.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí vemos los nombres de los bloqueos que provienen de Oracle. Los estamos utilizando.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí vemos términos que me confunden. Por ejemplo, SHARE UPDATE EXCLUSIVE. Luego, SHARE RAW EXCLUSIVE. Honestamente, estos nombres no son muy claros. Intentaremos analizarlos en mayor detalle. Algunos contienen la palabra 'share', que significa - separarse. Algunos contienen la palabra 'exclusive' - exclusivo. En algunos están ambas palabras. Me gustaría comenzar con cómo funcionan estos bloqueos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y también es muy importante la palabra 'acceso' - access. Y la palabra 'fila' - row. Es decir, distribución de acceso, distribución de filas.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Otro problema que hay que entender en Postgres, lamentablemente no podré explicar en mi presentación, es MVCC. Tengo una presentación separada sobre este tema en mi sitio web. Y si piensas que esta presentación es complicada, MVCC es probablemente la más complicada de todas. Si te interesa, puedes verla en el sitio. Puedes ver el video.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Otro punto que necesitamos entender son los identificadores de transacción. Muchas transacciones no pueden funcionar sin identificadores únicos. Y aquí tenemos una explicación de qué es una transacción. En Postgres hay dos sistemas de numeración de transacciones. Sé que no es una solución muy elegante.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

También ten en cuenta que las diapositivas serán bastante difíciles de seguir, por lo que debes prestar atención a lo que está resaltado en rojo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

http://momjian.us/main/writings/pgsql/locking.sql

Veamos. El número de transacción está marcado en rojo. Aquí se muestra la función SELECT pg_back. Devuelve mi transacción y el ID de esta transacción.

Otro punto, si te gusta esta presentación y deseas ejecutarla en tu base de datos, puedes seguir este enlace resaltado en rosa y descargar el SQL para esta presentación. Puedes ejecutarlo en tu PSQL y la presentación aparecerá en tu pantalla de inmediato. No contendrá colores, pero al menos podremos verla.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

En este caso, vemos el ID de la transacción. Este es el número que le hemos asignado. Y hay otro tipo de ID de transacción en Postgres, que se llama ID de transacción virtual.

Y debemos entender esto. Es muy importante, de lo contrario no podremos entender el bloqueo en Postgres.

El ID de transacción virtual es un ID de transacción que no contiene valores persistentes. Por ejemplo, si ejecuto un comando SELECT, es probable que no cambie la base de datos, no estaré bloqueando nada. Por lo tanto, cuando ejecutamos un simple SELECT, no le damos a esta transacción un ID persistente. Solo le damos un ID virtual.

Y esto mejora el rendimiento de Postgres, optimiza las capacidades de limpieza, por eso el ID de transacción virtual se compone de dos números. El primer número antes de la barra diagonal es el ID del backend. A la derecha simplemente vemos un contador.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Por lo tanto, si ejecuto una consulta, dice que el ID del backend es 2.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si ejecuto una serie de estas transacciones, vemos que el contador aumenta cada vez que ejecuto una consulta. Por ejemplo, cuando ejecuto las consultas 2/10, 2/11, 2/12, etc.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Ten en cuenta que aquí hay dos columnas. A la izquierda vemos el ID de transacción virtual: 2/12. Y a la derecha tenemos el ID de transacción persistente. Este campo está vacío. Y esta transacción no modifica la base de datos. Por lo tanto, no le asigno un ID de transacción persistente.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Tan pronto como ejecuto el comando analizar (ANALYZE), la misma consulta me da un ID de transacción persistente. Observa cómo ha cambiado. Antes no tenía este ID, ahora lo tengo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así que aquí hay otra consulta, otra transacción. El número de transacción virtual es 2/13. Y si pido el ID de transacción persistente, lo obtendré cuando ejecute la consulta.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así que, una vez más. Tenemos un ID de transacción virtual y un ID de transacción persistente. Solo entiende este punto para comprender el comportamiento de Postgres.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Pasamos a la tercera sección. Aquí simplemente revisaremos los diferentes tipos de bloqueos en Postgres. No es muy interesante. La última sección será mucho más interesante. Pero debemos considerar lo básico, de lo contrario no entenderemos lo que seguirá.

Revisaremos esta sección, miraremos cada tipo de bloqueos. Y les mostraré ejemplos de cómo se establecen, cómo funcionan, les mostraré algunas consultas que se pueden utilizar para ver cómo funciona el bloqueo en Postgres.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Para crear una consulta y ver qué sucede en Postgres, necesitamos emitir una consulta en la vista del sistema. En este caso, pg_lock está resaltado en rojo. Pg_lock es una tabla del sistema que nos indica qué bloqueos se están utilizando actualmente en Postgres.

Sin embargo, me resulta muy difícil mostrarles pg_lock en sí, porque es bastante complicado. Por eso he creado una vista que muestra pg_locks. Y también realiza para mí un trabajo que me permite entender mejor. Es decir, excluye mis bloqueos, mi propia sesión, etc. Es simplemente SQL estándar y me permite mostrarles mejor lo que está sucediendo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Otro problema es que esta vista es muy amplia, por lo que tengo que crear una segunda: lockview2.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian Y me muestra más columnas de la tabla. Y otra que me muestra las restantes columnas. Es bastante complicado, así que intenté presentarlo de la manera más sencilla posible.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así que hemos creado una tabla llamada Lockdemo. Y allí creamos una fila. Esta es nuestra tabla de muestra. Y vamos a crear secciones para simplemente mostrarles ejemplos de bloqueos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así que, una fila, una columna. El primer tipo de bloqueo se llama ACCESS SHARE. Este es el bloqueo menos restrictivo. Esto significa que prácticamente no entra en conflicto con otros bloqueos.

Y si queremos definir explícitamente un bloqueo, ejecutamos el comando «lock table». Esto bloqueará la tabla de manera clara, es decir, en modo ACCESS SHARE ejecutamos lock table. Y si inicio PSQL en segundo plano, estoy creando así una segunda sesión desde mi primera sesión. Es decir, ¿qué haré aquí? Cambio a la otra sesión y le digo «muéstrame el lockview para esta consulta». Y aquí tengo AccessShareLock en esta tabla. Eso es exactamente lo que estaba solicitando. Y dice que se ha asignado un bloqueo. Muy fácil.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Luego, si miramos la segunda columna, no hay nada allí. Están vacías.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si ejecuto el comando «SELECT», es una forma implícita (explícita) de solicitar AccessShareLock. Por lo tanto, libero mi tabla y ejecuto la consulta, y esta devuelve varias filas. Y en una de las filas vemos AccessShareLock. De este modo, SELECT provoca AccessShareLock en la tabla. Y prácticamente no entra en conflicto con nada, porque es un bloqueo de bajo nivel.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Qué pasa si ejecuto SELECT y tengo tres tablas diferentes? Antes solo ejecutaba una tabla, ahora estoy ejecutando tres: pg_class, pg_namespace y pg_attribute.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y ahora, cuando miro la consulta, veo 9 AccessShareLocks en tres tablas. ¿Por qué? Las tres tablas están destacadas en azul: pg_attribute, pg_class, pg_namespace. Pero también puedes ver que todos los índices que están definidos a través de estas tablas también tienen AccessShareLock.

Y este es un bloqueo que prácticamente no entra en conflicto con otros. Y todo lo que hace es simplemente no permitirnos liberar la tabla mientras la estamos seleccionando. Tiene sentido. Es decir, si seleccionamos una tabla y desaparece en ese momento, eso sería incorrecto, por lo tanto. AccessShare es un bloqueo de bajo nivel que nos dice "no eliminen esta tabla mientras estoy trabajando".. En esencia, eso es todo lo que hace.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

ROW SHARE es un tipo de bloqueo que es un poco diferente.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Tomemos un ejemplo. SELECT ROW SHARE es la forma de bloquear cada fila individualmente.. De este modo, nadie puede eliminarlas o modificarlas mientras las estamos mirando.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce MomjianEntonces, ¿qué hace el SHARE LOCK? Vemos que el ID de la transacción 681 corresponde a un SELECT. Y esto es interesante. ¿Qué ha sucedido aquí? La primera vez que vemos el número en el campo 'Lock'. Tomamos el ID de la transacción, y dice que lo bloquea en modo exclusivo. Todo lo que hace es indicar que tengo una fila que está técnicamente bloqueada en algún lugar de la tabla. Pero no dice dónde exactamente. Más adelante lo veremos con más detalle.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí decimos que el bloqueo es utilizado por nosotros.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Entonces, el bloqueo exclusivo indica explícitamente que es exclusivo. Y también, si eliminas una fila en esta tabla, eso es lo que sucederá, como puedes ver.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

SHARE EXCLUSIVE es un bloqueo más prolongado.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Este es el comando ANALYZE que se va a utilizar.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

SHARE LOCK te permite bloquear explícitamente en modo compartido.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

También puedes crear un índice único. Y allí puedes ver el SHARE LOCK, que es parte de ellos. Y bloquea la tabla y aplica el SHARE LOCK sobre ella.

Por defecto, el SHARE LOCK en la tabla significa que otras personas pueden leer la tabla, pero nadie puede modificarla. Y eso es lo que sucede cuando creas un índice único.

Si creo un índice único concurrentemente, tendré otro tipo de bloqueo, porque, como recordarás, el uso de índices concurrentes reduce la necesidad de bloqueo. Y si utilizo un bloqueo normal con un índice normal, estaré previniendo así la escritura en la tabla del índice durante su creación. Si utilizo un índice concurrente, tendré que usar otro tipo de bloqueo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

SHARE ROW EXCLUSIVE, nuevamente puede ser asignado explícitamente.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

O podemos crear una regla, es decir, tomar un caso específico en el que se va a utilizar.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

El bloqueo EXCLUSIVE significa que nadie más podrá modificar la tabla.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí vemos diferentes tipos de bloqueos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

ACCESS EXCLUSIVE, por ejemplo, es un comando de bloqueo. Por ejemplo, si haces CLUSTER table, eso significará que nadie podrá escribir en ella. Y bloquea no solo la tabla misma, sino también los índices.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esta es la segunda página del bloqueo ACCESS EXCLUSIVE, donde vemos específicamente lo que está bloqueando en la tabla. Está bloqueando filas individuales de la tabla, lo cual es bastante interesante.

Esta es toda la información básica que quería proporcionar. Hablamos sobre bloqueos, sobre ID de transacciones, mencionamos ID virtuales de transacciones y sobre ID de transacciones permanentes.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Ahora revisaremos ejemplos de bloqueos. Esta es la parte más interesante. Veremos casos muy interesantes. Y mi tarea en esta presentación es darte una mejor idea de lo que realmente hace Postgres cuando intenta bloquear ciertos elementos. Me parece que lo hace muy bien bloqueando partes específicas.

Examinemos ciertos ejemplos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Comenzaremos con tablas y una fila en la tabla. Cuando inserto algo, se muestra un ExclusiveLock, el ID de la transacción y un ExclusiveLock en la tabla.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y qué pasará si inserto dos filas más? Ahora nuestra tabla tiene tres filas. Inserté una fila y obtuve esto como salida. Y si inserto dos filas más, ¿qué hay de extraño aquí? Hay algo extraño, porque añadí tres filas a esta tabla, pero todavía tengo dos filas en la tabla de bloqueos. Y esto, en esencia, es el comportamiento fundamental de Postgres.

Muchos piensan que si en la base de datos bloqueas 100 filas, necesitarás crear 100 entradas de bloqueos. Si bloqueo de una vez 1,000 filas, entonces necesitaré 1,000 de tales solicitudes. Y si necesito bloquear un millón o mil millones. Pero hacer esto no funcionaría muy bien. Si has utilizado un sistema que crea entradas de bloqueos para cada fila individual, verás que esto es complicado. Porque necesitas definir de inmediato la tabla de bloqueos, que podría desbordarse, pero Postgres no lo hace.

Y en esta diapositiva es muy importante notar que aquí se demuestra claramente que hay otro sistema que funciona dentro del MVCC, que bloquea filas individuales. Por lo tanto, cuando bloqueas miles de millones de filas, Postgres no crea mil millones de comandos de bloqueo individuales. Y esto mejora mucho el rendimiento.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y qué pasa con la actualización? Ahora estoy actualizando una fila, y puedes notar que realizó dos operaciones diferentes al mismo tiempo. Bloqueó la tabla, pero también bloqueó el índice. Y necesitaba bloquear el índice porque hay restricciones únicas en esta tabla. Y queremos asegurarnos de que nadie lo cambie, así que lo bloqueamos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y qué sucederá si quiero actualizar dos filas? Y vemos que se comporta de la misma manera. Realizamos el doble de actualizaciones, pero exactamente la misma cantidad de filas bloqueadas.

Si te interesa cómo lo hace Postgres, necesitas escuchar mis presentaciones sobre MVCC para comprender cómo Postgres marca internamente las filas que cambia. Y Postgres tiene una forma de hacerlo, pero no lo realiza a nivel de bloqueo de tablas, lo hace a un nivel más bajo y más eficiente.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y si quiero eliminar algo? Si elimino, por ejemplo, una fila y aún tengo mis dos bloqueos en espera, incluso si quiero eliminarlas todas, todavía están ahí.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y, por ejemplo, si quiero insertar 1,000 filas, y luego eliminar o agregar 1,000 filas, esas filas individuales que agrego o cambio no se registran aquí. Se registran a un nivel más bajo dentro de la propia fila. Y durante la presentación sobre MVCC hablé de esto en detalle. Pero es muy importante, al analizar bloqueos, asegurarse de que tienes un bloqueo a nivel de tabla y que aquí no ves cómo se registran las filas individuales.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y qué pasa con el bloqueo explícito?

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Si presiono 'actualizar', tengo dos filas bloqueadas. Y si las selecciono todas y presiono 'actualizar todo', aún tendré dos registros bloqueados.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

No creamos registros separados para cada fila individual. Porque eso disminuiría el rendimiento, podría haber demasiados. Y podríamos encontrarnos en una situación incómoda.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y lo mismo sucede, si hacemos un bloqueo compartido, podemos hacerlo 30 veces.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Restauramos nuestra tabla, eliminamos todo, luego insertamos una fila de nuevo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Otro comportamiento que se observa en Postgres es el bien conocido y deseado comportamiento: puedes realizar actualizaciones o selecciones. Y puedes hacerlo simultáneamente. La selección no bloquea la actualización y viceversa. Estamos diciendo que el que lee no bloquea a quien escribe, y quien escribe no bloquea al lector.

Te mostraré un ejemplo de esto. Haré una selección ahora. Luego haremos un INSERT. Y después podrás ver – 694. Podrás ver el ID de la transacción que realizó esta inserción. Y así es como funciona.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si ahora miro mi ID de backend, se ha convertido en – 695.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y puedo ver que 695 aparece en mi tabla.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si realizo una actualización aquí de esta manera, obtengo otro caso. En este caso, 695 tiene un bloqueo exclusivo, y la actualización tiene un comportamiento similar, pero entre ellos no surge un conflicto, lo cual es bastante inusual.

Y puedes notar que en la parte superior – es un ShareLock, y abajo – es un ExclusiveLock. Y ambas transacciones se lograron.

Y debes escuchar mi presentación sobre MVCC para entender cómo sucede esto. Pero esta es una ilustración de que puedes hacer esto simultáneamente, es decir, realizar SELECT y UPDATE al mismo tiempo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Vamos a reiniciar y hacer una operación más.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Si intentas ejecutar dos actualizaciones simultáneamente en la misma fila, se bloqueará. Y recuerda, dije que el que lee no bloquea al escritor, y el escritor al lector, pero un escritor bloquea a otro escritor. Es decir, no podemos hacer que dos personas actualicen la misma fila al mismo tiempo. Hay que esperar a que uno de ellos termine.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y para ilustrar esto, miraré la tabla Lockdemo. Y veremos una fila. En la transacción 698.

Lo actualizamos a 2. 699 – es la primera actualización. Y fue exitosa o está en una transacción pendiente, esperando que confirmemos o cancelemos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Pero miren otra cosa: 2/51 es nuestra primera transacción, nuestra primera sesión. 3/112 es la segunda solicitud que apareció encima y que cambió este valor a 3. Y si lo notan, el superior se bloqueó a sí mismo, el que es 699. Pero 3/112 no proporcionó bloqueo. En la columna Lock_mode dice que está esperando. Está esperando a 699. Y si miran dónde está 699, está arriba. ¿Y qué hizo la primera sesión? Creó un bloqueo exclusivo en su propio ID transaccional. Así es como Postgres lo hace. Bloquea su propio ID transaccional. Y si quieren esperar a que alguien confirme o cancele, deben esperar hasta que haya una transacción pendiente. Por eso podemos ver esa línea extraña.

Veamos una vez más. A la izquierda vemos nuestro ID de procesamiento. En la segunda columna vemos nuestro ID virtual de transacción, y en la tercera vemos el lock_type. ¿Qué significa esto? Básicamente, dice que está bloqueando el ID transaccional. Pero noten que en todas las filas abajo dice relation. Y por eso tienen dos tipos de bloqueo en la tabla. Hay un bloqueo de relación. Y también hay un bloqueo de transactionid, donde bloquean por su cuenta, que es precisamente lo que sucede en la primera fila o en la parte más baja, donde transactionid, donde estamos esperando a que 699 termine su operación.

Estoy observando lo que está ocurriendo aquí. Y aquí sucede dos cosas simultáneamente. Están viendo el bloqueo por ID transaccional en la primera fila, que se bloquea a sí mismo. Y se bloquea a sí mismo para hacer que la gente espere.

Si miran la sexta línea, verán que el mismo registro que el primero. Y por eso la transacción 699 se está bloqueando. 700 también se bloquea a sí mismo. Y luego en la fila inferior verán que estamos esperando a que 699 termine su operación.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y en lock_type, tuple, ven los números.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Pueden ver que es 0/10. Y ese es el número de página, y también el offset de esta fila específica.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y ven que se convierte en 0/11 cuando actualizamos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Pero en realidad, es 0/10, porque hay una espera en esta operación. Tenemos la oportunidad de observar que esta es la fila que estoy esperando para confirmar.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Una vez que lo confirmamos y presionamos commit, y cuando se terminó la actualización, esto es lo que obtenemos de nuevo. La transacción 700 es el único bloqueo, no está esperando a nadie más porque se ha comprometido. Simplemente está esperando a que la transacción finalice. Una vez que 699 termina, no estamos esperando más nada. Y ahora la transacción 700 dice que todo está bien, que tiene todos los bloqueos necesarios en todas las tablas permitidas.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y para complicar aún más las cosas, estamos creando otra vista que en esta ocasión nos proporcionará una jerarquía. No espero que entiendas esta consulta. Pero nos dará una visión más clara de lo que está sucediendo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esta es una vista recursiva, que también tiene otra sección. Y luego vuelve todo junto. Vamos a utilizar esto.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Qué pasaría si hacemos tres actualizaciones simultáneas y decimos que la fila es ahora tres? Y cambiamos 3 a 4.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y aquí vemos 4. Y el ID de transacción 702.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y luego cambiaré 4 a 5. Y 5 a 6, y 6 a 7. Y estoy poniendo en cola a un grupo de personas que estarán esperando a que finalice esta única transacción.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y todo se vuelve claro. ¿Cuál es la primera fila? Es 702. Este es el ID de transacción que originalmente estableció este valor. ¿Y qué tengo escrito en la columna Granted? Tengo anotaciones f. Estas son aquellas actualizaciones (5, 6, 7) que no pueden ser aprobadas porque estamos esperando que el ID de transacción 702 termine. Tenemos un bloqueo del ID de transacción. Y así obtenemos 5 bloqueos de transacción ID.

Y si miras el 704, el 705, no hay nada escrito allí, porque aún no saben lo que está pasando. Simplemente están escribiendo que no tienen idea de lo que sucede. Y simplemente se irán a dormir, porque están esperando a que alguien termine y los despierte cuando haya una oportunidad de cambiar la fila.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así es como se ve. Es evidente que todos están esperando la fila 12.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esto es lo que vimos aquí. Aquí está 0/12.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Así que una vez que la primera transacción es aprobada, puedes aquí ver cómo funciona la jerarquía. Y ahora todo queda claro. Todos quedan limpios. Y en realidad todos todavía están en espera.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esto es lo que sucede. 702 se compromete. Y ahora 703 recibe este bloqueo de fila, mientras que 704 comienza a esperar a que 703 se comprometa. Y 705 también está esperando eso. Y cuando todo esto se completa, se limpian a sí mismos. Y me gustaría señalar que todos se alinean. Es muy parecido a una situación de embotellamiento, donde todos esperan el primer automóvil. El primer automóvil se detuvo y todos se alinean en una larga fila. Luego avanza, y el siguiente automóvil puede avanzar y obtener su bloqueo, y así sucesivamente.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si esto te parece poco complicado, ahora hablaremos sobre los deadlocks. No sé quién de ustedes se ha encontrado con ellos. Es un problema bastante común en los sistemas de bases de datos. Pero los deadlocks son el caso en que una sesión espera a que otra sesión realice algo. Y en ese momento, la otra sesión espera a que la primera sesión complete algo.

Y, por ejemplo, si Iván dice: 'Dame algo', y yo digo: 'No, solo te lo daré si me das algo a cambio'. Y él responde: 'No, no te daré eso a menos que me des algo primero'. Y nos encontramos en una situación de deadlock. Estoy seguro de que Iván no haría eso, pero entiendes la idea: tenemos a dos personas que quieren obtener algo, y no están dispuestas a darlo hasta que la otra persona les entregue lo que desean. Y aquí no hay solución.

Y, esencialmente, tu base de datos necesita identificar esto. Y luego es necesario eliminar o cerrar una de las sesiones, porque de lo contrario permanecerán allí para siempre. Y esto lo vemos en bases de datos, lo vemos en sistemas operativos. Y en todos los lugares donde hay procesos paralelos, esto puede suceder.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Ahora vamos a configurar dos deadlocks. Vamos a establecer 50 y 80. En la primera fila, realizaré una actualización de 50 a 50. Obtendré el número de transacción 710.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Luego cambiaré 80 a 81, y 50 a 51.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y así es como se verá. Por lo tanto, 710 tiene un bloqueo de fila, mientras que 711 espera confirmación. Lo vimos cuando actualizamos. 710 es el propietario de nuestra fila. Y 711 está esperando a que 710 complete la transacción.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y allí también está escrito en qué fila específica estamos experimentando deadlocks. Y aquí es donde comienza a volverse extraño.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Ahora actualizamos de 80 a 80.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y aquí es donde comienzan los bloqueos. El 710 está esperando una respuesta del 711, y el 711 está esperando al 710. Y esto no terminará bien. Y no hay salida de esto. Y estarán esperando una respuesta el uno del otro.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y esto simplemente comenzará a causar retrasos. Y eso es algo que no queremos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y en Postgres hay formas de notar cuando esto sucede. Y cuando ocurre, obtienes este tipo de error. Y queda claro que este proceso está esperando un SHARE LOCK de otro proceso, es decir, que está bloqueado por el proceso 711. Y ese proceso estaba esperando que se otorgara SHARE LOCK en un determinado ID transaccional y fue bloqueado por otro proceso. Por lo tanto, aquí hay una situación de bloqueo muerto.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

¿Y hay bloqueos muertos de tres vías? ¿Es posible? Sí.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Estamos ingresando estos números en la tabla. Cambiamos 40 por 40, hacemos un bloqueo.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Cambiamos 60 por 61, 80 por 81.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y luego cambiamos 80, y luego, ¡boom!

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y el 714 ahora está esperando al 715. El 716 está esperando al 715. Y ya no se puede hacer nada con esto.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí ya no son dos personas, son tres personas. Yo quiero algo de ti, este quiere algo del tercer persona, y el tercer persona quiere algo de mí. Y estamos en una espera de tres vías, porque todos estamos esperando a que otra persona complete lo que tiene que hacer.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y Postgres sabe en qué fila sucede esto. Y por eso te dará el siguiente mensaje, que muestra que tienes un problema donde tres entradas se bloquean entre sí. Y no hay restricciones. Puede ser en una situación donde 20 registros se bloquean entre sí.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

El siguiente problema es el serializable.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Si hay un bloqueo serializable especial.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y volvamos al 719. Tiene una salida completamente normal.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y puedes hacer clic para realizar una transacción de tipo serializable.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y ahora entiendes que tienes otro tipo de bloqueo SA, que significa serializable.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y por eso tenemos un nuevo tipo de bloqueo llamado SARieadLock, que es un bloqueo serial y permite introducir números de serie.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y también puedes insertar índices únicos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

En esta tabla tenemos índices únicos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Por lo tanto, si ingreso el número 2 aquí, tengo un 2. Pero en la parte superior inserto otro 2. Y puedes ver que el 721 tiene un bloqueo exclusivo. Pero ahora el 722 espera a que el 721 complete su operación, porque no puede insertar el 2 hasta que sepa qué sucederá con el 721.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si hacemos una subtransacción.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Aquí está nuestro 723.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y si conservamos el punto y luego lo actualizamos, obtenemos un nuevo ID transaccional. Este es otro comportamiento que necesitas conocer. Si lo devolvemos, el ID transaccional se pierde. 724 se pierde. Pero ahora tenemos 725.

¿Y qué estoy tratando de hacer aquí? Estoy tratando de mostrarte ejemplos de bloqueos inusuales que puedes encontrar: ya sean bloqueos serializables o SAVEPOINT, son diferentes tipos de bloqueos que aparecerán en la tabla de bloqueos.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esto es la creación de bloqueos explícitos, donde está pg_advisory_lock.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y ves que el tipo de bloqueo aquí se enumera como advisory. Y se indica en rojo ‘advisory’. Y puedes bloquear así simultáneamente con pg_advisory_unlock.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y para concluir, me gustaría mostrarte otra cosa sorprendente. Crearé un nuevo tipo. Pero combinaré la tabla pg_locks con la tabla pg_stat_activity. ¿Y por qué quiero hacer esto? Porque me permitirá ver todas las sesiones actuales y ver qué bloqueos están esperando. Y es bastante interesante cuando combinamos la tabla de bloqueos con la tabla de consultas.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y aquí creamos pg_stat_view.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y actualizamos la fila en uno. Y aquí vemos 724. Luego actualizamos nuestra fila a tres. ¿Y qué ves aquí ahora? Son consultas, es decir, ves toda la lista de consultas que se enumeran en la columna izquierda. Y luego, en el lado derecho, puedes ver los bloqueos y lo que crean. Y esto puede ser más claro para ti, para que no tengas que volver cada vez a cada sesión y ver si necesitas unirte a ella o no. Eso lo hacen por nosotros.

Otra función que es muy útil es pg_blocking_pids. Puede que nunca hayas oído hablar de ella. ¿Qué hace? Nos permite decir que para esta sesión 11740, qué ID de procesos específicos está esperando. Y puedes ver que 11740 está esperando 724. Y 724 está en la parte superior. Y 11306 es tu ID de proceso. Básicamente, esta función repasa tu tabla de bloqueos. Y sé que es un poco complicado, pero logras entenderlo. En esencia, esta función pasa por esta tabla de bloqueos e intenta encontrar dónde está este ID de proceso, considerando los bloqueos que está esperando. Y también intenta calcular qué ID de proceso tiene aquel proceso que está esperando bloqueos. Por lo tanto, puedes ejecutar esta función. pg_blocking_pids.

Y esto puede ser muy útil. Lo hemos añadido solo desde la versión 9.6, por lo que esta función tiene solo 5 años, pero es muy, muy útil. Lo mismo ocurre con la segunda consulta. Muestra exactamente lo que necesitamos ver.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Esto es de lo que quería hablar contigo. Y como esperaba, utilizamos todo nuestro tiempo porque hubo una gran cantidad de diapositivas. Las diapositivas están disponibles para descarga. Me gustaría agradecerte por estar aquí. Estoy seguro de que disfrutarás el resto de la conferencia, ¡muchas gracias!

Preguntas:

Por ejemplo, si intento actualizar filas y la segunda sesión intenta eliminar toda la tabla. Hasta donde entiendo, debería haber algo como un intent lock. ¿Hay algo así en Postgres?

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Volvamos al principio. Tal vez recuerdes que cuando haces cualquier cosa, por ejemplo, cuando haces un SELECT, emitimos un AccessShareLock. Y esto evita la eliminación de la tabla. Por lo tanto, si deseas actualizar una fila en la tabla o eliminar una fila, alguien no puede eliminar toda la tabla al mismo tiempo, porque mantienes este AccessShareLock sobre toda la tabla y sobre la fila. Y tan pronto como terminas, pueden eliminarla. Pero mientras estés cambiando algo allí, no podrán hacerlo.

Hagámoslo una vez más. Pasemos al ejemplo de eliminación. Y ves que hay un lock exclusivo sobre toda la tabla en la fila.

¿Se verá como un lock exclusivo, correcto?

Sí, eso parece. Entiendo de qué hablas. Dices que si ejecuto SELECT, tendré ShareExclusive, y luego lo transfiero a un estado Row Exclusive, ¿se convierte esto en un problema? Pero sorprendentemente, esto no crea un problema. Se parece a un aumento en el nivel de bloqueo, pero, en esencia, tengo un lock que previene la eliminación. Y ahora, cuando hago este lock más potente, aún así previene la eliminación. Por lo tanto, no es que esté aumentando. Es decir, ya prevenía esto cuando estaba a un nivel más bajo, así que cuando aumento su nivel, todavía evita la eliminación de la tabla.

Entiendo de qué hablas. Aquí no hay un caso de aumento en el nivel de bloqueo, donde intentas liberar un bloqueo para introducir uno más potente. Aquí simplemente se aumenta este mecanismo de prevención, por lo que no provoca ningún conflicto. Pero es una buena pregunta. ¡Muchas gracias por plantearla!

¿Qué necesitamos hacer para evitar una situación de deadlock cuando tenemos muchas sesiones y un gran número de usuarios?

Postgres detecta automáticamente situaciones de deadlock. Y de forma automática eliminará una de las sesiones. La única forma de ayudar a evitar situaciones de deadlock es bloquear a las personas en el mismo orden. Por lo tanto, cuando mires tu aplicación, a menudo la causa de los deadlocks... Imaginemos que quiero bloquear dos cosas diferentes. Una aplicación bloquea la tabla 1, y otra aplicación bloquea la tabla 2, y luego la tabla 1. Y la forma más sencilla de evitar los deadlocks es observar tu aplicación y tratar de asegurarte de que los bloqueos ocurran en el mismo orden en todas las aplicaciones. Y esto, por lo general, elimina el 80% de los problemas, porque diversas personas escriben estas aplicaciones. Y si los bloqueas en el mismo orden, entonces no te enfrentas a situaciones de deadlock.

¡Muchas gracias por tu presentación! Hablaste sobre vacuum full y, si entiendo correctamente, vacuum full deforma el orden de los registros en el almacenamiento separado, por lo que mantiene los registros actuales sin cambios. ¿Y por qué vacuum full requiere un acceso de bloqueo exclusivo y por qué entra en conflicto con las operaciones de escritura?

Es una buena pregunta. La razón es que vacuum full toma la tabla. Y, en esencia, estamos creando una nueva versión de la tabla. La tabla será nueva. Por lo tanto, será completamente una nueva versión de la tabla. Y el problema es que, cuando hacemos esto, no queremos que las personas la lean, porque necesitamos que vean la nueva tabla. Y por eso se conecta con la pregunta anterior. Si pudiéramos leer al mismo tiempo, no podríamos moverla y dirigir a las personas a la nueva tabla. Tendríamos que esperar a que todos terminen de leer esta tabla, por lo que, en esencia, es una situación de bloqueo exclusivo.
Simplemente decimos que bloqueamos desde el principio, porque sabemos que al final necesitaremos un bloqueo exclusivo para mover a todos a la nueva copia. Por lo tanto, potencialmente podemos resolverlo. Y lo hacemos con indexación simultánea. Pero esto es mucho más complicado de hacer. Y está muy relacionado con tu pregunta anterior sobre el bloqueo exclusivo.

¿Es posible agregar un tiempo de espera de bloqueo en Postgres? En Oracle, por ejemplo, puedo escribir "seleccionar para actualizar" y esperar 50 segundos antes de la actualización. Esto funcionaba bien para la aplicación. Pero en Postgres tengo que hacerlo de inmediato y no esperar, o esperar hasta un cierto tiempo.

Sí, puedes establecer un tiempo de espera para tus bloqueos. También puedes emitir un comando no way, que será... si no puedes obtener el bloqueo de inmediato. Así que habrá un tiempo de espera de bloqueo o algo más que te permita hacerlo. Esto no se realiza a nivel sintáctico. Se hace como una variable en el servidor. A veces no es posible usarlo.

¿Puedes abrir la diapositiva 75?

Sí.

Desbloqueando el Gestor de Bloqueos de Postgres. Bruce Momjian

Y mi pregunta es la siguiente. ¿Por qué ambos procesos de actualización están esperando 703?

Y esa es una pregunta excelente. No entiendo, por cierto, por qué Postgres hace esto. Pero cuando se creó 703, esperaba 702. Y cuando aparecen 704 y 705, parece que no saben lo que están esperando, porque aún no hay nada. Y Postgres actúa así: cuando no puedes obtener un bloqueo, dice '¿Cuál es el sentido de procesarte?', porque ya estás esperando a alguien. Así que simplemente lo dejamos en el aire, y no lo actualiza en absoluto. Pero, ¿qué ocurrió aquí? Tan pronto como 702 terminó el proceso y 703 obtuvo su bloqueo, el sistema volvió atrás. Y dijo que ahora tenemos dos personas que están en espera. Y luego actualicemos a ambos juntos. Y señalemos que ambos están esperando.

No sé por qué Postgres hace esto. Pero hay un problema que se llama f.... Me parece que este no es un término en español. Es cuando todos esperan por un solo bloqueo, incluso si hay 20 instancias que están esperando el bloqueo. Y de repente, todos se despiertan al mismo tiempo. Y todos empiezan a intentar reaccionar. Pero el sistema hace que todos esperen 703. Porque todos están esperando, y los pondremos en cola de inmediato. Y si aparece cualquier otra nueva solicitud, que se hizo después de eso, por ejemplo, 707, habrá otra vez vacío.

Y me parece que esto se hace para poder decir que en esta etapa 702 está esperando 703, y todos los que lleguen después de esto no tendrán ningún registro en este campo. Pero tan pronto como el primer en espera se va, todos los que estaban esperando en ese momento antes de la actualización obtienen el mismo marcador. Y por eso, me parece que esto se hizo para que pudiéramos procesar en orden, para que estuvieran correctamente ordenados.

Siempre he visto esto como un fenómeno bastante extraño. Porque aquí, por ejemplo, ni siquiera los enumeramos. Pero, me parece que cada vez que damos un nuevo bloqueo, miramos a todos los que están en proceso de espera. Entonces los alineamos en cola. Y luego, cualquier nuevo que llegue solo entra en la cola cuando la siguiente persona ha terminado de ser procesada. Muy buena pregunta. ¡Muchas gracias por la pregunta!

Me parece mucho más lógico que 705 espere a 704.

Pero el problema aquí es el siguiente. T técnicamente, puedes despertar uno u otro. Y por eso despertaremos uno u otro. Pero, ¿qué sucede en el funcionamiento del sistema? Ves cómo el 703 en la parte superior bloqueó su propio ID de transacción. Así es como funciona Postgres. Y el 703 se bloquea con su propio ID de transacción, así que si alguien quiere esperar, esperará al 703. Y, en esencia, el 703 finaliza. Y solo después de que termine, algún proceso se despierta. Y no sabemos cuál de ellos será. Luego los procesamos gradualmente. Pero no está claro qué proceso se despierta primero, porque puede ser cualquiera de esos procesos. En esencia, teníamos un programador que decía que ahora podíamos despertar cualquiera de esos procesos. Simplemente elegimos uno al azar. Por lo tanto, ambos deben ser marcados, porque podemos despertar cualquiera de ellos.

Y el problema es que tenemos la CP-infinidad. Y por eso es bastante probable que podamos despertar uno más tardío. Y si, por ejemplo, estamos despertando uno más tardío, entonces estaremos esperando al que acaba de bloquearse, por lo que no determinamos quién será despertado primero. Simplemente estamos creando esa situación, y el sistema los despertará en un orden aleatorio.

Hay artículos sobre locks de Yegor Rogov. Mira, también son interesantes y útiles. El tema, por supuesto, es tremendamente complejo. ¡Muchas gracias, Bruce!

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster