ホットスポットキーに対する大量の読み取りリクエストの影響を軽減するには、読み書き分離を有効にして読み取り専用ノードを追加します。これにより、読み取りリクエストがすべてのノードに分散され、プライマリノードの負荷が軽減され、システム全体のスループットと安定性が向上します。
機能概要
読み書き分離アーキテクチャでは、すべての読み取り専用ノードがプライマリノードから非同期にデータを複製します。これにより、全体的なデータ同期遅延が低く、結果整合性が提供されます。これは、書き込み量が多い期間にデータ同期遅延が発生する可能性があることを意味します。このような場合、アプリケーションはわずかに古いデータを許容できる必要があります。例として、次のシナリオが挙げられます:
キャッシュデータ: 更新頻度の低いウェブサイトのホームページの HTML キャッシュ。
ゲームのリーダーボード: わずかな更新の遅延がユーザーに大きな影響を与えない、時間単位のリーダーボード。
天気予報データ: ユーザーは頻繁に天気情報をクエリしますが、データは固定のスケジュールで更新されます。
読み取り専用ノードを追加するメリット
プライマリノードの負荷を分散: プライマリノードは書き込みリクエストの 100% と読み取りリクエストの 1/N を処理します。ここで N はノードの総数です。たとえば、プライマリノードが 1 つ、読み取り専用ノードが 3 つある場合、N は 4 です。これにより、プライマリノードの CPU 負荷、ネットワークトラフィック、および接続のオーバーヘッドが削減されます。
システムのスループットを向上: 各読み取り専用ノードは、読み取りリクエストを独立して処理できます。全体的な読み取りパフォーマンスは線形にスケールします。理想的には、N 個の読み取り専用ノードを追加すると、読み取りパフォーマンスが N 倍に向上します。
応答遅延の削減: 読み取りリクエストを複数のノードに分散することで、個々のリクエストのキューイング時間が短縮されます。
標準 (プライマリ-レプリカ) アーキテクチャとクラスタアーキテクチャの両方で読み書き分離を有効にできます。クラスタアーキテクチャの場合、各データシャードに対応する読み取り専用ノードを追加できます。アーキテクチャの詳細については、「読み書き分離」をご参照ください。
有効化の方法
前提条件:
インスタンスは クラウドネイティブ モードでデプロイされていること。
インスタンスが Redis Community Edition または Tair (Enterprise Edition) のメモリ最適化インスタンスまたは永続メモリインスタンスであること。
インスタンスの容量が 1 GB 以上であること。
インスタンスが高可用性 (HA) インスタンスであること。
インスタンス詳細ページの左側のナビゲーションウィンドウで、[ノード管理] をクリックし、[読み書き分離] スイッチをオンにします。詳細については、「読み書き分離を有効にする」をご参照ください。
クライアント接続
シングルゾーンインスタンス
通常、読み書き分離アーキテクチャのインスタンスは単一のエンドポイントを提供します。インスタンスは、ルーティングに Proxy コンポーネントを使用します。コードを修正する必要はありません。この機能はすぐに使用できます。詳細については、「クライアント接続チュートリアル」をご参照ください。
デュアルゾーンインスタンス
読み書き分離アーキテクチャを持つデュアルゾーンインスタンスの場合、インスタンスはプライマリゾーンとセカンダリゾーンに個別のエンドポイントを提供します。セカンダリゾーンのエンドポイントは、読み取りリクエスト専用です。これにより、ローカルアクセスが可能になり、読み取りリクエストの遅延が短縮されます。
たとえば、杭州ゾーン I (プライマリゾーン) と 杭州ゾーン J (セカンダリゾーン) にあるインスタンスを考えてみましょう。
杭州ゾーン I のクライアント (ECS インスタンス) は、プライマリゾーンのエンドポイントに接続して、読み取りおよび書き込み操作を実行できます。次のコードは例です:
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class MasterReadWrite { public static void main(String[] args) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(200); config.setMaxTotal(300); config.setTestOnBorrow(false); config.setTestOnReturn(false); // プライマリゾーンのエンドポイント、ポート、パスワードを設定します。 String host = "r-bp1vtq8tnrquy****pd.redis.rds.aliyuncs.com"; int port = 6379; String password = "default:Passw***2"; JedisPool pool = new JedisPool(config, host, port, 3000, password); Jedis jedis = null; try { jedis = pool.getResource(); // 操作を実行します。次のコードは例です。 jedis.set("foo", "bar"); System.out.println(jedis.get("foo")); } catch (Exception e) { // タイムアウトやその他の例外を処理します。 e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } pool.destroy(); // アプリケーションが終了するときに、このメソッドを呼び出してリソースを破棄します。このメソッドはクライアントを切断し、リソースを解放します。 } }杭州ゾーン J のクライアント (ECS インスタンス) は、セカンダリゾーンのエンドポイントに接続して、読み取り操作のみを実行できます。書き込み操作を実行するには、クライアントは引き続きプライマリゾーンのエンドポイントに接続する必要があります。次のコードは例です:
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class ReplicaRead { public static void main(String[] args) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(200); config.setMaxTotal(300); config.setTestOnBorrow(false); config.setTestOnReturn(false); // セカンダリゾーンのエンドポイント、ポート、パスワードを設定します。 String host = "r-bp1vtq8tnrquy****pd.redis.rds.aliyuncs.com"; int port = 6379; String password = "default:Passw***2"; JedisPool pool = new JedisPool(config, host, port, 3000, password); Jedis jedis = null; try { jedis = pool.getResource(); // 操作を実行します。次のコードは例です。 System.out.println(jedis.get("foo")); } catch (JedisDataException e) { // 書き込み操作が誤って実行された場合に、書き込み操作の例外をキャッチします。 e.getMessage(); } catch (Exception e) { // タイムアウトやその他の例外を処理します。 e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } } pool.destroy(); // アプリケーションが終了するときに、このメソッドを呼び出してリソースを破棄します。このメソッドはクライアントを切断し、リソースを解放します。 } }