mysql: Biblioteca de controlador MySQL Lua no bloqueante para nginx-module-lua
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-mysql
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-mysql
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-mysql v0.29 lanzado el 14 de enero de 2026.
Esta biblioteca Lua es un controlador cliente MySQL para el módulo ngx_lua de nginx:
https://github.com/openresty/lua-nginx-module
Esta biblioteca Lua aprovecha la API de cosocket de ngx_lua, que garantiza un comportamiento 100% no bloqueante.
Ten en cuenta que se requiere al menos ngx_lua 0.9.11 o ngx_openresty 1.7.4.1.
Además, también se requiere la biblioteca bit. Si estás utilizando LuaJIT 2 con ngx_lua, entonces la biblioteca bit ya está disponible por defecto.
Sinopsis
# no necesitas la siguiente línea si estás usando
# el paquete ngx_openresty:
server {
location /test {
content_by_lua '
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("falló al instanciar mysql: ", err)
return
end
db:set_timeout(1000) -- 1 seg
-- o conecta a un archivo de socket de dominio unix escuchado
-- por un servidor mysql:
-- local ok, err, errcode, sqlstate =
-- db:connect{
-- path = "/path/to/mysql.sock",
-- database = "ngx_test",
-- user = "ngx_test",
-- password = "ngx_test" }
local ok, err, errcode, sqlstate = db:connect{
host = "127.0.0.1",
port = 3306,
database = "ngx_test",
user = "ngx_test",
password = "ngx_test",
charset = "utf8",
max_packet_size = 1024 * 1024,
}
if not ok then
ngx.say("falló al conectar: ", err, ": ", errcode, " ", sqlstate)
db:close()
return
end
ngx.say("conectado a mysql.")
local res, err, errcode, sqlstate =
db:query("drop table if exists cats")
if not res then
ngx.say("resultado incorrecto: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
res, err, errcode, sqlstate =
db:query("create table cats "
.. "(id serial primary key, "
.. "name varchar(5))")
if not res then
ngx.say("resultado incorrecto: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
ngx.say("tabla cats creada.")
res, err, errcode, sqlstate =
db:query("insert into cats (name) "
.. "values (\'Bob\'),(\'\'),(null)")
if not res then
ngx.say("resultado incorrecto: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
ngx.say(res.affected_rows, " filas insertadas en la tabla cats ",
"(último id insertado: ", res.insert_id, ")")
-- ejecuta una consulta select, se espera alrededor de 10 filas en
-- el conjunto de resultados:
res, err, errcode, sqlstate =
db:query("select * from cats order by id asc", 10)
if not res then
ngx.say("resultado incorrecto: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
local cjson = require "cjson"
ngx.say("resultado: ", cjson.encode(res))
-- colócalo en el pool de conexiones de tamaño 100,
-- con un tiempo de espera máximo inactivo de 10 segundos
local ok, err = db:set_keepalive(10000, 100)
if not ok then
ngx.say("falló al establecer keepalive: ", err)
db:close()
return
end
-- o simplemente cierra la conexión de inmediato:
-- local ok, err = db:close()
-- if not ok then
-- ngx.say("falló al cerrar: ", err)
-- return
-- end
';
}
}
Métodos
new
syntax: db, err = mysql:new()
Crea un objeto de conexión MySQL. En caso de fallos, devuelve nil y una cadena que describe el error.
connect
syntax: ok, err, errcode, sqlstate = db:connect(options)
Intenta conectarse al servidor MySQL remoto.
El argumento options es una tabla Lua que contiene las siguientes claves:
-
hostel nombre del host para el servidor MySQL. *
portel puerto en el que el servidor MySQL está escuchando. Por defecto es 3306. *
pathla ruta del archivo de socket unix escuchado por el servidor MySQL. *
databaseel nombre de la base de datos MySQL. *
usernombre de la cuenta MySQL para iniciar sesión. *
passwordcontraseña de la cuenta MySQL para iniciar sesión (en texto claro). *
charsetel conjunto de caracteres utilizado en la conexión MySQL, que puede ser diferente de la configuración de conjunto de caracteres por defecto. Los siguientes valores son aceptados:
big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,utf16le,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms,gb18030. *max_packet_sizeel límite superior para los paquetes de respuesta enviados desde el servidor MySQL (por defecto es 1MB). *
sslSi se establece en
true, entonces utiliza SSL para conectarse a MySQL (por defecto esfalse). Si el servidor MySQL no tiene soporte para SSL (o simplemente está deshabilitado), se devolverá la cadena de error "ssl disabled on server". *ssl_verifySi se establece en
true, entonces verifica la validez del certificado SSL del servidor (por defecto esfalse). Ten en cuenta que necesitas configurar el lua_ssl_trusted_certificate para especificar el CA (o certificado del servidor) utilizado por tu servidor MySQL. También puede que necesites configurar lua_ssl_verify_depth en consecuencia. *poolel nombre para el pool de conexiones MySQL. si se omite, se generará automáticamente un nombre de pool ambiguo con la plantilla de cadena
user:database:host:portouser:database:path. (esta opción se introdujo por primera vez env0.08.) -
pool_sizeEspecifica el tamaño del pool de conexiones. Si se omite y no se proporcionó ninguna opción
backlog, no se creará ningún pool. Si se omite pero se proporcionóbacklog, el pool se creará con un tamaño por defecto igual al valor de la directiva lua_socket_pool_size. El pool de conexiones mantiene hastapool_sizeconexiones vivas listas para ser reutilizadas por llamadas posteriores a connect, pero ten en cuenta que no hay un límite superior para el número total de conexiones abiertas fuera del pool. Si necesitas restringir el número total de conexiones abiertas, especifica la opciónbacklog. Cuando el pool de conexiones exceda su límite de tamaño, la conexión menos utilizada (mantener viva) que ya está en el pool se cerrará para hacer espacio para la conexión actual. Ten en cuenta que el pool de conexiones de cosocket es por proceso de trabajo de Nginx en lugar de por instancia de servidor Nginx, por lo que el límite de tamaño especificado aquí también se aplica a cada proceso de trabajo de Nginx. También ten en cuenta que el tamaño del pool de conexiones no se puede cambiar una vez que ha sido creado. Ten en cuenta que se requiere al menos ngx_lua 0.10.14 para usar estas opciones. -
backlogSi se especifica, este módulo limitará el número total de conexiones abiertas para este pool. No se pueden abrir más conexiones que
pool_sizepara este pool en ningún momento. Si el pool de conexiones está lleno, las operaciones de conexión posteriores se encolarán en una cola igual al valor de esta opción (la cola de "backlog"). Si el número de operaciones de conexión en cola es igual abacklog, las operaciones de conexión posteriores fallarán y devolverán nil más la cadena de error"too many waiting connect operations". Las operaciones de conexión en cola se reanudarán una vez que el número de conexiones en el pool sea menor quepool_size. La operación de conexión en cola se abortará una vez que hayan estado en cola por más deconnect_timeout, controlado por set_timeout, y devolverá nil más la cadena de error "timeout". Ten en cuenta que se requiere al menos ngx_lua 0.10.14 para usar estas opciones. -
compact_arrayscuando esta opción se establece en true, entonces los métodos query y read_result devolverán la estructura de array-de-arrays para el conjunto de resultados, en lugar de la estructura por defecto de array-de-hashes.
Antes de resolver realmente el nombre del host y conectarse al backend remoto, este método siempre buscará en el pool de conexiones conexiones inactivas coincidentes creadas por llamadas anteriores a este método.
set_timeout
syntax: db:set_timeout(time)
Establece el tiempo de espera (en ms) de protección para operaciones posteriores, incluyendo el método connect.
set_keepalive
syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size)
Coloca la conexión MySQL actual inmediatamente en el pool de conexiones de cosocket de ngx_lua.
Puedes especificar el tiempo máximo inactivo (en ms) cuando la conexión está en el pool y el tamaño máximo del pool para cada proceso de trabajo de nginx.
En caso de éxito, devuelve 1. En caso de errores, devuelve nil con una cadena que describe el error.
Solo llama a este método en el lugar donde habrías llamado al método close en su lugar. Llamar a este método convertirá inmediatamente el objeto actual resty.mysql en el estado closed. Cualquier operación posterior diferente de connect() en el objeto actual devolverá el error closed.
get_reused_times
syntax: times, err = db:get_reused_times()
Este método devuelve el número de veces (exitosamente) reutilizadas para la conexión actual. En caso de error, devuelve nil y una cadena que describe el error.
Si la conexión actual no proviene del pool de conexiones incorporado, entonces este método siempre devuelve 0, es decir, la conexión nunca ha sido reutilizada (aún). Si la conexión proviene del pool de conexiones, entonces el valor de retorno siempre es distinto de cero. Por lo tanto, este método también se puede utilizar para determinar si la conexión actual proviene del pool.
close
syntax: ok, err = db:close()
Cierra la conexión mysql actual y devuelve el estado.
En caso de éxito, devuelve 1. En caso de errores, devuelve nil con una cadena que describe el error.
send_query
syntax: bytes, err = db:send_query(query)
Envía la consulta al servidor MySQL remoto sin esperar sus respuestas.
Devuelve los bytes enviados con éxito en caso de éxito y, de lo contrario, devuelve nil y una cadena que describe el error.
Deberías usar el método read_result para leer las respuestas de MySQL después.
read_result
syntax: res, err, errcode, sqlstate = db:read_result()
syntax: res, err, errcode, sqlstate = db:read_result(nrows)
Lee un resultado devuelto del servidor MySQL.
Devuelve una tabla Lua (res) que describe el OK packet de MySQL o el result set packet para el resultado de la consulta.
Para consultas que corresponden a un conjunto de resultados, devuelve un array que contiene todas las filas. Cada fila contiene pares clave-valor para cada campo de datos. Por ejemplo,
{
{ name = "Bob", age = 32, phone = ngx.null },
{ name = "Marry", age = 18, phone = "10666372"}
}
Para consultas que no corresponden a un conjunto de resultados, devuelve una tabla Lua como esta:
{
insert_id = 0,
server_status = 2,
warning_count = 1,
affected_rows = 32,
message = nil
}
Si hay más resultados después del resultado actual, un segundo valor de retorno err tendrá la cadena again. Siempre se debe verificar este (segundo) valor de retorno y si es again, entonces se debe llamar a este método nuevamente para recuperar más resultados. Esto suele ocurrir cuando la consulta original contiene múltiples declaraciones (separadas por punto y coma en la misma cadena de consulta) o al llamar a un procedimiento de MySQL. Consulta también Soporte de múltiples conjuntos de resultados.
En caso de errores, este método devuelve como máximo 4 valores: nil, err, errcode y sqlstate. El valor de retorno err contiene una cadena que describe el error, el valor de retorno errcode contiene el código de error de MySQL (un valor numérico) y, finalmente, el valor de retorno sqlstate contiene el código de error SQL estándar que consta de 5 caracteres. Ten en cuenta que, el errcode y sqlstate pueden ser nil si MySQL no los devuelve.
El argumento opcional nrows se puede utilizar para especificar un número aproximado de filas para el conjunto de resultados. Este valor puede ser utilizado para preasignar espacio en la tabla Lua resultante para el conjunto de resultados. Por defecto, toma el valor 4.
query
syntax: res, err, errcode, sqlstate = db:query(query)
syntax: res, err, errcode, sqlstate = db:query(query, nrows)
Este es un atajo para combinar la llamada a send_query y la primera llamada a read_result.
Siempre debes verificar si el valor de retorno err es again en caso de éxito porque este método solo llamará a read_result una vez por ti. Consulta también Soporte de múltiples conjuntos de resultados.
server_ver
syntax: str = db:server_ver()
Devuelve la cadena de versión del servidor MySQL, como "5.1.64".
Solo debes llamar a este método después de conectarte exitosamente a un servidor MySQL, de lo contrario se devolverá nil.
set_compact_arrays
syntax: db:set_compact_arrays(boolean)
Establece si usar la estructura de "arrays compactos" para los conjuntos de resultados devueltos por consultas posteriores. Consulta la opción compact_arrays para el método connect para más detalles.
Este método se introdujo por primera vez en la versión v0.09.
Cita Literal SQL
Siempre es importante citar correctamente los literales SQL para prevenir ataques de inyección SQL. Puedes usar la función ngx.quote_sql_str proporcionada por ngx_lua para citar valores. Aquí hay un ejemplo:
local name = ngx.unescape_uri(ngx.var.arg_name)
local quoted_name = ngx.quote_sql_str(name)
local sql = "select * from users where name = " .. quoted_name
Soporte de Múltiples Conjuntos de Resultados
Para una consulta SQL que produce múltiples conjuntos de resultados, siempre es tu deber verificar el mensaje de error "again" devuelto por las llamadas a query o read_result, y seguir extrayendo más conjuntos de resultados llamando al método read_result hasta que no se devuelva el mensaje de error "again" (o ocurran otros errores).
A continuación se muestra un ejemplo trivial para esto:
local cjson = require "cjson"
local mysql = require "resty.mysql"
local db = mysql:new()
local ok, err, errcode, sqlstate = db:connect({
host = "127.0.0.1",
port = 3306,
database = "world",
user = "monty",
password = "pass"})
if not ok then
ngx.log(ngx.ERR, "falló al conectar: ", err, ": ", errcode, " ", sqlstate)
return ngx.exit(500)
end
res, err, errcode, sqlstate = db:query("select 1; select 2; select 3;")
if not res then
ngx.log(ngx.ERR, "resultado incorrecto #1: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return ngx.exit(500)
end
ngx.say("resultado #1: ", cjson.encode(res))
local i = 2
while err == "again" do
res, err, errcode, sqlstate = db:read_result()
if not res then
ngx.log(ngx.ERR, "resultado incorrecto #", i, ": ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return ngx.exit(500)
end
ngx.say("resultado #", i, ": ", cjson.encode(res))
i = i + 1
end
local ok, err = db:set_keepalive(10000, 50)
if not ok then
ngx.log(ngx.ERR, "falló al establecer keepalive: ", err)
db:close()
ngx.exit(500)
end
Este fragmento de código producirá los siguientes datos en el cuerpo de la respuesta:
resultado #1: [{"1":"1"}]
resultado #2: [{"2":"2"}]
resultado #3: [{"3":"3"}]
Depuración
Usualmente es conveniente usar la biblioteca lua-cjson para codificar los valores de retorno de los métodos de consulta MySQL a JSON. Por ejemplo,
local cjson = require "cjson"
...
local res, err, errcode, sqlstate = db:query("select * from cats")
if res then
print("res: ", cjson.encode(res))
end
Registro Automático de Errores
Por defecto, el módulo subyacente ngx_lua realiza el registro de errores cuando ocurren errores de socket. Si ya estás realizando un manejo adecuado de errores en tu propio código Lua, se recomienda desactivar este registro automático de errores desactivando la directiva lua_socket_log_errors de ngx_lua, es decir,
lua_socket_log_errors off;
Limitaciones
- Esta biblioteca no se puede usar en contextos de código como init_by_lua, set_by_lua, log_by_lua, y header_filter_by_lua donde la API de cosocket de ngx_lua no está disponible.
- La instancia del objeto
resty.mysqlno puede ser almacenada en una variable Lua a nivel de módulo Lua, porque entonces será compartida por todas las solicitudes concurrentes manejadas por el mismo proceso de trabajo de nginx (ver https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker) y resultará en malas condiciones de carrera cuando las solicitudes concurrentes intenten usar la misma instancia deresty.mysql. Siempre debes iniciar los objetosresty.mysqlen variables locales de función o en la tablangx.ctx. Estos lugares tienen sus propias copias de datos para cada solicitud.
Más Soporte para Métodos de Autenticación
Por defecto, de todos los métodos de autenticación, solo se soportan Autenticación de Contraseña Antigua (mysql_old_password) y Autenticación de Contraseña Segura (mysql_native_password). Si el servidor requiere sha256_password o cache_sha2_password, puede devolverse un error como auth plugin caching_sha2_password or sha256_password are not supported because resty.rsa is not installed.
Necesitas lua-resty-rsa al usar sha256_password y cache_sha2_password.
Ver También
- el módulo ngx_lua: https://github.com/openresty/lua-nginx-module
- la especificación del protocolo de conexión MySQL: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol
- la biblioteca lua-resty-memcached
- la biblioteca lua-resty-redis
- el módulo ngx_drizzle: https://github.com/openresty/drizzle-nginx-module
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-mysql.