Las filas calientes son registros de la base de datos que se modifican con frecuencia. En escenarios de alta concurrencia, actualizar estas filas provoca una fuerte contención de bloqueos y tiempos de espera prolongados, lo que degrada el rendimiento del sistema. Para resolver este problema, PolarDB implementa optimizaciones en el kernel de la base de datos que mejoran significativamente el rendimiento del sistema.
Contexto
Las filas calientes presentan los siguientes desafíos:
Cuando una transacción actualiza un registro, adquiere un bloqueo sobre la fila destino y no lo libera hasta que la transacción se confirma o se revierte. Durante ese tiempo, solo una transacción puede actualizar la fila, por lo que las demás deben esperar. Esto significa que las solicitudes de actualización de una sola fila caliente se ejecutan de forma secuencial. Las estrategias tradicionales de fragmentación de bases de datos y tablas ofrecen mejoras de rendimiento limitadas en este escenario.
En estos escenarios, una gran cantidad de solicitudes de actualización de filas calientes puede llegar al sistema de base de datos del backend en poco tiempo. Esto genera una fuerte contención de bloqueos de fila y tiempos de espera prolongados, lo que degrada el rendimiento del sistema. Una espera larga en una solicitud de actualización puede afectar significativamente las operaciones del negocio.
Escalar el hardware no basta para cumplir estos requisitos de baja latencia. Por ello, PolarDB ofrece optimizaciones innovadoras en el kernel de la base de datos. El sistema identifica automáticamente las solicitudes de actualización de filas calientes y agrupa las actualizaciones de un mismo registro que ocurren dentro de un intervalo de tiempo específico. Luego, procesa los distintos grupos en paralelo mediante un pipeline. Estas optimizaciones mejoran notablemente el rendimiento del sistema.
Solución técnica
-
Del procesamiento secuencial al procesamiento por pipeline
El procesamiento en paralelo es la forma más directa de mejorar el rendimiento de una base de datos, pero resulta difícil paralelizar por completo las operaciones de actualización sobre una misma fila caliente. PolarDB utiliza un enfoque innovador de procesamiento por pipeline para maximizar el paralelismo de estas operaciones.
Las sentencias SQL utilizadas para actualizar filas calientes se marcan con
autocommito COMMIT_ON_SUCCESS. La capa optimizada del kernel de MySQL identifica automáticamente las operaciones de actualización con estas marcas. Dentro de un intervalo de tiempo determinado, aplica unHasha las operaciones recopiladas según su clave primaria o clave única. Para las operaciones que elHashasigna al mismo bucket, el kernel las agrupa y las confirma en lotes según su orden de llegada.Para procesar estas operaciones mediante un pipeline, se utilizan dos unidades de ejecución que las agrupan. Cuando el primer grupo se recopila y está listo para confirmarse, el segundo comienza inmediatamente a recopilar operaciones de actualización. Cuando el segundo grupo se recopila y está listo para confirmarse, el primero ya se confirmó y empieza a recopilar un nuevo lote. Ambos grupos se alternan y se ejecutan en paralelo.
Este modelo de procesamiento por pipeline aprovecha al máximo los recursos de hardware, aumenta la utilización de la CPU y mejora la capacidad de procesamiento en paralelo del sistema, lo que maximiza su rendimiento.
-
Eliminación de esperas al solicitar bloqueos de fila
Para garantizar la coherencia lógica de los datos, una actualización adquiere primero un bloqueo sobre la fila destino. Si la solicitud de bloqueo no se concede de inmediato, entra en estado de espera. Esto no solo aumenta la latencia de procesamiento, sino que también activa la detección de interbloqueos, lo que añade sobrecarga adicional.
Como se mencionó antes, las operaciones de actualización sobre un mismo registro se agrupan en orden cronológico. La primera operación de un grupo actúa como Leader: lee la fila destino y la bloquea. Las operaciones posteriores actúan como Followers. Cuando un Follower solicita un bloqueo sobre la fila destino y detecta que el Leader ya lo tiene, adquiere el bloqueo de inmediato sin esperar.
Esta optimización reduce la cantidad de adquisiciones de bloqueos de fila y el tiempo asociado. Como resultado, el rendimiento general del sistema mejora de manera significativa.
-
Reducción de recorridos de índices B-tree
MySQL gestiona los datos mediante índices B-tree. Cada consulta debe recorrer el índice para ubicar la fila destino. Entre más grande sea la tabla y más profundo el índice, más tiempo tomará el recorrido.
En el mecanismo de agrupación descrito anteriormente, solo el Leader de cada grupo recorre el índice para ubicar el registro y luego almacena la fila actualizada en memoria (Row Cache). Después de que un Follower del mismo grupo adquiere correctamente el bloqueo, lee la fila destino directamente desde la memoria sin recorrer el índice de nuevo.
Esto reduce la cantidad total de recorridos de índices y su sobrecarga asociada.
Requisitos previos
-
Su clúster de PolarDB debe ejecutar una de las siguientes versiones:
PolarDB for MySQL 5.6, con una versión secundaria del motor 20200601 o posterior.
PolarDB for MySQL 5.7, con una versión secundaria del motor 5.7.1.0.17 o posterior.
PolarDB for MySQL 8.0, con una versión secundaria del motor 8.0.1.1.10 o posterior.
binlog está habilitado.
-
El parámetro de clúster
rds_ic_reduce_hint_enabledebe estar deshabilitado.En PolarDB for MySQL 5.6 y PolarDB for MySQL 8.0, este parámetro está deshabilitado de forma predeterminada.
En PolarDB for MySQL 5.7, este parámetro está habilitado de forma predeterminada. Antes de habilitar la optimización de filas calientes, debe change the parameter value a OFF.
NotaPara garantizar la compatibilidad con los archivos de configuración de MySQL, PolarDB console antepone el prefijo loose_ a todos los parámetros del clúster. Para modificar el parámetro
rds_ic_reduce_hint_enableen PolarDB console, seleccione el parámetro con el prefijo loose_, es decir, loose_rds_ic_reduce_hint_enable.
Limitaciones
La optimización de filas calientes no aplica en los siguientes escenarios:
La tabla que contiene la fila caliente es una tabla particionada.
Se definió un trigger en la tabla que contiene la fila caliente.
La fila caliente utiliza el mecanismo Statement Queue.
Si el binlog global está habilitado pero el binlog a nivel de sesión está deshabilitado, la optimización de filas calientes no se aplica a las sentencias
UPDATE.Después de habilitar la optimización de filas calientes, las columnas que dependen únicamente del atributo
ON UPDATE CURRENT_TIMESTAMPpara actualizaciones automáticas dejarán de actualizarse de forma automática. Debe asignar valores explícitamente a estas columnas en sus sentenciasUPDATEmedianteSET column_name = CURRENT_TIMESTAMP(n).
Uso
-
Habilite la optimización de filas calientes.
En PolarDB console, puede modify the following parameter para habilitar o deshabilitar la optimización de filas calientes.
Parámetro
Descripción
hotspot
Especifica si se habilita la función de optimización de filas calientes. Valores válidos:
ON: habilitada.
OFF (predeterminado): deshabilitada.
NotaPara garantizar la compatibilidad con los archivos de configuración de MySQL, PolarDB console antepone el prefijo loose_ a todos los parámetros del clúster. Para modificar el parámetro hotspot en PolarDB console, seleccione el parámetro con el prefijo loose_, es decir, loose_hotspot.
-
Utilice la sintaxis de hint para aplicar la optimización de filas calientes.
Hint
Obligatorio
Descripción
Obligatorio
Confirma la transacción si la actualización se completa correctamente.
Opcional
Revierte la transacción si la actualización falla.
Opcional
Indica que se espera que la solicitud actualice solo una fila. Si no se cumple esta condición, la actualización falla.
NotaDado que este hint confirma la transacción automáticamente, debe incluirlo en la última sentencia SQL de la transacción.
Ejemplo: actualice el valor de la columna
cen la tablasbtest.UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
Operaciones relacionadas
Parámetros personalizados
No es posible modificar los siguientes parámetros en PolarDB console. Para modificarlos, vaya a Quota Center, busque el identificador de cuota polardb_mysql_hotspot y haga clic en Apply en la columna Actions.
Parámetro | Descripción |
hotspot_for_autocommit | Especifica si se habilita la optimización de filas calientes para las sentencias
|
hotspot_update_max_wait_time | Tiempo máximo que un Leader espera a que los Followers se unan a un grupo durante un Group Update.
|
hotspot_lock_type | Especifica si se habilita un nuevo tipo de bloqueo de fila para Group Update. Valores válidos:
Nota
|
Configuración de parámetros
Ejecute el siguiente comando para consultar la configuración de parámetros de la optimización de filas calientes.
SHOW variables LIKE "hotspot%";
Resultado de ejemplo:
+------------------------------+-------+
|Variable_name | Value |
+------------------------------+-------+
|hotspot | OFF |
|hotspot_for_autocommit | OFF |
|hotspot_lock_type | OFF |
|hotspot_update_max_wait_time | 100 |
+------------------------------+-------+
Estadísticas de uso
Ejecute el siguiente comando para consultar las estadísticas de uso de la optimización de filas calientes.
SHOW GLOBAL status LIKE 'Group_update%';
Prueba de rendimiento
Herramienta de prueba
Sysbench es una herramienta de pruebas de rendimiento multiplataforma y de código abierto. Se utiliza principalmente para benchmarks de bases de datos, como MySQL, y para pruebas de rendimiento del sistema en componentes como la CPU, la memoria, la E/S y los hilos. Admite pruebas multihilo y usa scripts de Lua para controlar de forma flexible la lógica de las pruebas. Es adecuada para escenarios como la evaluación de rendimiento de bases de datos y las pruebas de estrés.
Tabla y sentencia de prueba
-
Definición de la tabla
CREATE TABLE sbtest (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id)); -
Sentencia de prueba
UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
Resultados de la prueba
PolarDB for MySQL 5.6
Escenario de prueba
Una sola fila caliente en una CPU de 8 núcleos.
Resultados de la prueba
En este escenario de prueba, habilitar la optimización de filas calientes mejora casi 50 veces el rendimiento de actualización de filas calientes relacionadas con inventarios bajo alta concurrencia.
Datos de la prueba (QPS)

|
Concurrencia |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Optimización de filas calientes deshabilitada |
1365.31 |
1863.94 |
1866.6 |
1862.64 |
1867.32 |
1832.51 |
1838.31 |
1819.52 |
1833.2 |
|
Optimización de filas calientes habilitada |
1114.79 |
7000.19 |
12717.32 |
22029.48 |
43096.06 |
61349.7 |
83098.69 |
90860.94 |
87689 |
PolarDB for MySQL 5.7
Escenario de prueba
Una sola fila caliente en una CPU de 8 núcleos.
Resultados de la prueba
En este escenario de prueba, habilitar la optimización de filas calientes mejora casi 35 veces el rendimiento de actualización de filas calientes relacionadas con inventarios bajo alta concurrencia.
Datos de la prueba
QPS

|
Concurrencia |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Optimización de filas calientes deshabilitada |
1348.49 |
1892.29 |
1889.77 |
1895.86 |
1875.2 |
1850.26 |
1843.62 |
1849.92 |
1835.68 |
|
Optimización de filas calientes habilitada |
1104.9 |
6886.89 |
12485.17 |
16003.23 |
16460.31 |
16548.86 |
27920.89 |
47893.96 |
66500.92 |
Latencia del percentil 95

|
Concurrencia |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Optimización de filas calientes deshabilitada |
0.9 |
5.47 |
9.91 |
18.95 |
36.89 |
73.13 |
164.45 |
297.92 |
590.56 |
|
Optimización de filas calientes habilitada |
1.08 |
1.44 |
1.58 |
3.25 |
5.28 |
9.56 |
12.08 |
13.22 |
18.28 |
PolarDB for MySQL 8.0
Escenario de prueba
Una sola fila caliente en una CPU de 8 núcleos.
Resultados de la prueba
En este escenario de prueba, habilitar la optimización de filas calientes mejora casi 26 veces el rendimiento de actualización de filas calientes relacionadas con inventarios bajo alta concurrencia.
Datos de la prueba
QPS

|
Concurrencia |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Optimización de filas calientes deshabilitada |
1559.14 |
2103.82 |
2116.4 |
2082.1 |
2079.74 |
2031.64 |
1993.09 |
1977.6 |
1983.61 |
|
Optimización de filas calientes habilitada |
1237.28 |
7443.04 |
12244.19 |
15529.52 |
23041.15 |
33931.18 |
53924.24 |
54598.6 |
50988.22 |
Latencia del percentil 95

|
Concurrencia |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Optimización de filas calientes deshabilitada |
0.8 |
5 |
8.9 |
17.32 |
33.12 |
66.84 |
153.02 |
287.38 |
549.52 |
|
Optimización de filas calientes habilitada |
0.97 |
1.34 |
1.89 |
3.19 |
4.82 |
5.88 |
7.17 |
13.46 |
28.16 |
Pasos de la prueba de rendimiento
Prepare una instancia de ECS y install Sysbench.
-
Coloque el siguiente archivo
oltp_inventory.luaen el directoriosrc/luadel código fuente de Sysbench.#!/usr/bin/env sysbench -- it is to test inventory_hotspot performance sysbench.cmdline.options= { inventory_hotspot = {"enable ali inventory hotspot", 'off'}, tables = {"table number", 1}, table_size = {"table size", 1}, oltp_skip_trx = {'skip trx', true}, hotspot_rows = {'hotspot row number', 1} } function cleanup() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("drop table sbtest%d ...", i)) drop_table(drv, con, i) end end function drop_table(drv, con, table_id) local query query = string.format("drop table if exists sbtest%d ", table_id) con:query(query) end function create_table(drv, con, table_id) local query query = string.format("CREATE TABLE sbtest%d (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id))", table_id) con:query(query) for i=1, sysbench.opt.table_size do con:query("INSERT INTO sbtest" .. table_id .. "(id, c) values (" ..i.. ", 1)") end end function prepare() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("Creating table sbtest%d ...", i)) create_table(drv, con, i) end end function thread_init() drv = sysbench.sql.driver() con = drv:connect() begin_query = 'BEGIN' commit_query = 'COMMIT' end function event() local table_name table_name = "sbtest" .. sysbench.rand.uniform(1, sysbench.opt.tables) local min_line = math.min(sysbench.opt.table_size, sysbench.opt.hotspot_rows) local row_id = sysbench.rand.uniform(1, min_line) if not sysbench.opt.oltp_skip_trx then con:query(begin_query) end if (sysbench.opt.inventory_hotspot == "on") then con:query("UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ " .. table_name .. " SET c=c+1 WHERE id =" .. row_id) else con:query("UPDATE " .. table_name .. " SET c=c+1 WHERE id = " .. row_id) end if not sysbench.opt.oltp_skip_trx then if (sysbench.opt.inventory_hotspot == "on") then con:query(commit_query) end end end function thread_done() con:disconnect() end -
Ejecute la prueba de Sysbench.
-
Prepare los datos.
sysbench --hotspot_rows=1 --histogram=on --mysql-user=<user> --inventory_hotspot=on --mysql-host=<host> --threads=1 --report-interval=1 --mysql-password=<password> --tables=1 --table-size=1 --oltp_skip_trx=true --db-driver=mysql --percentile=95 --time=300 --mysql-port=<port> --events=0 --mysql-db=<database> oltp_inventory prepare -
Ejecute la prueba.
sysbench --db-driver=mysql --mysql-host=<host> --mysql-port=<port> --mysql-user=<user> --mysql-password=<password> --mysql-db=<database> --range-selects=0 --table_size=25000 --tables=250 --events=0 --time=600 --rand-type=uniform --threads=<threads> oltp_inventory run
Parámetros de entrada
Parámetro
Descripción
mysql-host
El endpoint del clúster.
mysql-port
El puerto del endpoint del clúster.
mysql-user
El nombre de usuario de la cuenta de la base de datos.
mysql-password
La contraseña de la cuenta de la base de datos.
mysql-db
El nombre de la base de datos.
Parámetros de salida
Parámetro
Métrica
Descripción
tables
Número de tablas
Cantidad total de tablas utilizadas en la prueba.
table_size
Número de filas por tabla
Cantidad de registros en cada tabla.
Tamaño de los datos
Tamaño de los datos de la tabla, medido en unidades como MB o GB.
threads
Número de hilos concurrentes
Cantidad de hilos configurados.
Estado de los hilos
Estado en tiempo real de los hilos en ejecución.
-