Optimieren Sie die Object-Storage-Leistung mit dem AWS Go SDK
Zuletzt aktualisiert am
Leistung verstehen: Arbeiter vs. Parallelität
Abschnitt betitelt „Leistung verstehen: Arbeiter vs. Parallelität“Um einen hohen Durchsatz bei der Interaktion mit Object Storage zu erreichen, ist es wichtig zu verstehen, wie das AWS SDK for Go die Datenübertragung handhabt. Die Leistungsoptimierung umfasst zwei Hauptdimensionen:
- Arbeiter (horizontale Parallelität): Dies bezieht sich auf die Anzahl der separaten Dateien, die gleichzeitig hochgeladen werden. Die Erhöhung der Worker ist die effektivste Möglichkeit, die Leistung für kleine Dateien zu verbessern, bei denen der Overhead des HTTP-Handshakes den Hauptengpass darstellt.
- Parallelität (vertikale Parallelität): Dies wird vom S3 Transfer Manager verwaltet. Es bestimmt, wie viele Teile einer einzelnen großen Datei parallel mit Multipart Upload hochgeladen werden. Dies ist entscheidend für die Auslastung der Bandbreite mit großen Dateien.
Konfigurationsrichtlinien
Abschnitt betitelt „Konfigurationsrichtlinien“Basierend auf unseren Tests finden Sie hier beispielhafte Ausgangspunkte für verschiedene Arbeitslasten. Diese Parameter sollten basierend auf den Ressourcen Ihrer Umgebung angepasst werden.
| Arbeitslasttyp | Dateigröße | Empfohlene Arbeiter | Parallelität (pro Datei) | Teilegröße |
|---|---|---|---|---|
| Kleine Dateien | ~1 MB | 400 | 1 | N / A |
| Mittlere Dateien | 10 - 100 MB | 40 - 200 | 4 - 8 | 5 MB |
| Große Dateien | > 1000 MB | 8 - 16 | 32 - 64 | 64 MB |
So ermitteln Sie Worker- und Parallelitätswerte
Abschnitt betitelt „So ermitteln Sie Worker- und Parallelitätswerte“Es gibt keine einheitliche Zauberformel für die optimale Anzahl an Workern und Parallelität, da diese vollständig von Ihrer Umgebung abhängt. Der Schlüssel liegt im Messen, Abstimmen und Wiederholen.
Leitprinzipien
Abschnitt betitelt „Leitprinzipien“- Beginnen Sie mit
workersfür die Parallelisierung auf Dateiebene.
- Ziel: Halten Sie die CPU und das Netzwerk beschäftigt, indem Sie mehrere Dateien gleichzeitig verarbeiten. Dies ist am effektivsten für Workloads mit vielen kleinen bis mittelgroßen Dateien.
- Ausgangspunkt: Ein guter Startwert ist das
2- bis4-Fache der Anzahl CPU-Kerne Ihres Systems. Bei einem 8-Core-System starten Sie mit16bis32Workern.
- Ausgangspunkt: Ein guter Startwert ist das
- Begrenzende Faktoren:
- CPU: Zu viele Worker können zu übermäßigem Kontextwechsel führen, bei dem die CPU mehr Zeit damit verbringt, zwischen Aufgaben zu wechseln, als tatsächliche Arbeit zu erledigen.
- Speicher: Jeder Worker und die zugehörigen Upload-Aufgaben verbrauchen Speicher.
- Datei-Handles: Ihr Betriebssystem hat eine Begrenzung der Anzahl geöffneter Dateien.
- Optimieren Sie
concurrencyfür den Durchsatz einzelner Dateien.
- Ziel: Überlastung Ihrer Netzwerkverbindung beim Hochladen einer einzelnen großen Datei.
- Ausgangspunkt: Für große Dateien (>100 MB) in einem schnellen Netzwerk sind Werte zwischen
8und32üblich. Der SDK-Standardwert ist 10.
- Ausgangspunkt: Für große Dateien (>100 MB) in einem schnellen Netzwerk sind Werte zwischen
- Begrenzende Faktoren:
- Netzwerkbandbreite: Wenn Ihre Netzwerkverbindung ausgelastet ist, hilft eine weitere Erhöhung der Parallelität nicht und kann aufgrund des Overheads sogar zu einer geringfügigen Leistungseinbuße führen.
- Speicher: Jeder parallele Teil belegt einen Puffer im Speicher (
PartSize). Der gesamte Speicherverbrauch für den Upload einer Datei beträgt ungefährWorkers * Concurrency * PartSize.
- Speicher: Jeder parallele Teil belegt einen Puffer im Speicher (
Der Tuning-Prozess
Abschnitt betitelt „Der Tuning-Prozess“Befolgen Sie diesen iterativen Prozess, um die richtige Balance zu finden:
-
Legen Sie eine Grundlinie fest.
Führen Sie das Skript mit niedrigen, konservativen Werten aus (z. B.
WORKERS=4,CONCURRENCY=4). Das ist Ihre Basisleistung. -
Erhöhen Sie die Anzahl der Arbeitnehmer.
Halten Sie
CONCURRENCYkonstant und erhöhen SieWORKERSschrittweise (z. B. 4, 8, 16, 32, 64). Überwachen Sie CPU-Auslastung und gesamte Upload-Zeit. Sie erreichen einen Punkt, an dem zusätzliche Worker die Leistung nicht mehr verbessern oder sogar verschlechtern. Das ist Ihre optimale Worker-Anzahl für diesen Dateisatz. -
Erhöhen Sie die Parallelität.
Beginnen Sie mit Ihrer optimalen
WORKERS-Anzahl und erhöhen Sie dannCONCURRENCY(z. B. 4, 8, 16, 32). Das hilft vor allem bei großen Dateien in Ihrem Datensatz. Finden Sie erneut den Punkt mit abnehmendem Nutzen. -
Teilegröße anpassen.
Bei sehr großen Dateien (mehrere GB) kann ein größeres
PART_SIZE_MB(z. B. 64, 128, 256) effizienter sein, da es die Gesamtzahl der Teile und API-Aufrufe pro Upload reduziert.
Durch die methodische Abstimmung dieser drei Parameter können Sie die Leistung an die spezifischen Eigenschaften Ihrer Hardware und Arbeitslast anpassen.
Beispiel-Go-Skript
Abschnitt betitelt „Beispiel-Go-Skript“Dieses Skript konfiguriert den S3-Client und den Upload-Service. Es verwendet einen Worker-Pool, um mehrere Dateien parallel aus einem lokalen Verzeichnis hochzuladen. Die Leistung können Sie direkt im Block CONFIGURATION abstimmen.
Speichern als main.go
package main
import ( "context" "fmt" "log" "net/http" "os" "path/filepath" "sync" "time"
"github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3")
func main() { // --- CONFIGURATION --- // 1. Set your S3 Bucket and Region bucket := "your-s3-bucket-name" region := "eu01"
// 2. Set Local Source and Performance Parameters srcDir := "./test-data" // Directory containing your test files
// Performance Tuning workers := 8 // How many files to upload at the same time concurrency := 32 // How many chunks per file to upload at once partSizeMB := 64 // The size of each chunk in Megabytes // ---------------------
ctx := context.TODO()
// 3. Initialize SDK with a 15s Timeout cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region), config.WithHTTPClient(&http.Client{ Timeout: 15 * time.Second, }), ) if err != nil { log.Fatalf("unable to load SDK config: %v", err) }
client := s3.NewFromConfig(cfg) uploader := manager.NewUploader(client, func(u *manager.Uploader) { u.PartSize = int64(partSizeMB) * 1024 * 1024 u.Concurrency = concurrency })
// 4. Gather all files from the source directory files, err := os.ReadDir(srcDir) if err != nil { log.Fatalf("failed to read directory %q: %v", srcDir, err) }
// 5. Worker Pool Logic jobs := make(chan string, len(files)) var wg sync.WaitGroup start := time.Now()
fmt.Printf("Starting benchmark: %d workers, %d concurrency per file, %dMB part size\n", workers, concurrency, partSizeMB)
for w := 1; w <= workers; w++ { wg.Add(1) go func(workerID int) { defer wg.Done() for fileName := range jobs { fullPath := filepath.Join(srcDir, fileName) file, err := os.Open(fullPath) if err != nil { fmt.Printf("[Worker %d] Error opening %s: %v\n", workerID, fileName, err) continue }
_, err = uploader.Upload(ctx, &s3.PutObjectInput{ Bucket: &bucket, Key: &fileName, Body: file, }) file.Close()
if err != nil { fmt.Printf("[Worker %d] Error uploading %s: %v\n", workerID, fileName, err) } } }(w) }
// Send files to the worker pool for _, f := range files { if !f.IsDir() { jobs <- f.Name() } } close(jobs) wg.Wait()
fmt.Printf("\nFinished! Uploaded %d files in %v\n", len(files), time.Since(start))}So führen Sie das Testskript aus
Abschnitt betitelt „So führen Sie das Testskript aus“-
Anmeldeinformationen festlegen
Exportieren Sie Ihren AWS-Zugriffsschlüssel und Ihren geheimen Schlüssel in Ihr Terminal. Das Skript verwendet sie automatisch zur Authentifizierung.
Terminal-Fenster export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY" -
Bereiten Sie Testdaten vor
Das Skript lädt Dateien aus einem lokalen Verzeichnis hoch (Standard
./test-data). Erstellen Sie zunächst das Verzeichnis:Terminal-Fenster mkdir -p ./test-dataErstellen Sie anschließend einige Beispieldateien, um eine Arbeitslast zu simulieren. Dafür eignet sich der Befehl
dd.Hier finden Sie drei Beispiele für die Erstellung von Testdateien in unterschiedlichen Größen. Jede for-Schleife erstellt 1 GB Testdateien mit unterschiedlichen Dateigrößen.
Terminal-Fenster # Create 4 large 256 MB filefor i in {1..4}; dodd if=/dev/zero of=./test-data/large_256MB_$i.tmp bs=1M count=256 2>/dev/nulldoneTerminal-Fenster # Create 20 medium 50 MB filefor i in {1..20}; dodd if=/dev/zero of=./test-data/medium_50MB_$i.tmp bs=1M count=50 2>/dev/nulldoneTerminal-Fenster # Create 1024 small 1 MB filefor i in {1..1024}; dodd if=/dev/zero of=./test-data/small_1MB_$i.tmp bs=1M count=1 2>/dev/nulldone -
Passen Sie die Parameter an
Gehen Sie in das
main.go-Skript und wählen Sie im Abschnitt „Konfiguration” den richtigen Parameter aus. Ändern Sie bei Bedarf die folgenden Zeilen:// Performance Tuningworkers := 8 // How many files to upload at the same timeconcurrency := 32 // How many chunks per file to upload at oncepartSizeMB := 64 // The size of each chunk in Megabytes -
Führen Sie das Skript aus
Terminal-Fenster go mod init main.gogo mod tidygo run main.go