Zum Inhalt

mysql: Nonblocking Lua MySQL-Treiberbibliothek für nginx-module-lua

Installation

Wenn Sie das RPM-Repository-Abonnement noch nicht eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder 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

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-mysql v0.29, veröffentlicht am 14. Januar 2026.


Diese Lua-Bibliothek ist ein MySQL-Clienttreiber für das ngx_lua NGINX-Modul:

https://github.com/openresty/lua-nginx-module

Diese Lua-Bibliothek nutzt die cosocket-API von ngx_lua, die ein 100% nicht-blockierendes Verhalten gewährleistet.

Beachten Sie, dass mindestens ngx_lua 0.9.11 oder ngx_openresty 1.7.4.1 erforderlich ist.

Außerdem wird die bit-Bibliothek benötigt. Wenn Sie LuaJIT 2 mit ngx_lua verwenden, ist die bit-Bibliothek standardmäßig bereits verfügbar.

Synopsis

    # Sie benötigen die folgende Zeile nicht, wenn Sie das
    # ngx_openresty-Bundle verwenden:
    server {
        location /test {
            content_by_lua '
                local mysql = require "resty.mysql"
                local db, err = mysql:new()
                if not db then
                    ngx.say("Fehler beim Instanziieren von mysql: ", err)
                    return
                end

                db:set_timeout(1000) -- 1 Sekunde

                -- oder verbinden Sie sich mit einer Unix-Domain-Socket-Datei,
                -- die von einem MySQL-Server überwacht wird:
                --     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("Fehler beim Verbinden: ", err, ": ", errcode, " ", sqlstate)
                    db:close()
                    return
                end

                ngx.say("Mit MySQL verbunden.")

                local res, err, errcode, sqlstate =
                    db:query("drop table if exists cats")
                if not res then
                    ngx.say("schlechtes Ergebnis: ", 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("schlechtes Ergebnis: ", err, ": ", errcode, ": ", sqlstate, ".")
                    db:close()
                    return
                end

                ngx.say("Tabelle cats erstellt.")

                res, err, errcode, sqlstate =
                    db:query("insert into cats (name) "
                             .. "values (\'Bob\'),(\'\'),(null)")
                if not res then
                    ngx.say("schlechtes Ergebnis: ", err, ": ", errcode, ": ", sqlstate, ".")
                    db:close()
                    return
                end

                ngx.say(res.affected_rows, " Zeilen in die Tabelle cats eingefügt ",
                        "(letzte eingefügte ID: ", res.insert_id, ")")

                -- Führen Sie eine SELECT-Abfrage aus, erwarten Sie etwa 10 Zeilen im
                -- Ergebnissatz:
                res, err, errcode, sqlstate =
                    db:query("select * from cats order by id asc", 10)
                if not res then
                    ngx.say("schlechtes Ergebnis: ", err, ": ", errcode, ": ", sqlstate, ".")
                    db:close()
                    return
                end

                local cjson = require "cjson"
                ngx.say("Ergebnis: ", cjson.encode(res))

                -- Fügen Sie es in den Verbindungspool mit einer Größe von 100 ein,
                -- mit 10 Sekunden maximaler Leerlaufzeit
                local ok, err = db:set_keepalive(10000, 100)
                if not ok then
                    ngx.say("Fehler beim Setzen von Keepalive: ", err)
                    db:close()
                    return
                end

                -- oder schließen Sie die Verbindung sofort:
                -- local ok, err = db:close()
                -- if not ok then
                --     ngx.say("Fehler beim Schließen: ", err)
                --     return
                -- end
            ';
        }
    }

Methoden

new

syntax: db, err = mysql:new()

Erstellt ein MySQL-Verbindungsobjekt. Im Falle von Fehlern gibt es nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

connect

syntax: ok, err, errcode, sqlstate = db:connect(options)

Versucht, eine Verbindung zum entfernten MySQL-Server herzustellen.

Das Argument options ist eine Lua-Tabelle, die die folgenden Schlüssel enthält:

  • host

    der Hostname für den MySQL-Server. * port

    der Port, auf dem der MySQL-Server lauscht. Standardmäßig 3306. * path

    der Pfad der Unix-Socket-Datei, die vom MySQL-Server überwacht wird. * database

    der Name der MySQL-Datenbank. * user

    MySQL-Kontoname für die Anmeldung. * password

    MySQL-Kontopasswort für die Anmeldung (im Klartext). * charset

    der Zeichensatz, der für die MySQL-Verbindung verwendet wird, der von der Standardzeichensatz-Einstellung abweichen kann. Die folgenden Werte werden akzeptiert: 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_size

    die obere Grenze für die Antwortpakete, die vom MySQL-Server gesendet werden (standardmäßig 1 MB). * ssl

    Wenn auf true gesetzt, wird SSL verwendet, um eine Verbindung zu MySQL herzustellen (standardmäßig false). Wenn der MySQL-Server keine SSL-Unterstützung hat (oder einfach deaktiviert ist), wird die Fehlermeldung "ssl disabled on server" zurückgegeben. * ssl_verify

    Wenn auf true gesetzt, wird die Gültigkeit des Server-SSL-Zertifikats überprüft (standardmäßig false). Beachten Sie, dass Sie die lua_ssl_trusted_certificate konfigurieren müssen, um das CA- (oder Server-)Zertifikat zu spezifizieren, das von Ihrem MySQL-Server verwendet wird. Möglicherweise müssen Sie auch lua_ssl_verify_depth entsprechend konfigurieren. * pool

    der Name für den MySQL-Verbindungspool. Wenn weggelassen, wird ein mehrdeutiger Poolname automatisch mit der Zeichenfolgenvorlage user:database:host:port oder user:database:path generiert. (Diese Option wurde erstmals in v0.08 eingeführt.)

  • pool_size

    Gibt die Größe des Verbindungspools an. Wenn weggelassen und keine backlog-Option angegeben wurde, wird kein Pool erstellt. Wenn weggelassen, aber backlog angegeben wurde, wird der Pool mit einer Standardgröße erstellt, die dem Wert der lua_socket_pool_size Direktive entspricht. Der Verbindungspool hält bis zu pool_size aktive Verbindungen, die bereit sind, von nachfolgenden Aufrufen von connect wiederverwendet zu werden, aber beachten Sie, dass es keine obere Grenze für die Gesamtzahl der außerhalb des Pools geöffneten Verbindungen gibt. Wenn Sie die Gesamtzahl der geöffneten Verbindungen einschränken müssen, geben Sie die backlog-Option an. Wenn der Verbindungspool seine Größenbeschränkung überschreiten würde, wird die am wenigsten kürzlich verwendete (gehaltene) Verbindung, die sich bereits im Pool befindet, geschlossen, um Platz für die aktuelle Verbindung zu schaffen. Beachten Sie, dass der cosocket-Verbindungspool pro Nginx-Arbeitsprozess und nicht pro Nginx-Serverinstanz gilt, sodass die hier angegebene Größenbeschränkung auch für jeden einzelnen Nginx-Arbeitsprozess gilt. Beachten Sie auch, dass die Größe des Verbindungspools nicht geändert werden kann, sobald er erstellt wurde. Beachten Sie, dass mindestens ngx_lua 0.10.14 erforderlich ist, um diese Optionen zu verwenden.

  • backlog

    Wenn angegeben, wird dieses Modul die Gesamtzahl der geöffneten Verbindungen für diesen Pool begrenzen. Zu keinem Zeitpunkt können mehr Verbindungen als pool_size für diesen Pool geöffnet werden. Wenn der Verbindungspool voll ist, werden nachfolgende Verbindungsoperationen in eine Warteschlange gestellt, die dem Wert dieser Option entspricht (die "Backlog"-Warteschlange). Wenn die Anzahl der in der Warteschlange befindlichen Verbindungsoperationen gleich backlog ist, schlagen nachfolgende Verbindungsoperationen fehl und geben nil plus die Fehlermeldung "too many waiting connect operations" zurück. Die in der Warteschlange befindlichen Verbindungsoperationen werden fortgesetzt, sobald die Anzahl der Verbindungen im Pool kleiner ist als pool_size. Die in der Warteschlange befindliche Verbindungsoperation wird abgebrochen, sobald sie länger als connect_timeout in der Warteschlange steht, gesteuert durch set_timeout, und gibt nil plus die Fehlermeldung "timeout" zurück. Beachten Sie, dass mindestens ngx_lua 0.10.14 erforderlich ist, um diese Optionen zu verwenden.

  • compact_arrays

    Wenn diese Option auf true gesetzt ist, geben die query und read_result Methoden die Struktur array-of-arrays für den Ergebnissatz zurück, anstelle der Standardstruktur array-of-hashes.

Bevor der Hostname tatsächlich aufgelöst und eine Verbindung zum entfernten Backend hergestellt wird, wird diese Methode immer den Verbindungspool nach passenden inaktiven Verbindungen durchsuchen, die durch vorherige Aufrufe dieser Methode erstellt wurden.

set_timeout

syntax: db:set_timeout(time)

Setzt den Timeout (in ms) Schutz für nachfolgende Operationen, einschließlich der connect-Methode.

set_keepalive

syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size)

Fügt die aktuelle MySQL-Verbindung sofort in den ngx_lua cosocket-Verbindungspool ein.

Sie können die maximale Leerlaufzeit (in ms) angeben, wenn die Verbindung im Pool ist, und die maximale Größe des Pools für jeden Nginx-Arbeitsprozess.

Im Erfolgsfall gibt es 1 zurück. Im Falle von Fehlern gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

Rufen Sie diese Methode nur an der Stelle auf, an der Sie stattdessen die close-Methode aufgerufen hätten. Das Aufrufen dieser Methode versetzt das aktuelle resty.mysql-Objekt sofort in den Zustand closed. Alle nachfolgenden Operationen, die nicht connect() auf dem aktuellen Objekt sind, geben den closed-Fehler zurück.

get_reused_times

syntax: times, err = db:get_reused_times()

Diese Methode gibt die (erfolgreich) wiederverwendeten Zeiten für die aktuelle Verbindung zurück. Im Falle eines Fehlers gibt sie nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Wenn die aktuelle Verbindung nicht aus dem integrierten Verbindungspool stammt, gibt diese Methode immer 0 zurück, das heißt, die Verbindung wurde (noch) nie wiederverwendet. Wenn die Verbindung aus dem Verbindungspool stammt, ist der Rückgabewert immer ungleich null. Diese Methode kann also auch verwendet werden, um festzustellen, ob die aktuelle Verbindung aus dem Pool stammt.

close

syntax: ok, err = db:close()

Schließt die aktuelle MySQL-Verbindung und gibt den Status zurück.

Im Erfolgsfall gibt es 1 zurück. Im Falle von Fehlern gibt es nil mit einer Zeichenfolge zurück, die den Fehler beschreibt.

send_query

syntax: bytes, err = db:send_query(query)

Sendet die Abfrage an den entfernten MySQL-Server, ohne auf dessen Antworten zu warten.

Gibt die Bytes zurück, die erfolgreich gesendet wurden, und gibt andernfalls nil und eine Zeichenfolge zurück, die den Fehler beschreibt.

Sie sollten die read_result Methode verwenden, um die MySQL-Antworten danach zu lesen.

read_result

syntax: res, err, errcode, sqlstate = db:read_result()

syntax: res, err, errcode, sqlstate = db:read_result(nrows)

Liest ein Ergebnis, das vom MySQL-Server zurückgegeben wird.

Es gibt eine Lua-Tabelle (res) zurück, die das MySQL OK-Paket oder Ergebnismengen-Paket für das Abfrageergebnis beschreibt.

Für Abfragen, die einem Ergebnisset entsprechen, gibt es ein Array zurück, das alle Zeilen enthält. Jede Zeile enthält Schlüssel-Wert-Paare für jedes Datenfeld. Zum Beispiel,

    {
        { name = "Bob", age = 32, phone = ngx.null },
        { name = "Marry", age = 18, phone = "10666372"}
    }

Für Abfragen, die nicht einem Ergebnisset entsprechen, gibt es eine Lua-Tabelle wie diese zurück:

    {
        insert_id = 0,
        server_status = 2,
        warning_count = 1,
        affected_rows = 32,
        message = nil
    }

Wenn weitere Ergebnisse dem aktuellen Ergebnis folgen, wird ein zweiter err Rückgabewert mit der Zeichenfolge again zurückgegeben. Man sollte immer diesen (zweiten) Rückgabewert überprüfen und, wenn er again ist, sollte man diese Methode erneut aufrufen, um weitere Ergebnisse abzurufen. Dies geschieht normalerweise, wenn die ursprüngliche Abfrage mehrere Anweisungen enthält (die durch Semikolon in derselben Abfragezeichenfolge getrennt sind) oder eine MySQL-Prozedur aufgerufen wird. Siehe auch Multi-Resultset Support.

Im Falle von Fehlern gibt diese Methode maximal 4 Werte zurück: nil, err, errcode und sqlstate. Der Rückgabewert err enthält eine Zeichenfolge, die den Fehler beschreibt, der Rückgabewert errcode enthält den MySQL-Fehlercode (einen numerischen Wert), und schließlich enthält der Rückgabewert sqlstate den standardmäßigen SQL-Fehlercode, der aus 5 Zeichen besteht. Beachten Sie, dass errcode und sqlstate nil sein können, wenn MySQL sie nicht zurückgibt.

Das optionale Argument nrows kann verwendet werden, um eine ungefähre Anzahl von Zeilen für den Ergebnisset anzugeben. Dieser Wert kann verwendet werden, um im resultierenden Lua-Array für den Ergebnisset Platz vorzubelegen. Standardmäßig hat es den Wert 4.

query

syntax: res, err, errcode, sqlstate = db:query(query)

syntax: res, err, errcode, sqlstate = db:query(query, nrows)

Dies ist eine Abkürzung zum Kombinieren des send_query Aufrufs und des ersten read_result Aufrufs.

Sie sollten immer überprüfen, ob der Rückgabewert err im Erfolgsfall again ist, da diese Methode nur einmal read_result für Sie aufruft. Siehe auch Multi-Resultset Support.

server_ver

syntax: str = db:server_ver()

Gibt die MySQL-Serverversionszeichenfolge zurück, wie "5.1.64".

Sie sollten diese Methode nur nach erfolgreichem Verbinden mit einem MySQL-Server aufrufen, andernfalls wird nil zurückgegeben.

set_compact_arrays

syntax: db:set_compact_arrays(boolean)

Legt fest, ob die "compact-arrays"-Struktur für die von nachfolgenden Abfragen zurückgegebenen Ergebnisset verwendet werden soll. Siehe die compact_arrays-Option für die connect-Methode für weitere Details.

Diese Methode wurde erstmals in der v0.09-Version eingeführt.

SQL Literal Quoting

Es ist immer wichtig, SQL-Literale ordnungsgemäß zu zitieren, um SQL-Injection-Angriffe zu verhindern. Sie können die ngx.quote_sql_str Funktion verwenden, die von ngx_lua bereitgestellt wird, um Werte zu zitieren. Hier ist ein Beispiel:

    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

Multi-Resultset Support

Für eine SQL-Abfrage, die mehrere Ergebnismengen erzeugt, liegt es immer in Ihrer Verantwortung, die "again"-Fehlermeldung zu überprüfen, die von den query oder read_result Methodenaufrufen zurückgegeben wird, und weitere Ergebnismengen abzurufen, indem Sie die read_result Methode aufrufen, bis keine "again"-Fehlermeldung mehr zurückgegeben wird (oder andere Fehler auftreten).

Unten ist ein triviales Beispiel dafür:

    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, "Fehler beim Verbinden: ", 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, "schlechtes Ergebnis #1: ", err, ": ", errcode, ": ", sqlstate, ".")
        db:close()
        return ngx.exit(500)
    end

    ngx.say("Ergebnis #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, "schlechtes Ergebnis #", i, ": ", err, ": ", errcode, ": ", sqlstate, ".")
            db:close()
            return ngx.exit(500)
        end

        ngx.say("Ergebnis #", i, ": ", cjson.encode(res))
        i = i + 1
    end

    local ok, err = db:set_keepalive(10000, 50)
    if not ok then
        ngx.log(ngx.ERR, "Fehler beim Setzen von Keepalive: ", err)
        db:close()
        ngx.exit(500)
    end

Dieser Code-Schnipsel wird die folgenden Antwortkörperdaten erzeugen:

Ergebnis #1: [{"1":"1"}]
Ergebnis #2: [{"2":"2"}]
Ergebnis #3: [{"3":"3"}]

Debugging

Es ist normalerweise praktisch, die lua-cjson Bibliothek zu verwenden, um die Rückgabewerte der MySQL-Abfragemethoden in JSON zu kodieren. Zum Beispiel,

    local cjson = require "cjson"
    ...
    local res, err, errcode, sqlstate = db:query("select * from cats")
    if res then
        print("res: ", cjson.encode(res))
    end

Automatische Fehlerprotokollierung

Standardmäßig protokolliert das zugrunde liegende ngx_lua Modul Fehler, wenn Socket-Fehler auftreten. Wenn Sie bereits eine ordnungsgemäße Fehlerbehandlung in Ihrem eigenen Lua-Code durchführen, wird empfohlen, diese automatische Fehlerprotokollierung zu deaktivieren, indem Sie die lua_socket_log_errors Direktive von ngx_lua deaktivieren, das heißt,

    lua_socket_log_errors off;

Einschränkungen

  • Diese Bibliothek kann nicht in Codekontexten wie init_by_lua, set_by_lua, log_by_lua, und header_filter_by_lua verwendet werden, wo die ngx_lua cosocket-API nicht verfügbar ist.
  • Die resty.mysql-Objektinstanz kann nicht auf Modulebene in einer Lua-Variablen gespeichert werden, da sie dann von allen gleichzeitigen Anfragen, die vom selben Nginx-Arbeitsprozess bearbeitet werden, geteilt wird (siehe https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker) und schlechte Race-Conditions verursachen kann, wenn gleichzeitige Anfragen versuchen, dieselbe resty.mysql-Instanz zu verwenden. Sie sollten immer resty.mysql-Objekte in lokalen Funktionsvariablen oder in der ngx.ctx-Tabelle initiieren. Diese Orte haben alle ihre eigenen Datenkopien für jede Anfrage.

Mehr Unterstützung für Authentifizierungsmethoden

Standardmäßig werden von allen Authentifizierungsmethoden nur Old Password Authentication(mysql_old_password) und Secure Password Authentication(mysql_native_password) unterstützt. Wenn der Server sha256_password oder cache_sha2_password benötigt, kann ein Fehler wie auth plugin caching_sha2_password or sha256_password are not supported because resty.rsa is not installed zurückgegeben werden.

Benötigt lua-resty-rsa, wenn sha256_password und cache_sha2_password verwendet werden.

Siehe auch

GitHub

Sie finden zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-mysql.