🌩️

AlibabaCloudでエンハンスRedisを利用〜分散キャッシュのアルゴリズム Beyond the Cache〜

2023/03/03に公開

目次

  1. RDBのデータをキャッシュ
  2. キャッシュの拡張性
  3. AlibabaCloud Tairの紹介
  4. 最後に

RDBのデータをキャッシュ

一般的なWebアプリケーションの構成はロードバランサーの後ろにオートスケールのサーバーフリートがあります。
大量なサーバーフリートの後ろにRDBがあります。
アプリケーションサーバーは負荷に応じてオートスケールしますが、データベースのオートスケールが課題になります。
例えばクラウドネイティヴ新型なデータベースAurora、PolarDB、AlloyDBなどを使ってもオートスケールできるのがリードレプリカだけで、スケールアウトの上限は15レプリカまでです。
RDBの負荷を軽減する手段の一つは手前にキャッシュを構えることです。
アプリケーションサーバーフリートとRDBの間に、Redisを設置することで、
RDBの負荷を軽減しつつ、レスポンスをマイクロ秒単位で、早く返すようにできます。

この場合の課題として、
アプリケーションサーバーフリートはオートスケールでき、クラウドネイティヴデータベースのリードレプリカもオートスケールでき、Redisのスケールはどうなのという課題が出てきます。

キャッシュの拡張性

キャッシュの拡張性について、データをシャーディングに保存するのが一般的です。
どのデータはどのシャードに保存されているのかを特定する必要があります。
データはどこにあるかを特定するために、いくつかの考え方があり、1つ1つ見ていきます。

方式一:ハッシュ値余数

ハッシュ値余数方は簡単に言いますと、Keyのハッシュ値をノード数で割り算をして、余数を求めて、
その余数でノードを決めます。

メリット:簡単に実装できます。
デメリット:ノード数の変化に弱い
              スケールアウト スケールイン 不向き

用途:小規模向け

方式二:コンシステントハッシュ法

ハッシュ値余数方は簡単に言いますと、Keyのハッシュ値をもとに、ひとつの円盤のように配置したノード上のデータを探しにいきます。

メリット:ハッシュ値余数と比べて、ノードの数を増減できるようになりました。
デメリット:イラストのように、場合によって一部のノードだけ負荷が集中する可能性があります。

用途:中規模向け

方式三:ハッシュスロット

上記の2つの方式を踏まえて、Redisが最終的に採用したのがこちらのハッシュスロットです。
※Memcachedはコンシステントハッシュ法などのあるアルゴリズムを使って、クライアント側でデータがどのノードにあるかを特定する必要があります。
ハッシュスロットは簡単に言いますと、Proxy のようなイメージで、どのデータがどのノードにあるかはProxyのようなSlotで管理しています。一方で、実際にはProxyサーバーがあるわけではなく、HashSlotが各Redisノードにあります。
HashSlotを配分、再配分することで、ノードを柔軟に増減できるようになります。なおかつ負荷が偏ることもないです。
メリット:ノードを増減でき、負荷が偏ることもないです。
デメリット:下のコマンド操作のように、HashSlotを配分、または再配分する手間がかかります。

# redis-cli --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6385 to 127.0.0.1:6381
Adding replica 127.0.0.1:6386 to 127.0.0.1:6382
Adding replica 127.0.0.1:6384 to 127.0.0.1:6383
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: ea29fafcaf7431e87910c1a7d9a796877d653cfd 127.0.0.1:6381
   slots:[0-5460] (5461 slots) master
M: 14328ae41008f607345c37b343a28deac09276f4 127.0.0.1:6382
   slots:[5461-10922] (5462 slots) master
M: f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6 127.0.0.1:6383
   slots:[10923-16383] (5461 slots) master
S: cce7cb8d0710ce9c3f9cdc5b6aa5e235535dff50 127.0.0.1:6384
   replicates ea29fafcaf7431e87910c1a7d9a796877d653cfd
S: 5b1d2fb7abf2ac89603d9a1cee190572c6c9dd8d 127.0.0.1:6385
   replicates 14328ae41008f607345c37b343a28deac09276f4
S: 38295f90be90d638ba3d061b6acae2aefdaa478b 127.0.0.1:6386
   replicates f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 127.0.0.1:6381)
M: ea29fafcaf7431e87910c1a7d9a796877d653cfd 127.0.0.1:6381
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: cce7cb8d0710ce9c3f9cdc5b6aa5e235535dff50 127.0.0.1:6384
   slots: (0 slots) slave
   replicates ea29fafcaf7431e87910c1a7d9a796877d653cfd
S: 38295f90be90d638ba3d061b6acae2aefdaa478b 127.0.0.1:6386
   slots: (0 slots) slave
   replicates f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6
M: f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6 127.0.0.1:6383
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 5b1d2fb7abf2ac89603d9a1cee190572c6c9dd8d 127.0.0.1:6385
   slots: (0 slots) slave
   replicates 14328ae41008f607345c37b343a28deac09276f4
M: 14328ae41008f607345c37b343a28deac09276f4 127.0.0.1:6382
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
redis-cli --cluster add-node 127.0.0.1:6387 127.0.0.1:6381
>>> Adding node 127.0.0.1:6387 to cluster 127.0.0.1:6381
>>> Performing Cluster Check (using node 127.0.0.1:6381)
M: ea29fafcaf7431e87910c1a7d9a796877d653cfd 127.0.0.1:6381
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: cce7cb8d0710ce9c3f9cdc5b6aa5e235535dff50 127.0.0.1:6384
   slots: (0 slots) slave
   replicates ea29fafcaf7431e87910c1a7d9a796877d653cfd
S: 5b1d2fb7abf2ac89603d9a1cee190572c6c9dd8d 127.0.0.1:6385
   slots: (0 slots) slave
   replicates 14328ae41008f607345c37b343a28deac09276f4
M: 14328ae41008f607345c37b343a28deac09276f4 127.0.0.1:6382
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 38295f90be90d638ba3d061b6acae2aefdaa478b 127.0.0.1:6386
   slots: (0 slots) slave
   replicates f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6
M: f1ee6ebc014e0604e84c82fa34dc9e2c6a6d24f6 127.0.0.1:6383
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:6387 to make it join the cluster.
[OK] New node added correctly.
redis-cli --cluster check 127.0.0.1:6381
127.0.0.1:6381 (ea29fafc...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:6382 (14328ae4...) -> 1 keys | 5462 slots | 1 slaves.
127.0.0.1:6387 (0be70f48...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:6383 (f1ee6ebc...) -> 2 keys | 5461 slots | 1 slaves.
redis-cli --cluster reshard 127.0.0.1:6381

16384/masterノード数

How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? 0be70f48b17d0281929f3abca297b2af0d22b038
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1: all
redis-cli --cluster check 127.0.0.1:6381
127.0.0.1:6381 (ea29fafc...) -> 1 keys | 4096 slots | 1 slaves.
127.0.0.1:6382 (14328ae4...) -> 1 keys | 4096 slots | 1 slaves.
127.0.0.1:6387 (0be70f48...) -> 1 keys | 4096 slots | 0 slaves.
127.0.0.1:6383 (f1ee6ebc...) -> 2 keys | 4096 slots | 1 slaves.
M: 0be70f48b17d0281929f3abca297b2af0d22b038 127.0.0.1:6387
   slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
redis-cli --cluster add-node 127.0.0.1:6388 127.0.0.1:6387 --cluster-slave --cluster-master-id 0be70f48b17d0281929f3abca297b2af0d22b038
How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? ea29fafcaf7431e87910c1a7d9a796877d653cfd
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1: 0be70f48b17d0281929f3abca297b2af0d22b038
Source node #2: done
redis-cli --cluster del-node 127.0.0.1:6387 0be70f48b17d0281929f3abca297b2af0d22b038

AlibabaCloud Tairの紹介

RedisクラスタのHashSlotを配分、または再配分する手間がAlibabaCloud マネージドサービスのRedisを使えば解消されます。
Redisマネージドサービスの1つはTairです。
Tairは普通のマネージドサービスのRedisとどう違うかというと、Tairはクラウドネイティヴなキャッシュです。
RDBでいうとAurora、PolarDB、AlloyDBのようなイメージです。
以下の特徴があります。

  • Redisと100%互換性を持っています。
  • 性能は普通のRedisの3倍です。
  • データの耐久性もあります。
  • シームレスにスケールできます。
  • ポイントインタイムリカバリできます。
  • グローバルデータベース(Geo-replication)をサポートしています。

こちらの構成のように、Redisはノード上にHashSlotを管理しているのと比べて、TairはConfigサーバーというコンポーネントを導入しています。
Configサーバー側でどのデータがどのノードにあるかを管理してくれて、性能が向上します。
もちろんTairで改善しているところはConfigサーバーの導入だけでないですが、構成上Configサーバーの導入が目立つところです。

性能が3倍向上するだけでなく、Configサーバーを導入することで、スケールがシームレスにできるようになります。従来のマネージドサービスRedisはスケールアウト、スケールインするときに、瞬間的に切断が発生する場合があります。
Tairの場合はそのような瞬間的な切断がなく、シームレスにスケールできます。

最後に

オンプレミスもしくは仮想サーバーフリート上にRedisをインストールして運用する場合はHashSlotの管理が出てきます。
マネージドサービスRedisを使えば、運用管理が楽になります。
さらに、クラウドネイティヴなエンハンスRedisであるTairを使えば、性能が3倍に向上し、シームレスにスケールでき、データの耐久性などのメリットも享受できます。

Discussion