使用 Memorystore 快取資料

在處理某些工作時,高效能且可擴充的網路應用程式通常會優先使用記憶體內建的分散式資料快取,或是以此替代完善的永久儲存空間。建議您使用 Memorystore for Redis 做為快取服務。請注意,Memorystore for Redis 不提供免費方案。詳情請參閱「Memorystore 定價」一節。

開始前,請確認應用程式會維持在 Memorystore for Redis 配額範圍內。

使用記憶體快取的時機

工作階段資料、使用者偏好設定和其他網頁查詢傳回的資料都很適合使用快取。一般來說,如果經常執行的查詢會傳回一組不需要立即顯示在應用程式中的結果,您可以將結果快取。如此一來,後續要求就可以先查看快取,然後只有在找不到結果或結果已過期時,才去查詢資料庫。

如果您只將值儲存在 Memorystore 中,而未在永久儲存空間中備份,請務必確保在值到期並從快取中移除時,應用程式仍能正常運作。舉例來說,如果使用者的工作階段會因為資料突然消失,變成運作不良,那麼資料除了存放在 Memorystore,可能也應該儲存在資料庫。

瞭解 Memorystore 權限

您必須授權才能與 Google Cloud 服務互動。舉例來說,如要與由 Memorystore 代管的 Redis 資料庫互動,應用程式必須提供已授權存取 Memorystore 的帳戶憑證。

根據預設,您的應用程式會提供 App Engine 預設服務帳戶的憑證,該帳戶已獲授權存取與應用程式相同專案中的資料庫。

如果符合下列任一條件,您就必須使用其他驗證方法,明確提供憑證:

  • 您的應用程式和 Memorystore 資料庫位於不同的Google Cloud 專案中。

  • 您已變更指派給預設 App Engine 服務帳戶的角色。

如要瞭解其他驗證技術,請參閱「設定伺服器對伺服器正式版應用程式的驗證作業」。

使用 Memorystore 的總覽

如何在應用程式中使用 Memorystore:

  1. 設定 Memorystore for Redis,這需要您在 Memorystore 上建立 Redis 執行個體,並建立應用程式用來與 Redis 執行個體通訊的無伺服器虛擬私有雲端存取。建立這兩個獨立實體的順序不拘,可以按照任何順序設定。本指南的操作說明會先說明如何設定無伺服器虛擬私有雲存取。

  2. 安裝 Redis 用戶端程式庫,並使用 Redis 指令快取資料。

    Memorystore for Redis 與 所有適用於 Redis 的用戶端程式庫相容。本指南說明如何使用 Jedis 用戶端程式庫,從應用程式傳送 Redis 指令。如要進一步瞭解如何使用 Jedis,請參閱 Jedis 維基百科

  3. 測試更新內容

  4. 將應用程式部署至 App Engine

設定 Memorystore for Redis

如要設定 Memorystore for Redis,請按照下列步驟操作:

  1. 將 App Engine 連線至虛擬私有雲網路。您的應用程式只能透過虛擬私有雲連接器與 Memorystore 通訊。

    請務必按照「設定應用程式使用連接器」一文所��,��� VPC 連線���訊新增至 app.yaml 檔案。

  2. 請記下您建立的 Redis 執行個體的 IP 位址和通訊埠編號。您可以在程式碼中建立 Redis 用戶端時使用這項資訊。

  3. 在 Memorystore 中建立 Redis 執行個體

    收到系統提示時,請選取 Redis 執行個體的所在地區,請選取與 App Engine 應用程式相同的地區

安裝依附元件

如要在應用程式在 App Engine 中執行時提供 Jedis 用戶端程式庫,請將程式庫新增至應用程式的依附元件。舉例來說,如果您使用 Maven,請在 pom.xml 檔案中新增下列依附元件:
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>5.1.0</version>
</dependency>

建立 Redis 用戶端

如要與 Redis 資料庫互動,程式碼需要建立 Redis 用戶端,以便管理與 Redis 資料庫的連線。以下各節將說明如何使用 Jedis 用戶端程式庫建立 Redis 用戶端。

指定環境變數

Jedis 用戶端程式庫會使用兩個環境變數組合 Redis 資料庫的網址:

  • 用於識別您在 Memorystore 中建立的 Redis 資料庫 IP 位址的變數。
  • 用於識別您在 Memorystore 中建立的 Redis 資料庫通訊埠編號的變數。

建議您在應用程式的 app.yaml 檔案中定義這些變數,而不要直接在程式碼中定義。這樣一來,您就能更輕鬆地在不同環境 (例如本機環境和 App Engine) 中執行應用程式。

舉例來說,請在 app.yaml 檔案中新增下列行:

 env_variables:
      redis.host: '10.112.12.112'
      redis.port: '6379'

匯入 Jedis 並建立用戶端

使用 Jedis 程式庫時,建議您建立 JedisPool,然後使用該池建立用戶端。以下程式碼行會使用您先前定義的 redis.hostredis.port 環境變數,建立集區:


package com.example.redis;

import java.io.IOException;
import java.util.Properties;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@WebListener
public class AppServletContextListener implements ServletContextListener {

  private Properties config = new Properties();

  private JedisPool createJedisPool() throws IOException {
    String host;
    Integer port;
    config.load(
        Thread.currentThread()
            .getContextClassLoader()
            .getResourceAsStream("application.properties"));
    host = config.getProperty("redis.host");
    port = Integer.valueOf(config.getProperty("redis.port", "6379"));

    JedisPoolConfig poolConfig = new JedisPoolConfig();
    // Default : 8, consider how many concurrent connections into Redis you will need under load
    poolConfig.setMaxTotal(128);

    return new JedisPool(poolConfig, host, port);
  }

  @Override
  public void contextDestroyed(ServletContextEvent event) {
    JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");
    if (jedisPool != null) {
      jedisPool.destroy();
      event.getServletContext().setAttribute("jedisPool", null);
    }
  }

  // Run this before web application is started
  @Override
  public void contextInitialized(ServletContextEvent event) {
    JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");
    if (jedisPool == null) {
      try {
        jedisPool = createJedisPool();
        event.getServletContext().setAttribute("jedisPool", jedisPool);
      } catch (IOException e) {
        // handle exception
      }
    }
  }
}

如要從集區建立用戶端,請使用 JedisPool.getResource() 方法。例如:


package com.example.redis;

import java.io.IOException;
import java.net.SocketException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@WebServlet(name = "Track visits", value = "")
public class VisitCounterServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    try {
      JedisPool jedisPool = (JedisPool) req.getServletContext().getAttribute("jedisPool");

      if (jedisPool == null) {
        throw new SocketException("Error connecting to Jedis pool");
      }
      Long visits;

      try (Jedis jedis = jedisPool.getResource()) {
        visits = jedis.incr("visits");
      }

      resp.setStatus(HttpServletResponse.SC_OK);
      resp.getWriter().println("Visitor counter: " + String.valueOf(visits));
    } catch (Exception e) {
      resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
  }
}

使用 Redis 指令在快取中儲存及擷取資料

雖然 Memorystore Redis 資料庫支援大部分 Redis 指令,但您只需使用幾個指令,即可儲存快取資料並從中擷取資料。下表列出可用於快取資料的 Redis 指令。如要瞭解如何從應用程式呼叫這些指令,請參閱用戶端程式庫的說明文件。

工作 Redis 指令
在資料快取中建立項目,並
為項目設定到期時間
SETNX
MSETNX
從快取中擷取資料 GET
MGET
取代現有的快取值 SET
MSET
遞增或遞減數值快取值 INCR
INCRBY
DECR
DECRBY
從快取中刪除項目 DEL
取消連結
支援與快取的並行互動 請參閱 Redis 交易的詳細說明。

測試更新

在本機測試應用程式時,建議您執行 Redis 的本機執行個體,以免與實際工作環境中的資料互動 (Memorystore 不提供模擬器)。如要在本機安裝及執行 Redis,請按照 Redis 說明文件中的指示操作。請注意,目前無法在 Windows 本機上執行 Redis。

部署您的應用程式

應用程式在本機開發伺服器中執行時,如果沒有發生錯誤,請按照下列步驟操作:

  1. 在 App Engine 上測試應用程式

  2. 如果應用程式運作無誤,請使用流量分配功能,逐步增加更新版應用程式的流量。在將更多流量導向更新版應用程式之前,請密切監控應用程式是否有任何資料庫問題。