mysql: Biblioteca de driver MySQL Lua não bloqueante para nginx-module-lua
Instalação
Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Então você pode prosseguir com os seguintes passos.
CentOS/RHEL 7 ou 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 com NGINX, certifique-se de que o nginx-module-lua está instalado.
Este documento descreve lua-resty-mysql v0.29 lançado em 14 de janeiro de 2026.
Esta biblioteca Lua é um driver cliente MySQL para o módulo ngx_lua do nginx:
https://github.com/openresty/lua-nginx-module
Esta biblioteca Lua aproveita a API de cosocket do ngx_lua, que garante um comportamento 100% não bloqueante.
Observe que é necessário pelo menos ngx_lua 0.9.11 ou ngx_openresty 1.7.4.1.
Além disso, a biblioteca bit também é necessária. Se você estiver usando LuaJIT 2 com ngx_lua, então a biblioteca bit já está disponível por padrão.
Sinopse
# você não precisa da linha a seguir se estiver usando
# o pacote ngx_openresty:
server {
location /test {
content_by_lua '
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("falha ao instanciar mysql: ", err)
return
end
db:set_timeout(1000) -- 1 seg
-- ou conecte-se a um arquivo de socket de domínio unix escutado
-- por um 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("falha ao conectar: ", err, ": ", errcode, " ", sqlstate)
db:close()
return
end
ngx.say("conectado ao mysql.")
local res, err, errcode, sqlstate =
db:query("drop table if exists cats")
if not res then
ngx.say("resultado ruim: ", 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 ruim: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
ngx.say("tabela cats criada.")
res, err, errcode, sqlstate =
db:query("insert into cats (name) "
.. "values (\'Bob\'),(\'\'),(null)")
if not res then
ngx.say("resultado ruim: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
ngx.say(res.affected_rows, " linhas inseridas na tabela cats ",
"(último id inserido: ", res.insert_id, ")")
-- execute uma consulta select, espera cerca de 10 linhas no
-- conjunto de resultados:
res, err, errcode, sqlstate =
db:query("select * from cats order by id asc", 10)
if not res then
ngx.say("resultado ruim: ", err, ": ", errcode, ": ", sqlstate, ".")
db:close()
return
end
local cjson = require "cjson"
ngx.say("resultado: ", cjson.encode(res))
-- coloque-o no pool de conexões de tamanho 100,
-- com 10 segundos de tempo máximo ocioso
local ok, err = db:set_keepalive(10000, 100)
if not ok then
ngx.say("falha ao definir keepalive: ", err)
db:close()
return
end
-- ou simplesmente feche a conexão imediatamente:
-- local ok, err = db:close()
-- if not ok then
-- ngx.say("falha ao fechar: ", err)
-- return
-- end
';
}
}
Métodos
new
syntax: db, err = mysql:new()
Cria um objeto de conexão MySQL. Em caso de falhas, retorna nil e uma string descrevendo o erro.
connect
syntax: ok, err, errcode, sqlstate = db:connect(options)
Tenta conectar ao servidor MySQL remoto.
O argumento options é uma tabela Lua contendo as seguintes chaves:
-
hosto nome do host para o servidor MySQL. *
porta porta que o servidor MySQL está escutando. Padrão é 3306. *
patho caminho do arquivo de socket unix escutado pelo servidor MySQL. *
databaseo nome do banco de dados MySQL. *
usernome da conta MySQL para login. *
passwordsenha da conta MySQL para login (em texto claro). *
charseto conjunto de caracteres usado na conexão MySQL, que pode ser diferente da configuração de charset padrão. Os seguintes valores são aceitos:
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_sizeo limite superior para os pacotes de resposta enviados pelo servidor MySQL (padrão é 1MB). *
sslSe definido como
true, então usa SSL para conectar ao MySQL (padrão éfalse). Se o servidor MySQL não tiver suporte a SSL (ou apenas estiver desativado), a string de erro "ssl disabled on server" será retornada. *ssl_verifySe definido como
true, então verifica a validade do certificado SSL do servidor (padrão éfalse). Observe que você precisa configurar o lua_ssl_trusted_certificate para especificar o CA (ou servidor) certificado usado pelo seu servidor MySQL. Você também pode precisar configurar o lua_ssl_verify_depth de acordo. *poolo nome para o pool de conexões MySQL. se omitido, um nome de pool ambíguo será gerado automaticamente com o template de string
user:database:host:portouuser:database:path. (esta opção foi introduzida pela primeira vez nav0.08.) -
pool_sizeEspecifica o tamanho do pool de conexões. Se omitido e nenhuma opção
backlogfoi fornecida, nenhum pool será criado. Se omitido, masbacklogfoi fornecido, o pool será criado com um tamanho padrão igual ao valor da diretiva lua_socket_pool_size. O pool de conexões mantém atépool_sizeconexões ativas prontas para serem reutilizadas por chamadas subsequentes a connect, mas observe que não há limite superior para o número total de conexões abertas fora do pool. Se você precisar restringir o número total de conexões abertas, especifique a opçãobacklog. Quando o pool de conexões exceder seu limite de tamanho, a conexão menos recentemente usada (mantida ativa) já no pool será fechada para abrir espaço para a conexão atual. Observe que o pool de conexões de cosocket é por processo de trabalhador do Nginx, em vez de por instância do servidor Nginx, portanto, o limite de tamanho especificado aqui também se aplica a cada processo de trabalhador do Nginx. Também observe que o tamanho do pool de conexões não pode ser alterado uma vez que foi criado. Observe que é necessário pelo menos ngx_lua 0.10.14 para usar essas opções. -
backlogSe especificado, este módulo limitará o número total de conexões abertas para este pool. Não mais conexões do que
pool_sizepodem ser abertas para este pool a qualquer momento. Se o pool de conexões estiver cheio, operações de conexão subsequentes serão enfileiradas em uma fila igual ao valor desta opção (a fila "backlog"). Se o número de operações de conexão enfileiradas for igual abacklog, operações de conexão subsequentes falharão e retornarão nil mais a string de erro"too many waiting connect operations". As operações de conexão enfileiradas serão retomadas uma vez que o número de conexões no pool for menor quepool_size. A operação de conexão enfileirada será abortada uma vez que tenha sido enfileirada por mais deconnect_timeout, controlada por set_timeout, e retornará nil mais a string de erro "timeout". Observe que é necessário pelo menos ngx_lua 0.10.14 para usar essas opções. -
compact_arraysquando esta opção é definida como verdadeira, então os métodos query e read_result retornarão a estrutura de array-de-arrays para o conjunto de resultados, em vez da estrutura padrão de array-de-hashes.
Antes de realmente resolver o nome do host e conectar ao backend remoto, este método sempre procurará no pool de conexões por conexões ociosas correspondentes criadas por chamadas anteriores deste método.
set_timeout
syntax: db:set_timeout(time)
Define o tempo limite (em ms) de proteção para operações subsequentes, incluindo o método connect.
set_keepalive
syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size)
Coloca a conexão MySQL atual imediatamente no pool de conexões cosocket do ngx_lua.
Você pode especificar o tempo máximo ocioso (em ms) quando a conexão está no pool e o tamanho máximo do pool para cada processo de trabalhador do nginx.
Em caso de sucesso, retorna 1. Em caso de erros, retorna nil com uma string descrevendo o erro.
Chame este método apenas no lugar onde você teria chamado o método close. Chamar este método imediatamente transformará o objeto resty.mysql atual no estado closed. Qualquer operação subsequente além de connect() no objeto atual retornará o erro closed.
get_reused_times
syntax: times, err = db:get_reused_times()
Este método retorna o número de vezes (com sucesso) reutilizadas para a conexão atual. Em caso de erro, retorna nil e uma string descrevendo o erro.
Se a conexão atual não vier do pool de conexões embutido, então este método sempre retornará 0, ou seja, a conexão nunca foi reutilizada (ainda). Se a conexão vem do pool de conexões, então o valor retornado é sempre diferente de zero. Portanto, este método também pode ser usado para determinar se a conexão atual vem do pool.
close
syntax: ok, err = db:close()
Fecha a conexão mysql atual e retorna o status.
Em caso de sucesso, retorna 1. Em caso de erros, retorna nil com uma string descrevendo o erro.
send_query
syntax: bytes, err = db:send_query(query)
Envia a consulta para o servidor MySQL remoto sem esperar suas respostas.
Retorna os bytes enviados com sucesso em caso de sucesso e, caso contrário, retorna nil e uma string descrevendo o erro.
Você deve usar o método read_result para ler as respostas do MySQL posteriormente.
read_result
syntax: res, err, errcode, sqlstate = db:read_result()
syntax: res, err, errcode, sqlstate = db:read_result(nrows)
Lê um resultado retornado do servidor MySQL.
Retorna uma tabela Lua (res) descrevendo o OK packet ou result set packet do MySQL para o resultado da consulta.
Para consultas que correspondem a um conjunto de resultados, retorna um array contendo todas as linhas. Cada linha contém pares chave-valor para cada campo de dados. Por exemplo,
{
{ name = "Bob", age = 32, phone = ngx.null },
{ name = "Marry", age = 18, phone = "10666372"}
}
Para consultas que não correspondem a um conjunto de resultados, retorna uma tabela Lua assim:
{
insert_id = 0,
server_status = 2,
warning_count = 1,
affected_rows = 32,
message = nil
}
Se mais resultados seguirem o resultado atual, um segundo valor de retorno err será dado com a string again. Deve-se sempre verificar este (segundo) valor de retorno e, se for again, então deve-se chamar este método novamente para recuperar mais resultados. Isso geralmente acontece quando a consulta original contém várias instruções (separadas por ponto e vírgula na mesma string de consulta) ou ao chamar um procedimento MySQL. Veja também Suporte a Múltiplos Conjuntos de Resultados.
Em caso de erros, este método retorna no máximo 4 valores: nil, err, errcode e sqlstate. O valor de retorno err contém uma string descrevendo o erro, o valor de retorno errcode contém o código de erro do MySQL (um valor numérico) e, finalmente, o valor de retorno sqlstate contém o código de erro SQL padrão que consiste em 5 caracteres. Observe que os valores errcode e sqlstate podem ser nil se o MySQL não os retornar.
O argumento opcional nrows pode ser usado para especificar um número aproximado de linhas para o conjunto de resultados. Este valor pode ser usado para pré-alocar espaço na tabela Lua resultante para o conjunto de resultados. Por padrão, ele assume o valor 4.
query
syntax: res, err, errcode, sqlstate = db:query(query)
syntax: res, err, errcode, sqlstate = db:query(query, nrows)
Este é um atalho para combinar a chamada send_query e a primeira chamada read_result.
Você deve sempre verificar se o valor de retorno err é again em caso de sucesso, porque este método só chamará read_result uma vez para você. Veja também Suporte a Múltiplos Conjuntos de Resultados.
server_ver
syntax: str = db:server_ver()
Retorna a string da versão do servidor MySQL, como "5.1.64".
Você deve chamar este método apenas após conectar-se com sucesso a um servidor MySQL, caso contrário, nil será retornado.
set_compact_arrays
syntax: db:set_compact_arrays(boolean)
Define se deve usar a estrutura "compact-arrays" para os conjuntos de resultados retornados por consultas subsequentes. Veja a opção compact_arrays para o método connect para mais detalhes.
Este método foi introduzido pela primeira vez na versão v0.09.
Citação Literal SQL
É sempre importante citar literais SQL corretamente para prevenir ataques de injeção SQL. Você pode usar a função ngx.quote_sql_str fornecida pelo ngx_lua para citar valores. Aqui está um exemplo:
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
Suporte a Múltiplos Conjuntos de Resultados
Para uma consulta SQL que produz múltiplos conjuntos de resultados, é sempre seu dever verificar a mensagem de erro "again" retornada pelas chamadas dos métodos query ou read_result, e continuar puxando mais conjuntos de resultados chamando o método read_result até que nenhuma mensagem de erro "again" seja retornada (ou que ocorram outros erros).
Abaixo está um exemplo trivial para isso:
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, "falha ao 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 ruim #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 ruim #", 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, "falha ao definir keepalive: ", err)
db:close()
ngx.exit(500)
end
Este trecho de código produzirá os seguintes dados de corpo de resposta:
resultado #1: [{"1":"1"}]
resultado #2: [{"2":"2"}]
resultado #3: [{"3":"3"}]
Depuração
É geralmente conveniente usar a biblioteca lua-cjson para codificar os valores de retorno dos métodos de consulta MySQL em JSON. Por exemplo,
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 Erros
Por padrão, o módulo subjacente ngx_lua faz o registro de erros quando ocorrem erros de socket. Se você já estiver fazendo o tratamento de erros adequados em seu próprio código Lua, então é recomendado desativar este registro automático de erros desligando a diretiva lua_socket_log_errors do ngx_lua, ou seja,
lua_socket_log_errors off;
Limitações
- Esta biblioteca não pode ser usada em contextos de código como init_by_lua, set_by_lua, log_by_lua, e header_filter_by_lua onde a API de cosocket do ngx_lua não está disponível.
- A instância do objeto
resty.mysqlnão pode ser armazenada em uma variável Lua no nível do módulo Lua, porque então será compartilhada por todas as requisições concorrentes tratadas pelo mesmo processo de trabalhador do nginx (veja https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker) e resultará em más condições de corrida quando requisições concorrentes tentarem usar a mesma instância deresty.mysql. Você deve sempre iniciar objetosresty.mysqlem variáveis locais de função ou na tabelangx.ctx. Esses locais têm suas próprias cópias de dados para cada requisição.
Mais Suporte a Métodos de Autenticação
Por padrão, de todos os métodos de autenticação, apenas Old Password Authentication(mysql_old_password) e Secure Password Authentication(mysql_native_password) são suportados. Se o servidor exigir sha256_password ou cache_sha2_password, um erro como auth plugin caching_sha2_password or sha256_password are not supported because resty.rsa is not installed pode ser retornado.
Necessita de lua-resty-rsa ao usar sha256_password e cache_sha2_password.
Veja Também
- o módulo ngx_lua: https://github.com/openresty/lua-nginx-module
- a especificação do protocolo de rede MySQL: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol
- a biblioteca lua-resty-memcached
- a biblioteca lua-resty-redis
- o módulo ngx_drizzle: https://github.com/openresty/drizzle-nginx-module
GitHub
Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub para nginx-module-mysql.