Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Bloquear estadísticas

Cloud Spanner proporciona estadísticas de bloqueo que te permiten identificar las columnas de clave y tabla de fila que eran las fuentes principales de conflictos de bloqueo de transacciones en tu base de datos durante un período determinado. Puedes recuperar estas estadísticas desde las tablas del sistema SPANNER_SYS.LOCK_STATS* mediante las instrucciones de SQL.

Disponibilidad

Los datos SPANNER_SYS solo están disponibles a través de interfaces de SQL (por ejemplo, executeQuery y gcloud spanner databases execute-sql); otros métodos de lectura únicos que proporciona Cloud Spanner no son compatibles con SPANNER_SYS.

Bloquear estadísticas por clave de fila

En las siguientes tablas, se realiza un seguimiento de la clave de fila con el mayor tiempo de espera:

  • SPANNER_SYS.LOCK_STATS_TOP_MINUTE: Claves de fila con los tiempos de espera de bloqueo más altos durante intervalos de 1 minuto.

  • SPANNER_SYS.LOCK_STATS_TOP_10MINUTE: claves de fila con los tiempos de espera de bloqueo más altos durante intervalos de 10 minutos

  • SPANNER_SYS.LOCK_STATS_TOP_HOUR: claves de fila con los tiempos de espera de bloqueo más altos durante intervalos de 1 hora

Estas tablas tienen las siguientes propiedades:

  • Cada tabla contiene datos de intervalos de tiempo no superpuestos de la longitud que se especifica en el nombre de la tabla.

  • Los intervalos se basan en tiempos de reloj. Los intervalos de 1 minuto finalizan en el minuto, los intervalos de 10 minutos finalizan cada 10 minutos a partir de la hora y los intervalos de 1 hora finalizan en la hora. Después de cada intervalo, Cloud Spanner recopila datos de todos los servidores y luego los hace disponibles en las tablas SPANNER_SYS.

    Por ejemplo, a las 11:59:30 a.m., los intervalos más recientes disponibles para las consultas de SQL son los siguientes:

    • 1 minuto: de 11:58:00 a 11:58:59 a.m.
    • 10 minutos: de 11:40:00 a 11:49:59 a.m.
    • 1 hora: de 10:00:00 a 10:59:59 a.m.
  • Cloud Spanner agrupa las estadísticas mediante el inicio del rango de claves de fila.

  • Cada fila contiene estadísticas del tiempo de espera de bloqueo total de un rango de claves de fila de inicio determinado por el que Cloud Spanner captura estadísticas durante el intervalo especificado.

  • Si Cloud Spanner no puede almacenar información sobre cada rango de clave de fila para esperas de bloqueo durante el intervalo, el sistema prioriza el rango de claves de fila con el tiempo de espera de bloqueo más alto durante el intervalo especificado.

Esquema de la tabla

Nombre de la columna Tipo Descripción
INTERVAL_END TIMESTAMP Fin del intervalo en el que ocurrieron los conflictos de bloqueo incluidos.
ROW_RANGE_START_KEY BYTES(MAX) La clave de fila donde se produjo el conflicto de bloqueo. Cuando el conflicto implica un rango de filas, este valor representa la clave inicial de ese rango. Un signo más, +, significa un rango. Para obtener más información, consulta ¿Qué es una clave de inicio de rango de filas?
LOCK_WAIT_SECONDS FLOAT64 El tiempo de espera de bloqueo acumulado de los conflictos de bloqueo registrados para todas las columnas del rango de claves de la fila, en segundos
SAMPLE_LOCK_REQUESTS ARRAY<STRUCT<
  lock_mode STRING,
  column STRING>>
Cada entrada en este arreglo corresponde a una solicitud de bloqueo de muestra que contribuyó al conflicto de bloqueo en la clave de fila determinada o al rango de claves de fila. El número máximo de muestras en este arreglo es de 20. Cada muestra contiene los dos campos siguientes:

  • lock_mode: El modo de bloqueo que se solicitó. Para obtener más información, consulta Modos de bloqueo
  • column: la columna que encontró el conflicto de bloqueo El formato de este valor es tablename.columnname.

Modos de bloqueo

Las operaciones de Cloud Spanner adquieren bloqueos cuando las operaciones son parte de una transacción de lectura y escritura. Las transacciones de solo lectura no adquiere bloqueos. Cloud Spanner usa diferentes modos de bloqueo para maximizar la cantidad de transacciones que tienen acceso a una celda de datos en particular en un momento determinado. Los diferentes cerraduras tienen características diferentes. Por ejemplo, algunos bloqueos se pueden compartir entre varias transacciones, mientras que otros no.

Puede ocurrir un conflicto de bloqueo cuando intentas obtener uno de los siguientes modos de bloqueo en una transacción.

  • ReaderShared Bloqueo: Es un bloqueo que permite que otras lecturas accedan a los datos hasta que la transacción esté lista para confirmarse. Este bloqueo compartido se adquiere cuando una transacción de lectura y escritura lee datos.

  • WriterShared Bloqueo: Este bloqueo se adquiere cuando una transacción de lectura y escritura intenta confirmar una escritura.

  • Exclusive Bloqueo: se adquiere un bloqueo exclusivo cuando una transacción de lectura y escritura, que ya adquirió un bloqueo de ReaderShared, intenta escribir datos después de la lectura. Un bloqueo exclusivo es una actualización desde un bloqueo de ReaderShared. Un bloqueo exclusivo es un caso especial de una transacción que contiene la cerradura ReaderShared y el bloqueo de WriterShared al mismo tiempo. Ninguna otra transacción puede adquirir cualquier bloqueo en la misma celda.

  • WriterSharedTimestamp Bloqueo: un tipo especial de bloqueo WriterShared que se adquiere cuando insertas filas nuevas en una tabla que tiene una marca de tiempo de confirmación como parte de la clave primaria. Este tipo de bloqueo impide que los participantes de las transacciones creen la misma fila y, por lo tanto, entrar en conflicto entre sí. Cloud Spanner actualiza la clave de la fila insertada para que coincida con la marca de tiempo de confirmación de la transacción que realizó la inserción.

Para obtener más información sobre los tipos de transacciones y los tipos de bloqueos disponibles, consulta Transacciones.

Conflictos del modo de bloqueo

En la siguiente tabla, se muestran los posibles conflictos entre diferentes modos de bloqueo.

Modos de bloqueo Lectores compartidos Escritor compartido Exclusivo WriterSharedTimestamp
Lectores compartidos No
Escritor compartido No No aplicable
Exclusivo No aplicable
WriterSharedTimestamp No aplicable No aplicable

Los bloqueos WriterSharedTimestamp solo se usan cuando se insertan filas nuevas con una marca de tiempo como parte de su clave primaria. Los bloqueos WriterShared y Exclusive se usan para escribir en celdas existentes o insertar filas nuevas sin marcas de tiempo. Como resultado, WriterSharedTimestamp no puede entrar en conflicto con otros tipos de bloqueos, y esas situaciones se muestran como No aplicable en la tabla anterior.

La única excepción es ReaderShared, que se puede aplicar a filas no existentes y, por lo tanto, podría entrar en conflicto con WriterSharedTimestamp. Por ejemplo, un análisis completo de la tabla bloquea toda la tabla, incluso las filas que no se crearon, por lo que es posible que ReaderShared entre en conflicto con WriterSharedTimestamp.

¿Qué es una clave de inicio del rango de filas?

La columna ROW_RANGE_START_KEY identifica la clave primaria compuesta, o la clave primaria inicial de un rango de filas, que tiene conflictos de bloqueo. El siguiente esquema se usa para ilustrar un ejemplo.

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
) PRIMARY KEY (SingerId);

CREATE TABLE Albums (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE TABLE Songs (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  TrackId      INT64 NOT NULL,
  SongName     STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId, TrackId),
  INTERLEAVE IN PARENT Albums ON DELETE CASCADE;

CREATE TABLE Users (
  UserId     INT64 NOT NULL,
  LastAccess TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
  ...
) PRIMARY KEY (UserId, LastAccess);

Como se muestra en la siguiente tabla de rangos de clave de fila y clave de fila, se muestra un rango con un signo +, "+", la clave. La clave en esos casos representa la clave de inicio de un rango de claves en el que se produjo un conflicto de bloqueo.

ROW_RANGE_START_KEY Explicación
cantantes(2) Tabla de cantantes en la clave SingerId=2
álbumes(2,1) Tabla de álbumes en la clave SingerId=2,AlbumId=1
canciones(2,1,5) Tabla Songs en la clave SingerId=2,AlbumId=1,TrackId=5
canciones(2,1,5+) Rango de la tabla de canciones a partir de SingerId=2,AlbumId=1,TrackId=5
álbumes(2,1+) Rango de clave de la tabla de álbumes a partir de SingerId=2,AlbumId=1
usuarios(3, 11-11-01 12:34:56.426426+00:00) Tabla de usuarios en la clave UserId=3, LastAccess=commit_timestamp

Estadísticas agregadas

SPANNER_SYS también contiene tablas con el fin de almacenar datos agregados para las estadísticas de bloqueo capturadas por Cloud Spanner en un período específico:

  • SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: estadísticas agregadas de todas las esperas de bloqueo durante intervalos de 1 minuto.

  • SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: estadísticas agregadas de todas las esperas de bloqueo durante intervalos de 10 minutos.

  • SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: estadísticas agregadas de todas las esperas de bloqueo durante intervalos de 1 hora.

Las tablas conjuntas de estadísticas tienen las siguientes propiedades:

  • Cada tabla contiene datos de intervalos de tiempo no superpuestos de la longitud que se especifica en el nombre de la tabla.

  • Los intervalos se basan en tiempos de reloj. Los intervalos de 1 minuto finalizan en el minuto, los intervalos de 10 minutos finalizan cada 10 minutos a partir de la hora y los intervalos de 1 hora finalizan en la hora.

    Por ejemplo, a las 11:59:30 a.m., los intervalos más recientes disponibles para las consultas de SQL en las estadísticas de bloqueo agregadas son los siguientes:

    • 1 minuto: de 11:58:00 a 11:58:59 a.m.
    • 10 minutos: de 11:40:00 a 11:49:59 a.m.
    • 1 hora: de 10:00:00 a 10:59:59 a.m.
  • Cada fila contiene estadísticas de todos los bloqueos de la base de datos durante el intervalo especificado, combinadas. Solo hay una fila por intervalo de tiempo.

  • Las estadísticas capturadas en las tablas SPANNER_SYS.LOCK_STATS_TOTAL_* incluyen tiempos de bloqueo que Cloud Spanner no capturó en las tablas SPANNER_SYS.LOCK_STATS_TOP_*.

Esquema de la tabla

Nombre de la columna Tipo Descripción
INTERVAL_END TIMESTAMP Fin del intervalo en el que se produjo el conflicto de bloqueo
TOTAL_LOCK_WAIT_SECONDS FLOAT64 Tiempo de espera de bloqueo total de conflictos de bloqueo registrados para toda la base de datos, en segundos

Consultas de ejemplo

El siguiente es un ejemplo de una instrucción de SQL que puedes usar para recuperar estadísticas de bloqueo. Puedes ejecutar estas instrucciones de SQL con las bibliotecas cliente, la herramienta de línea de comandos de gcloud o Cloud Console.

Enumera las estadísticas de bloqueo del intervalo de 1 minuto anterior

La siguiente consulta muestra la información de espera de bloqueo para cada clave de fila con un conflicto de bloqueo, incluida la fracción de conflictos de bloqueo totales, durante el intervalo de tiempo más reciente de 1 minuto.

La función CAST() convierte el campo BYTES_range_start_key BYTES en una STRING.

SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
       t.total_lock_wait_seconds,
       s.lock_wait_seconds,
       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
       s.sample_lock_requests
FROM spanner_sys.lock_stats_top_minute s, spanner_sys.lock_stats_total_minute t
WHERE s.interval_end =
  (SELECT MAX(s.interval_end)
   FROM spanner_sys.lock_stats_top_minute)
AND s.interval_end = t.interval_end
ORDER BY s.lock_wait_seconds DESC;
Resultado de la consulta
clave_rango_inicio_clave total_lock_wait_seconds lock_wait_seconds frac_de_total solicitudes_muestra_muestra
Canciones(2,1,1) 0.184476 0.184476 1 LOCK_MODE: Lectores compartidos

COLUMNA: Singers.SingerInfo

LOCK_MODE: Escritor compartido

COLUMNA: Singers.SingerInfo
Usuarios(3, 12-11-01 12:34:56.426426+00:00) 5,21 2.37 0,454 LOCK_MODE: Lectores compartidos

COLUMNA: usuarios._existe1

LOCK_MODE: Escritor compartido

COLUMNA: usuarios._existe1

1 _exists es un campo interno que se usa para verificar si existe una determinada fila o no.

Retención de datos

Como mínimo, Cloud Spanner conserva los datos para cada tabla durante los siguientes períodos:

  • SPANNER_SYS.LOCK_STATS_TOP_MINUTE y SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: Intervalos que abarcan las 6 horas anteriores.

  • SPANNER_SYS.LOCK_STATS_TOP_10MINUTE y SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: Intervalos que abarcan los 4 días anteriores.

  • SPANNER_SYS.LOCK_STATS_TOP_HOUR y SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: Intervalos que abarcan los 30 días anteriores.

Soluciona problemas de bloqueo en tu base de datos con estadísticas de bloqueo

En el siguiente procedimiento, te mostraremos cómo usar las estadísticas de bloqueo para investigar los conflictos de bloqueo en tu base de datos.

Imagina que escuchas que la aplicación de tu empresa es lenta. Usa los siguientes pasos para investigar el problema.

Selecciona un período de tiempo para investigar

Examina las Métricas de latencia de tu base de datos de Cloud Spanner y descubres un período cuando tu app experimenta una latencia alta y uso de CPU. Por ejemplo, supongamos que el problema comenzó el 10 de noviembre de 2020 a las 10:50 p.m.

Determina si la latencia de confirmación de la transacción aumentó junto con el tiempo de espera de bloqueo durante el período seleccionado

Las transacciones se adquieren mediante transacciones, por lo que, si los conflictos de bloqueo provocan tiempos de espera prolongados, debemos poder ver el aumento en la latencia de confirmación de la transacción junto con el aumento en el tiempo de espera de bloqueo.

Después de seleccionar un período para comenzar nuestra investigación, nos uniremos a las estadísticas de transacciones TXN_STATS_TOTAL_10MINUTE con estadísticas de bloqueo LOCK_STATS_TOTAL_10MINUTE alrededor de ese momento para ayudarnos a comprender si el aumento de la latencia de confirmación promedio se contribuye al aumento del tiempo de espera de bloqueo.

SELECT t.interval_end, t.avg_commit_latency_seconds, l.total_lock_wait_seconds
FROM spanner_sys.txn_stats_total_10minute t
LEFT JOIN spanner_sys.lock_stats_total_10minute l
ON t.interval_end = l.interval_end
WHERE
  t.interval_end >= "2020-11-12T21:50:00Z"
  AND t.interval_end <= "2020-11-12T23:50:00Z"
ORDER BY interval_end;

Tomemos los siguientes datos como ejemplo de los resultados que obtenemos de nuestra consulta.

interval_end avg_commit_latency_seconds total_lock_wait_seconds
12-1-12: 21:40:00-07:00 0.002 0,05
11-1-12 21:50:00-07:00 0,003 0,10
12/11/2020, 22:00:00-07:00 0.002 0,100
22-1-12: 22:10:00-07:00 0.002 0,80
2020 11-22, 22:2-07:00 0,30 0,240
2020 11/12 22:30:00-07:00 0,03 0,20
12-1/12: 22:40:00-07:00 0,03 0,18
12-1-12 22:50:00-07:00 3.741 780,193
2020-1-12 23:00:00-07:00 0,0242 0,240
12-1-12: 23:10:00-07:00 0,0.0 0,29
2020-1-12 23:20:00-07:00 0,02 0,128
2020-1-12 23:30:00-07:00 0,0.0 0,231

Estos resultados anteriores muestran un aumento notable en avg_commit_latency_seconds y total_lock_wait_seconds durante el mismo período de 2020-1-12: 22:40:00 a 2020-1-12 22:50:00 y se quitó después. Algo que debes tener en cuenta es que avg_commit_latency_seconds es el tiempo promedio empleado solo para el paso de confirmación. Por otro lado, total_lock_wait_seconds es el tiempo de bloqueo total del período, por lo que el tiempo es mucho más largo que el de la confirmación de la transacción.

Ahora que hemos confirmado que el tiempo de espera de bloqueo está estrechamente relacionado con el aumento en la latencia de escritura, lo analizaremos en el siguiente paso, qué filas y columnas causan un tiempo de espera prolongado.

Descubrir qué claves de fila y columnas tenían tiempos de espera de bloqueo prolongados durante el período seleccionado

Para averiguar qué claves y claves de fila experimentaron tiempos de espera de bloqueo alto durante el período que investigamos, consultamos elLOCK_STAT_TOP_10MINUTE tabla, que enumera las claves de filas y las columnas que contribuyen a la espera de bloqueo.

La función CAST() en la siguiente consulta convierte el campo row_range_start_key BYTES a una STRING.

SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
       t.total_lock_wait_seconds,
       s.lock_wait_seconds,
       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
       s.sample_lock_requests
FROM spanner_sys.lock_stats_top_10minute s, spanner_sys.lock_stats_total_10minute t
WHERE
  t.interval_end = "2020-11-12T22:50:00Z" and s.interval_end = t.interval_end;
clave_rango_inicio_clave total_lock_wait_seconds lock_wait_seconds frac_de_total solicitudes_muestra_muestra
Cantantes(32) 780,193 780,193 1 LOCK_MODE: Escritor compartido

COLUMNA: Singers.SingerInfo

LOCK_MODE: Lectores compartidos

COLUMNA: Singers.SingerInfo

En esta tabla de resultados, podemos ver el conflicto en la tabla Singers de la clave SingerId=32. Singers.SingerInfo es la columna en la que se produjo el conflicto de bloqueo entre ReaderShared y WriterShared.

Este es un tipo común de conflicto cuando hay una transacción que intenta leer una determinada celda y la otra, intenta escribir en la misma celda. Ahora conocemos la celda de datos exacta para la que las transacciones compiten con el candado; por lo tanto, en el siguiente paso identificaremos las transacciones que prevalecen para los bloqueos.

Encontrar qué transacciones acceden a las columnas involucradas en el conflicto de bloqueo

Para averiguar qué transacciones intentan leer o escribir las columnas involucradas en el conflicto de bloqueo, unimos las estadísticas de transacción con estadísticas de bloqueo en la siguiente consulta.

Para identificar las transacciones, recupera fprint, read_columns, write_constructive_columns y avg_commit_latency_seconds de TXN_STATS_TOTAL_10MINUTE.

SELECT
  fprint,
  read_columns,
  write_constructive_columns,
  avg_commit_latency_seconds
FROM spanner_sys.txn_stats_top_10minute t2
WHERE (
  EXISTS (
    SELECT * FROM t2.read_columns columns WHERE columns IN (
      SELECT DISTINCT(req.COLUMN)
      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
      WHERE req.LOCK_MODE = "ReaderShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
OR
  EXISTS (
    SELECT * FROM t2.write_constructive_columns columns WHERE columns IN (
      SELECT DISTINCT(req.COLUMN)
      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
      WHERE req.LOCK_MODE = "WriterShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
)
AND t2.interval_end ="2020-11-12T23:50:00Z"
ORDER BY avg_commit_latency_seconds DESC;

La consulta identifica las siguientes transacciones en el período que investigamos.

  • Las transacciones que se leen de cualquiera de las columnas que generaron un conflicto de bloqueo mientras se intentaban obtener el bloqueo ReaderShared

  • Las transacciones que se escriben en cualquiera de las columnas que generaron un conflicto de bloqueo mientras se intentaban adquirir un bloqueo WriterShared

El resultado de la consulta se ordena por la columna avg_commit_latency_seconds, por lo que la transacción que puede verse afectada por la espera de la mayor cantidad de resultados se muestra primero.

fprint read_columns write_constructive_columns avg_commit_latency_seconds
186604399671516800


['Singers.SingerInfo',
'Singers.FirstName',
'Singers.LastName',
'Singers__exists']
['Singers.SingerInfo'] 4,89
4168578515815911936 [] ['Singers.SingerInfo'] 3,65

Como se muestra los resultados de la consulta, dos transacciones intentaron acceder a Singers.SingerInfo, que es la columna que tuvo conflictos de bloqueo durante el período. Una vez identificadas las transacciones que causan los conflictos de bloqueo, puedes analizar las transacciones con su huella digital, fprint, para identificar los posibles problemas que contribuyeron al problema.

Después de revisar la transacción con fprint 1866043996151616, descubrimos que el DML en la transacción en cuestión no se filtra en la clave primaria, SingerId. Por lo tanto, se produjo un análisis completo de la tabla y se bloqueó la tabla hasta que la transacción se confirmó. Si conocemos el SingerId, podemos actualizar la cláusula WHERE en la instrucción para que funcione en el SingerId. Si no sabemos los ID para las filas que se actualizarán, podemos usar una transacción de solo lectura independiente para obtener los ID primero y, luego, enviar otra transacción de lectura y escritura para actualizar el filas basadas en los ID.

Aplica las prácticas recomendadas para reducir la competencia de bloqueo

En nuestro caso de ejemplo, pudimos usar las estadísticas de bloqueo y las estadísticas de transacciones para acotar nuestro problema a una transacción que no usaba la clave primaria de nuestra tabla cuando realizamos actualizaciones. Se nos ocurrieron ideas para mejorar la transacción en función de si conocemos las claves de las filas que queríamos actualizar antes o no.

Cuando examines posibles problemas en tu solución o incluso cuando diseñes tu solución, ten en cuenta estas prácticas recomendadas para reducir la cantidad de conflictos de bloqueo en tu base de datos.

  • Usa transacciones de solo lectura cuando sea posible, ya que no adquieren bloqueos.

  • Evita los análisis completos de la tabla en una transacción de lectura y escritura. Esto incluye escribir un condicional de DML en la clave primaria o asignar un rango de claves específico cuando se usa la API de lectura.

  • Para mantener un período de bloqueo corto, confirma el cambio en cuanto puedas leer los datos posible en una transacción de lectura y escritura. Una transacción de lectura y escritura garantiza que los datos permanezcan sin cambios después de que leas los datos hasta que confirmes el cambio correctamente. Para ello, la transacción requiere el bloqueo de las celdas de datos durante la lectura y durante la confirmación. Como resultado, si puedes mantener el período de bloqueo corto, es menos probable que las transacciones tengan conflictos de bloqueo.

  • Prefiere las transacciones pequeñas por transacciones grandes o considera DML particionado para las transacciones DML de larga duración. Una transacción de larga duración adquiere un bloqueo por mucho tiempo, por lo tanto, considera dividir una transacción que toca miles de filas en varias transacciones más pequeñas que actualicen cientos de filas siempre que sea posible.

  • Si no necesitas la garantía que proporciona una transacción de lectura y escritura, evita leer datos en la transacción de lectura y escritura antes de confirmar el cambio, por ejemplo, lee los datos en una lectura de solo lectura. transacción. La mayoría de los conflictos de bloqueo se producen debido a la fuerte garantía, para garantizar que los datos no se modifiquen entre la lectura y la confirmación. Por lo tanto, si la transacción de lectura y escritura no lee datos, no necesita bloquear las celdas durante mucho tiempo.

  • Sigue las prácticas recomendadas de diseño de esquemas.

¿Qué sigue?