Best practices for Memorystore for Redis

Auf dieser Seite finden Sie eine Anleitung zur optimalen Verwendung von Memorystore for Redis. Diese Seite weist auch auf mögliche Probleme hin.

Eine Liste mit Szenarien zur Fehlerbehebung finden Sie unter Fehlerbehebung.

RDB-Export

Beachten Sie beim Exportieren einer RDB-Sicherung die folgende Anleitung:

Ressourcenintensive Vorgänge

Bei Redis-Instanzen der Standardstufe wird für die folgenden Vorgänge während der Ausführung zusätzlicher Arbeitsspeicher verwendet:

Für Versionsupgrades, Skalierung und manuelles Failover wird aufgrund der Replikation zusätzlicher Arbeitsspeicher benötigt (für Instanzen der Standard-Tier). Diese Vorgänge folgen dem Replikationsprozess, wie unter Standardverhalten der Instanz-Upgrade beschrieben.

Import- und Exportvorgänge erfordern zusätzlichen Arbeitsspeicher, da der Redis-Prozess verzweigt wird und die Datenverwaltung bei diesen Vorgängen nach dem Copy-on-Write-Prinzip erfolgt.

Zur Verringerung der Nachteile ressourcenintensiver Vorgänge sollten Sie Folgendes tun:

Vorgänge und Szenarien, für die eine Verbindungswiederholung erforderlich ist

Folgende Vorgänge und Szenarien unterbrechen die Netzwerkverbindung zwischen Ihrem Netzwerk und der Redis-Instanz:

Durch diese Vorgänge wird Ihre Instanz geändert, sodass eine vorübergehende Verbindungsunterbrechung notwendig ist. Sie müssen eine Wiederholungslogik mit exponentiellem Backoff eingerichtet haben, bevor diese Vorgänge ausgeführt werden, damit Ihre Anwendung automatisch wieder eine Verbindung herstellt und weiterhin normal funktioniert.

Routinewartung

Memorystore for Redis-Instanzen werden regelmäßig gewartet. Weitere Informationen finden Sie in der Wartungsrichtlinie für Memorystore for Redis.

Setzen Sie folgende Best Practices ein, um auf routinemäßige Wartungen vorbereitet zu sein:

Speicherverwaltung

Die Speicherverwaltung kann aufgrund der bekannten Speicherfragmentierung, die bei Open-Source-Redis auftritt, eine Herausforderung sein. Wir empfehlen Ihnen, die Konfiguration von maxmemory für Ihre Instanz zu senken, um sich im Falle eines hohen Arbeitsspeichers durch Overhead zu kümmern.

Am besten lässt sich die Arbeitsspeichersituation Ihrer Memorystore-Instanz mit dem Messwert „Verhältnis der Arbeitsspeichernutzung des Systems“ überwachen. Einen detaillierten Leitfaden zur Verwaltung des Arbeitsspeichers für Memorystore for Redis finden Sie unter Best Practices für die Arbeitsspeicherverwaltung.

Inaktive Verbindungen verwalten

Im Laufe der Zeit kann die Anzahl der Verbindungen zu Ihrer Memorystore-Instanz zunehmen, wenn Verbindungen nicht ordnungsgemäß beendet werden. Dies kann sich negativ auf die Leistung auswirken, insbesondere wenn Sie die Verschlüsselung während der Übertragung verwenden. Hierbei gelten maximale Verbindungslimits entsprechend Ihrer Kapazitätsstufe. Um dies zu vermeiden, empfehlen wir die Verwendung des Redis-Konfigurationsparameters timeout, mit dem Sie die Anzahl der Sekunden festlegen können, bevor inaktive Clientverbindungen automatisch beendet werden.

Access Transparency-Ressourcennamen

Vertrauliche Daten sollten nicht in Memorystore for Redis-Ressourcennamen gespeichert werden. Mit Ressourcennamen sind Memorystore for Redis-Instanznamen und Instanzmetadaten wie Tags gemeint. Es wird nicht garantiert, dass Daten, die in Ressourcennamen gespeichert sind, durch Google Cloud Access Transparency geschützt sind. Sie können mit den Access Transparency-Compliance-Anforderungen Ihrer Organisation in Konflikt geraten.

Connector für serverlosen VPC-Zugriff für einige serverlose Umgebungen erforderlich

In einigen serverlosen Umgebungen ist ein Connector für serverlosen VPC-Zugriff erforderlich, um eine Verbindung zu Memorystore for Redis herzustellen. Richten Sie den Connector für serverlosen VPC-Zugriff für Ihr Projekt ein, wenn Sie eine Verbindung über eine dieser Umgebungen herstellen möchten.

Netzwerk

Wir empfehlen die Verwendung des Verbindungsmodus Zugriff auf private Dienste. Memorystore for Redis verwendet zwei Verbindungsmodi: Zugriff auf private Dienste und Direct Peering. Mit dem Verbindungsmodus für den Zugriff auf private Dienste wird die Verwaltung von IP-Bereichen vereinfacht und Sie können bei Bedarf eine freigegebene VPC verwenden.

Der Verbindungsmodus kann nach dem Erstellen einer Instanz nicht mehr geändert werden.

Weitere Informationen finden Sie unter Netzwerk.

Monitoring und Warnungen

Wir empfehlen die Verwendung von Monitoring und Benachrichtigungen, da sie wichtige Signale zur Arbeitsspeichernutzung Ihrer Redis-Instanz liefern. Sie geben außerdem Aufschluss darüber, wie effizient Ihre Redis-Instanz auf eingehende Cache-Anfragen reagiert.

Sie sollten folgende Standardwarnungen einrichten:

Best Practices für die CPU-Nutzung

Die unsachgemäße Verwendung von ressourcenintensiven Redis-Befehlen führt zu hoher Latenz, mangelnder Reaktionsfähigkeit oder Verbindungsproblemen. Instanzen der Standard-Stufe bieten Hochverfügbarkeit bei der Notfallwiederherstellung und basieren auf asynchroner Replikation zwischen primären und Replikatknoten. Wenn auf einem der Knoten eine aufwendige Befehlsverarbeitung stattfindet, die den Redis-Hauptthread blockiert, kann sich das auf die Replikation auswirken. Wenn das Problem weiterhin besteht und ein Standortausfall auftritt, sind die zuletzt am Standort des Ausfalls geschriebenen Daten möglicherweise nicht am anderen Standort verfügbar.

Wir empfehlen, Cloud Monitoring zu verwenden, um Benachrichtigungen für den Messwert CPU-Sekunden des Hauptthreads (redis.googleapis.com/stats/cpu_utilization_main_thread) festzulegen. So können Sie dafür sorgen, dass die CPU-Auslastung für den primären Knoten nicht mehr als 0,8 Sekunden und für jeden Replikatknoten nicht mehr als 0,5 Sekunden beträgt, wenn das Replikat als Lesereplikat festgelegt ist.

Wenn Ihre Redis-Instanz die empfohlenen Werte überschreitet, empfehlen wir, die Instanz auf eine höhere Kapazitätsstufe zu skalieren oder die Anleitung zur Fehlerbehebung zu befolgen, um CPU-intensive Vorgänge zu vermeiden.

Wenn die CPU-Auslastung Ihrer Instanz hoch ist oder die Ressourcen der Instanz erschöpft sind (z. B. durch zu viele Verbindungen), kann es sein, dass sich die Instanz nicht richtig verhält und externe Messwerte fehlen.

Ressourcenintensive Befehle

Wir empfehlen dringend, ressourcenintensive Redis-Befehle zu vermeiden. Die Verwendung dieser Befehle kann zu den folgenden Leistungsproblemen führen:

  • Hohe Latenz und Client-Zeitüberschreitungen
  • Arbeitsspeichermangel durch Befehle, die die Arbeitsspeichernutzung erhöhen
  • Datenverlust während der Knotenreplikation und ‑synchronisierung, da der Redis-Hauptthread blockiert ist
  • Systemdiagnosen, die nicht ausgeführt werden, Beobachtbarkeit und Replikation

In der folgenden Tabelle finden Sie Beispiele für ressourcenintensive Redis-Befehle und ressourcenschonende Alternativen.

Kategorie Ressourcenintensiver Befehl Ressourcenschonende Alternative
Für den gesamten Schlüsselbereich ausführen KEYS SCAN
Für einen Schlüsselsatz mit variabler Länge ausführen LRANGE Begrenzen Sie die Größe des Bereichs, den Sie für eine Abfrage verwenden.
ZRANGE Begrenzen Sie die Größe des Bereichs, den Sie für eine Abfrage verwenden.
HGETALL HSCAN
SMEMBERS SSCAN
Ausführung eines Skripts blockieren EVAL Achten Sie darauf, dass Ihr Script nicht unbegrenzt ausgeführt wird.
EVALSHA Achten Sie darauf, dass Ihr Script nicht unbegrenzt ausgeführt wird.
Dateien und Links entfernen DEL UNLINK
Veröffentlichen und abonnieren PUBLISH SPUBLISH
SUBSCRIBE SSUBSCRIBE

Best Practices für Redis-Clients

In diesem Abschnitt finden Sie Informationen zur optimalen Verwendung Ihres Redis-Clients.

Nicht reagierende Verbindungen erkennen und verarbeiten

Wir empfehlen dringend, Ihre Clientanwendung so zu konfigurieren, dass nicht reagierende Verbindungen zu Memorystore for Redis erkannt werden. Wenn eine nicht reagierende Verbindung erkannt wird, muss der Client sie zurücksetzen. Um eine robuste Anwendung zu erstellen, empfehlen wir die folgenden Clientkonfigurationen:

  • TCP-Keep-Alive-Parameter konfigurieren: Legen Sie die Parameter TCP keepalive time, TCP keepalive interval und TCP keepalive probes so fest, dass Clients nicht reagierende Verbindungen proaktiv erkennen und trennen, auch wenn die Verbindungen inaktiv sind. Wenn Sie beispielsweise den Parameter TCP keepalive time auf 30 Sekunden, TCP keepalive interval auf 10 Sekunden und TCP keepalive probes auf 3 festlegen, werden inaktive Verbindungen, die nicht reagieren, innerhalb einer Minute zurückgesetzt.
  • TCP-Nutzer-Timeouts konfigurieren: Legen Sie dieses Timeout in Ihren Clients fest, um Verbindungen zurückzusetzen, für die ausstehende Anfragen vorhanden sind und die nicht mehr reagieren. Wenn Sie das Zeitlimit beispielsweise auf 15 Sekunden festlegen, werden nicht reagierende Verbindungen mit ausstehenden Anfragen nach 15 Sekunden zurückgesetzt.

Best Practices für Kunden

Wenn Sie Ihre Anwendungen horizontal skalieren, können bei Schreibbefehlen READONLY-Fehler auftreten. Memorystore for Redis verwendet eine Bereitstellung ohne Sentinel mit statischen Endpunkten und Clients erhalten keine dynamischen Rolleninformationen im Voraus. Wenn Sie eine einzelne Clientverbindung für Ihre primären und Replikatendpunkte verwenden, kann es passieren, dass Ihr Client versehentlich Schreibbefehle an das schreibgeschützte Replikat sendet. Das Replikat gibt einen READONLY-Fehler zurück, weil es keine Schreibbefehle verarbeiten kann.

Verwenden Sie nicht eine einzelne Verbindung für Lese- und Schreibvorgänge, um Fehlleitungen von Schreibvorgängen zu vermeiden. Trennen Sie Ihre Vorgänge stattdessen, indem Sie die folgenden separaten Clientinstanzen erstellen:

  • Primärer Client: Nur mit dem primären Endpunkt verbinden
  • Replikatclient: Nur Verbindung zum Replikatendpunkt herstellen

Auf den folgenden Tabs sehen Sie, wie Sie die einzelnen Vorlagen in Go, Java, Node.js und Python konfigurieren.

Go

package main

import (
  "context"
  "fmt"

  "github.com/redis/go-redis/v9"
)

func main() {
ctx := context.Background()

  // Initialize the primary client connecting only to the primary endpoint
  primaryClient := redis.NewClient(&redis.Options{
      Addr:     "PRIMARY_HOST:6379",
      Password: "YOUR_PASSWORD",
      DB:       0,
  })
  defer primaryClient.Close()

  // Initialize the replica client connecting only to the replica endpoint
  replicaClient := redis.NewClient(&redis.Options{
      Addr:     "REPLICA_HOST:6379",
      Password: "YOUR_PASSWORD",
      DB:       0,
  })
  defer replicaClient.Close()

  // Use the primary client for all mutating commands
  err := primaryClient.Set(ctx, "example_key", "example_value", 0).Err()
  if err != nil {
      fmt.Printf("Failed to write to primary: %v\n", err)
  }

  // Use the replica client for all read-only commands
  val, err := replicaClient.Get(ctx, "example_key").Result()
  if err != nil {
      fmt.Printf("Failed to read from replica: %v\n", err)
  } else {
      fmt.Printf("Successfully read value: %s\n", val)
  }
}

Java

@Bean
public RedisConnectionFactory primaryConnectionFactory() {
  RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("PRIMARY_HOST", 6379);
  config.setPassword(RedisPassword.of("YOUR_PASSWORD"));
  return new LettuceConnectionFactory(config);
}

@Bean
public RedisConnectionFactory replicaConnectionFactory() {
  RedisStaticMasterReplicaConfiguration config =
      new RedisStaticMasterReplicaConfiguration("PRIMARY_HOST", 6379);
  config.addNode("REPLICA_HOST", 6379);
  config.setPassword(RedisPassword.of("YOUR_PASSWORD"));

  LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
      .readFrom(ReadFrom.REPLICA_PREFERRED)
      .build();

  return new LettuceConnectionFactory(config, clientConfig);
}

@Bean
public RedisTemplate<String, Object> primaryRedisTemplate(
  @Qualifier("primaryConnectionFactory") RedisConnectionFactory factory) {

  RedisTemplate<String, Object> template = new RedisTemplate<>();
  template.setConnectionFactory(factory);
  return template;
}

@Bean
public RedisTemplate<String, Object> replicaRedisTemplate(
  @Qualifier("replicaConnectionFactory") RedisConnectionFactory factory) {

  RedisTemplate<String, Object> template = new RedisTemplate<>();
  template.setConnectionFactory(factory);
  return template;
}

Node.js

import { createClient } from 'redis';

async function main() {
// Initialize the primary client connecting only to the primary endpoint
const primaryClient = createClient({
  url: 'redis://PRIMARY_HOST:6379',
  password: 'YOUR_PASSWORD'
});

// Initialize the replica client connecting only to the replica endpoint
const replicaClient = createClient({
  url: 'redis://REPLICA_HOST:6379',
  password: 'YOUR_PASSWORD'
});

primaryClient.on('error', (err) => console.error('Primary Client Error', err));
replicaClient.on('error', (err) => console.error('Replica Client Error', err));

await primaryClient.connect();
await replicaClient.connect();

// Use the primary client for all mutating commands
try {
  await primaryClient.set('example_key', 'example_value');
  console.log('Successfully wrote to primary');
} catch (err) {
  console.error('Failed to write to primary:', err);
}

// Use the replica client for all read-only commands
try {
  const val = await replicaClient.get('example_key');
  console.log(`Successfully read value: ${val}`);
} catch (err) {
  console.error('Failed to read from replica:', err);
}

await primaryClient.disconnect();
await replicaClient.disconnect();
}

main();

Python

import redis

def main():
  # Initialize the primary client connecting only to the primary endpoint
  primary_client = redis.Redis(
      host='PRIMARY_HOST',
      port=6379,
      password='YOUR_PASSWORD',
      decode_responses=True
  )

  # Initialize the replica client connecting only to the replica endpoint
  replica_client = redis.Redis(
      host='REPLICA_HOST',
      port=6379,
      password='YOUR_PASSWORD',
      decode_responses=True
  )

  # Use the primary client for all mutating commands
  try:
      primary_client.set('example_key', 'example_value')
      print('Successfully wrote to primary')
  except redis.RedisError as e:
      print(f'Failed to write to primary: {e}')

  # Use the replica client for all read-only commands
  try:
      val = replica_client.get('example_key')
      print(f'Successfully read value: {val}')
  except redis.RedisError as e:
      print(f'Failed to read from replica: {e}')

if __name__ == '__main__':
  main()