---
title: "Vorteile und Nachteile von `string` in OpenResty - API7.ai"
description: "Dieser Artikel stellt die Optimierungstechniken vor, die in OpenResty befolgt werden sollten: Anfragen kurz, einfach und schnell verarbeiten und die Erzeugung von Zwischendaten vermeiden. Außerdem werden die Optimierungstechniken für Strings gezeigt."
url: "https://api7.ai/de/learning-center/openresty/advantages-and-disadvantages-of-string"
---

# Vorteile und Nachteile von \`string\` in OpenResty

API7.ai

December 8, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

Im letzten Artikel haben wir uns mit den gängigen Blockierungsfunktionen in OpenResty vertraut gemacht, die oft von Anfängern falsch verwendet werden. Ab diesem Artikel werden wir uns mit dem Kern der Leistungsoptimierung befassen, was viele Optimierungstechniken beinhaltet, die uns helfen können, die Leistung von OpenResty-Code schnell zu verbessern. Daher sollten wir dies nicht auf die leichte Schulter nehmen.

In diesem Prozess müssen wir mehr Testcode schreiben, um zu erfahren, wie man diese Optimierungstechniken verwendet und ihre Wirksamkeit überprüft, damit wir sie gut nutzen können.

## Hinter den Kulissen der Leistungsoptimierungstipps

Optimierungstechniken gehören alle zum "praktischen" Teil, daher wollen wir vorher über die "Theorie" der Optimierung sprechen.

Leistungsoptimierungsmethoden werden sich mit den Iterationen von LuaJIT und OpenResty ändern. Einige Methoden könnten direkt durch die zugrunde liegende Technologie optimiert werden und müssen nicht mehr beherrscht werden; gleichzeitig wird es einige neue Optimierungstechniken geben. Daher ist es am wichtigsten, das konstante Konzept hinter diesen Optimierungstechniken zu beherrschen.

Schauen wir uns einige der kritischen Ideen zur Leistung in der OpenResty-Programmierung an.

### Theorie 1: Die Verarbeitung von Anfragen sollte kurz, einfach und schnell sein

OpenResty ist ein Webserver, daher verarbeitet er oft 1.000+, 10.000+ oder sogar 100.000+ Client-Anfragen gleichzeitig. Um die höchste Gesamtleistung zu erreichen, müssen wir sicherstellen, dass einzelne Anfragen schnell verarbeitet werden und verschiedene Ressourcen, wie z.B. Speicher, zurückgewonnen werden.

- Das hier erwähnte "kurz" bedeutet, dass der Lebenszyklus der Anfrage kurz sein sollte, um Ressourcen nicht lange zu blockieren, ohne sie freizugeben; selbst bei langen Verbindungen sollte ein Schwellenwert für die Zeit oder die Anzahl der Anfragen festgelegt werden, um Ressourcen regelmäßig freizugeben.
- Das zweite "einfach" bezieht sich darauf, nur eine Sache in einer API zu tun. Komplexe Geschäftslogik sollte in mehrere APIs aufgeteilt werden, und der Code sollte einfach gehalten werden.
- Schließlich bedeutet "schnell", dass der Hauptthread nicht blockiert werden sollte und nicht zu viele CPU-Operationen ausgeführt werden sollten. Selbst wenn dies notwendig ist, sollten andere Methoden, die wir im letzten Artikel vorgestellt haben, nicht vergessen werden.

Diese architektonische Überlegung ist nicht nur für OpenResty geeignet, sondern auch für weitere Entwicklungssprachen und Plattformen, daher hoffe ich, dass Sie dies sorgfältig verstehen und darüber nachdenken können.

### Theorie 2: Vermeiden Sie die Erzeugung von Zwischendaten

Die Vermeidung von nutzlosen Daten im Zwischenprozess ist wohl die dominierende Optimierungstheorie in der OpenResty-Programmierung. Schauen wir uns ein kleines Beispiel an, um nutzlose Daten im Zwischenprozess zu erklären.

```
$ resty -e 'local s= "hello"
s = s .. " world"
s = s .. "!"
print(s)
'
```

In diesem Code-Snippet haben wir mehrere Verkettungsoperationen auf der Variablen `s` durchgeführt, um das Ergebnis `hello world!` zu erhalten. Aber nur der endgültige Zustand `hello world!` von `s` ist nützlich. Der Anfangswert von `s` und die Zwischenzuweisungen sind alle Zwischendaten, die so wenig wie möglich erzeugt werden sollten.

Der Grund dafür ist, dass diese temporären Daten Initialisierungs- und GC-Leistungsverluste mit sich bringen. Unterschätzen Sie diese Verluste nicht; wenn dies in heißem Code wie Schleifen auftritt, wird die Leistung offensichtlich beeinträchtigt. Ich werde dies später auch mit einem String-Beispiel erklären.

## `string`s sind unveränderlich

Nun zurück zum Thema dieses Artikels, `string`. Hier möchte ich betonen, dass `string`s in Lua unveränderlich sind.

Das bedeutet natürlich nicht, dass `string`s nicht verkettet, modifiziert usw. werden können, aber wenn wir einen `string` ändern, ändern wir nicht den ursprünglichen `string`, sondern erstellen ein neues `string`-Objekt und ändern die Referenz auf den `string`. Wenn der ursprüngliche `string` keine anderen Referenzen hat, wird er natürlich von Lua's GC (Garbage Collection) zurückgewonnen.

Der offensichtliche Vorteil von unveränderlichen `string`s ist, dass sie Speicher sparen. Auf diese Weise gibt es nur eine Kopie des gleichen `string`s im Speicher, und verschiedene Variablen zeigen auf die gleiche Speicheradresse.

Der Nachteil dieses Designs ist, dass beim Hinzufügen und Zurückgewinnen von `string`s jedes Mal, wenn Sie einen `string` hinzufügen, LuaJIT `lj_str_new` aufrufen muss, um zu prüfen, ob der `string` bereits existiert; wenn nicht, muss ein neuer `string` erstellt werden. Wenn Sie dies sehr oft tun, wird dies die Leistung erheblich beeinträchtigen.

Schauen wir uns ein konkretes Beispiel für eine `string`-Verkettungsoperation wie in diesem Beispiel an, das in vielen OpenResty-Open-Source-Projekten zu finden ist.

```
$ resty -e 'local begin = ngx.now()
local s = ""
-- `for`-Schleife, die `..` verwendet, um String-Verkettungen durchzuführen
for i = 1, 100000 do
    s = s .. "a"
end
ngx.update_time()
print(ngx.now() - begin)
'
```

Was dieser Beispielcode tut, ist 100.000 `string`-Verkettungen auf der Variablen `s` durchzuführen und die Laufzeit auszugeben. Obwohl das Beispiel etwas extrem ist, gibt es einen guten Eindruck von der Differenz vor und nach der Leistungsoptimierung. Ohne Optimierung läuft dieser Code auf meinem Laptop 0,4 Sekunden, was immer noch relativ langsam ist. Wie sollten wir ihn also optimieren?

In den vorherigen Artikeln wurde die Antwort gegeben, nämlich `table` zu verwenden, um eine Ebene der Kapselung durchzuführen, alle temporären Zwischen-`string`s zu entfernen und nur die ursprünglichen Daten und das Endergebnis zu behalten. Schauen wir uns die konkrete Code-Implementierung an.

```
$ resty -e 'local begin = ngx.now()
local t = {}
-- for-Schleife, die ein Array verwendet, um den String zu halten, und jedes Mal die Länge des Arrays zählt
for i = 1, 100000 do
    t[#t + 1] = "a"
end
-- Verkettung der Strings mit der concat-Methode des Arrays
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Wir können sehen, dass dieser Code jeden String nacheinander mit `table` speichert, und der Index wird durch `#t + 1` bestimmt, also die aktuelle Länge von `table` plus `1`. Schließlich wird die `table.concat`-Funktion verwendet, um jedes Array-Element zu verketten. Dies überspringt natürlich alle temporären Strings und vermeidet 100.000 Mal `lj_str_new` und GC.

Das war unsere Code-Analyse, aber wie funktioniert die Optimierung? Der optimierte Code dauert nur 0,007 Sekunden, was eine Leistungssteigerung von mehr als 50 Mal bedeutet. In einem tatsächlichen Projekt könnte die Leistungssteigerung noch deutlicher sein, da wir in diesem Beispiel nur ein Zeichen `a` hinzugefügt haben.

Wie wäre die Leistungsdifferenz, wenn der neue `string` die Länge von 10x `a` hätte?

Sind die 0,007 Sekunden des Codes gut genug für unsere Optimierungsarbeit? Nein, es kann noch optimiert werden. Lassen Sie uns noch eine Codezeile ändern und das Ergebnis sehen.

```
$ resty -e 'local begin = ngx.now()
local t = {}
-- for-Schleife, die ein Array verwendet, um den String zu halten, und die Länge des Arrays selbst verwaltet
for i = 1, 100000 do
    t[i] = "a"
end
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Dieses Mal haben wir `t[#t + 1] = "a"` in `t[i] = "a"` geändert, und mit nur einer Codezeile können wir 100.000 Funktionsaufrufe vermeiden, um die Länge des Arrays zu erhalten. Erinnern Sie sich an die Operation, um die Länge eines Arrays zu erhalten, die wir im `table`-Abschnitt früher erwähnt haben? Sie hat eine Zeitkomplexität von `O(n)`, eine relativ teure Operation. Hier umgehen wir einfach die Operation, die Länge des Arrays zu erhalten, indem wir unseren Array-Index selbst verwalten. Wie das Sprichwort sagt: Wenn Sie es sich nicht leisten können, es zu vermasseln, können Sie es vermeiden.

Natürlich ist dies eine einfachere Schreibweise. Der folgende Code zeigt deutlicher, wie wir den Index eines Arrays selbst verwalten können.

```
$ resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
local s = table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

## Reduzieren Sie andere temporäre `string`s

Die Fehler, über die wir gerade gesprochen haben, temporäre `string`s, die durch `string`-Verkettung verursacht werden, sind offensichtlich. Mit ein paar Erinnerungen an den obigen Beispielcode glaube ich, dass wir keine ähnlichen Fehler mehr machen werden. Es gibt jedoch einige verstecktere temporäre `string`s, die in OpenResty erzeugt werden und viel schwerer zu erkennen sind. Zum Beispiel die `string`-Behandlungsfunktion, die wir unten diskutieren werden, wird oft verwendet. Können Sie sich vorstellen, dass sie auch temporäre `string`s erzeugt?

Wie wir wissen, schneidet die `string.sub`-Funktion einen bestimmten Teil eines `string`s ab. Wie wir bereits erwähnt haben, sind `string`s in Lua unveränderlich, daher beinhaltet das Abschneiden eines neuen Strings `lj_str_new` und nachfolgende GC-Operationen.

```
resty -e 'print(string.sub("abcd", 1, 1))'
```

Die Funktion des obigen Codes besteht darin, das erste Zeichen des `string`s zu extrahieren und auszugeben. Natürlich wird dies unweigerlich einen temporären `string` erzeugen. Gibt es eine bessere Möglichkeit, den gleichen Effekt zu erzielen?

```
resty -e 'print(string.char(string.byte("abcd")))'
```

Natürlich. Schauen wir uns diesen Code an, wir verwenden zuerst `string.byte`, um den numerischen Code des ersten Zeichens zu erhalten, und dann `string.char`, um die Zahl in das entsprechende Zeichen umzuwandeln. Dieser Prozess erzeugt keine temporären `string`s. Daher ist es am effizientesten, `string.byte` zu verwenden, um `string`-bezogene Scans und Analysen durchzuführen.

## Nutzen Sie die SDK-Unterstützung für den `table`-Typ

Nachdem Sie gelernt haben, wie man temporäre `string`s reduziert, sind Sie begierig darauf, es auszuprobieren? Dann können wir das Ergebnis des obigen Beispielcodes nehmen und es als Inhalt des Antwortkörpers an den Client ausgeben. An dieser Stelle können Sie pausieren und versuchen, diesen Code selbst zu schreiben.

```
$ resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
local response = table.concat(t, "")
ngx.say(response)
'
```

Wenn Sie diesen Code schreiben können, sind Sie bereits den meisten OpenResty-Entwicklern voraus. OpenResty's Lua API berücksichtigt bereits die Verwendung von `table`s für `string`-Verkettungen, daher akzeptiert `ngx.say`, `ngx.print`, `ngx.log`, `cosocket:send` und andere APIs, die viele `string`s verarbeiten können, nicht nur `string` als Parameter, sondern auch `table` als Parameter.

```
resty -e 'local begin = ngx.now()
local t = {}
local index = 1
for i = 1, 100000 do
    t[index] = "a"
    index = index + 1
end
ngx.say(t)
'
```

In diesem letzten Code-Snippet haben wir `local response = table.concat(t, "")`, den `string`-Verkettungsschritt, weggelassen und das `table` direkt an `ngx.say` übergeben. Dies verschiebt die `string`-Verkettungsaufgabe von der Lua-Ebene auf die C-Ebene und vermeidet eine weitere `string`-Suche, -Erzeugung und -GC. Für lange `string`s ist dies ein weiterer erheblicher Leistungsgewinn.

## Zusammenfassung

Nachdem wir diesen Artikel gelesen haben, können wir sehen, dass sich viel von OpenResty's Leistungsoptimierung mit verschiedenen Details befasst. Daher müssen wir LuaJIT und OpenResty's Lua API gut kennen, um optimale Leistung zu erzielen. Dies erinnert uns auch daran, dass wir, wenn wir den vorherigen Inhalt vergessen haben, ihn rechtzeitig überprüfen und festigen müssen.

Zum Schluss denken Sie über ein Problem nach: Schreiben Sie die Strings `hello`, `world` und `!` in das Fehlerprotokoll. Können wir einen Beispielcode ohne `string`-Verkettung schreiben?

Vergessen Sie auch nicht die andere Frage im Text. Wie wäre die Leistungsdifferenz im folgenden Code, wenn die neuen `string`s die Länge von 10x `a` hätten?

```
$ resty -e 'local begin = ngx.now()
local t = {}
for i = 1, 100000 do
    t[#t + 1] = "a"
end
local s =  table.concat(t, "")
ngx.update_time()
print(ngx.now() - begin)
'
```

Sie können diesen Artikel auch gerne mit Ihren Freunden teilen, um zu lernen und zu kommunizieren.

Share article link

Share

Related Posts

[![Non-blocking I/O – Der Schlüssel zur Verbesserung der OpenResty-Leistung](https://static.api7.ai/2022/12/05/638dce5075e0f.png)](https://api7.ai/learning-center/openresty/blocking-io)

## [Non-blocking I/O – Der Schlüssel zur Verbesserung der OpenResty-Leistung](https://api7.ai/learning-center/openresty/blocking-io)

[![OpenResty FAQ | Wie OpenResty in der Praxis eingesetzt wird](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | Wie OpenResty in der Praxis eingesetzt wird](https://api7.ai/learning-center/openresty/faq-5)

Table of Contents

- [Hinter den Kulissen der Leistungsoptimierungstipps](#hinter-den-kulissen-der-leistungsoptimierungstipps)

  - [Theorie 1: Die Verarbeitung von Anfragen sollte kurz, einfach und schnell sein](#theorie-1-die-verarbeitung-von-anfragen-sollte-kurz-einfach-und-schnell-sein)
  - [Theorie 2: Vermeiden Sie die Erzeugung von Zwischendaten](#theorie-2-vermeiden-sie-die-erzeugung-von-zwischendaten)

- [`string`s sind unveränderlich](#strings-sind-unver%C3%A4nderlich)

- [Reduzieren Sie andere temporäre `string`s](#reduzieren-sie-andere-tempor%C3%A4re-strings)

- [Nutzen Sie die SDK-Unterstützung für den `table`-Typ](#nutzen-sie-die-sdk-unterst%C3%BCtzung-f%C3%BCr-den-table-typ)

- [Zusammenfassung](#zusammenfassung)
