【新手】尝试使用亚马逊ElastiCache for Redis

1. 用意

    AWSのデータベース関連サービスの復習をしている。ElastiCacheを使ったことがなかったため、AWS公式のハンズオンを実施することで少し理解を深める。

2. 曾经做过的事情

    • AWS公式ハンズオン「Amazon ElastiCache for Redis を使い、MySQL データベースのパフォーマンスを向上させる」を一通り実施する。

 

    ハンズオンをなぞるだけでは今一つ動作が見えないため、Redis内のデータ保存状態を確認するなど、少し追加の動作確認を行う。

3. 亚马逊ElastiCache是什么(我理解的)。

    インメモリDBであるMemcachedとRedisをAWSがマネージドサービスとして提供しているもの。

4. 构图

redis構成図.png

5. 实机确认步骤

    • AWS公式ハンズオン「Amazon ElastiCache for Redis を使い、MySQL データベースのパフォーマンスを向上させる」をそのまま実施するが、疑問に思った点を追加で確認する。

 

    公式ハンズオン手順に記載されていない、追加作業を行った部分を中心に以下に記録する。

5.1 准备作业用实例

    • サブネットが2つあるVPCを用意する。(RDS、ElastiCacheのデプロイには2つサブネットが必要)

 

    作業用EC2インスタンス(Amazon Linux 2)を起動し、ハンズオンの「前提条件」通りに必要資材をインストールする。
$ sudo yum install git -y
$ sudo yum install mysql -y
$ sudo yum install python3 -y
$ pip3 install --user virtualenv
$ git clone https://github.com/aws-samples/amazon-elasticache-samples/
$ cd amazon-elasticache-samples/database-caching
$ virtualenv venv
$ source ./venv/bin/activate
$ pip3 install -r requirements.txt
    後の動作確認にRedis接続用クライアント(redis-cli)を使いたいため、追加で入れておく。
[ec2-user@ip-10-0-1-50 ~]$ sudo amazon-linux-extras install redis4.0

创建5.2版的ElastiCache for Redis。

    • ハンズオン手順「ステップ 1: Redis クラスターを作成する」の通りにRedisクラスターを作成する。

 

    作成時に必要なVPC、サブネットなどはあらかじめ用意したものを利用する。

创建5.3 RDS数据库并输入数据。

    • ハンズオン手順「ステップ 2: MySQL データベースを作成する」の通りにRDSデータベースを作成する。

 

    • ハンズオン手順「ステップ 3: MySQL データベースに入力する」の通りにデータを入力する。

結果としてテーブル「planet」に以下のデータが入力される。(Plutoがないのが気になるがそのままにしておく。)

MySQL [tutorial]> select * from planet;
+----+---------+
| id | name    |
+----+---------+
|  1 | Mercury |
|  2 | Venus   |
|  3 | Earth   |
|  4 | Mars    |
|  5 | Jupiter |
|  6 | Saturn  |
|  7 | Uranus  |
|  8 | Neptune |
+----+---------+
8 rows in set (0.00 sec)

5.4 进行 ElastiCache + RDS 的操作验证

5.4.1 确定操作步骤

    • ハンズオン手順「ステップ 4: キャッシュとベストプラクティス」に従い動作確認を行う。

ステップ4で急に少し手順が分かりづらくなるが、ハンズオンで用意されている資材「example.py」を連続して実行することで以下の動作確認が可能。

スクリプトの初回実行時、「select * from planet」などをまずElastiCache(Redis)に問い合わせする。キャッシュヒットしないので、RDS(MySQL)にアクセスしてデータを取得し、その内容をElastiCache(Redis)にも書き込む。
スクリプトの2回目の実行時、キャッシュヒットするので、ElastiCache(Redis)からそのまま結果が得られる。

「example.py」だけだと動作が分かりづらいため、少しスクリプトの内容を追加して 「myexample.py」とした。

実行時にキャッシュヒットしたのかどうかの表示(print文)を追加
実行に要した時間の計算を追加
コメントを追加

import os
import json

import redis
import pymysql
import datetime

class DB:
    def __init__(self, **params):
        params.setdefault("charset", "utf8mb4")
        params.setdefault("cursorclass", pymysql.cursors.DictCursor)

        self.mysql = pymysql.connect(**params)

    def query(self, sql):
        with self.mysql.cursor() as cursor:
            cursor.execute(sql)
            return cursor.fetchall()

    def record(self, sql, values):
        with self.mysql.cursor() as cursor:
            cursor.execute(sql, values)
            return cursor.fetchone()

# Time to live for cached data
### TTLの指定は秒単位
TTL = 60

# Read the Redis credentials from the REDIS_URL environment variable.
REDIS_URL = os.environ.get('REDIS_URL')

# Read the DB credentials from the DB_* environment variables.
DB_HOST = os.environ.get('DB_HOST')
DB_USER = os.environ.get('DB_USER')
DB_PASS = os.environ.get('DB_PASS')
DB_NAME = os.environ.get('DB_NAME')

# Initialize the database
Database = DB(host=DB_HOST, user=DB_USER, password=DB_PASS, db=DB_NAME)

# Initialize the cache
Cache = redis.Redis.from_url(REDIS_URL)

def fetch(sql):
    """Retrieve records from the cache, or else from the database."""
    ### まずRedisにアクセスし、値があればその値を返す。なければRDSからデータを取得し、
    ### RedisにTTL付きで書き込む。

    res = Cache.get(sql)

    if res:
        print("Cache Hit")
        return json.loads(res)

    res = Database.query(sql)

    ### setexはTTL付きでデータをstring型でsetする
    Cache.setex(sql, TTL, json.dumps(res))
    print("Cache Write")
    return res

def planet(id):
    """Retrieve a record from the cache, or else from the database."""
    key = f"planet:{id}"
    res = Cache.hgetall(key)

    if res:
        print("Cache Hit")
        return res

    sql = "SELECT `id`, `name` FROM `planet` WHERE `id`=%s"
    res = Database.record(sql, (id,))

    if res:
    ### hmsetはデータをhash型でsetする
        Cache.hmset(key, res)
        Cache.expire(key, TTL)
        print("Cache Write")
    return res

# Display the result of some queries

dt_now = datetime.datetime.now()
print("start:",dt_now)
print(fetch("SELECT * FROM planet"))
print(planet(1))
dt_now = datetime.datetime.now()
print("end:",dt_now)

5.4.2 执行结果确认(脚本运行)

    上記で作成したスクリプト「myexample.py」を2回連続して実行する。
# 初回
(venv) [ec2-user@ip-10-0-1-50 database-caching]$ python3 myexample.py
start: 2021-05-05 07:12:28.771716
Cache Write
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]
Cache Write
{'id': 1, 'name': 'Mercury'}
end: 2021-05-05 07:12:28.792133
# 2回目
(venv) [ec2-user@ip-10-0-1-50 database-caching]$ python3 myexample.py
start: 2021-05-05 07:12:46.273983
Cache Hit
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]
Cache Hit
{b'id': b'1', b'name': b'Mercury'}
end: 2021-05-05 07:12:46.284483

実行内容は以下の通り。

fetch(“SELECT * FROM planet”) は、「select * from planet」 を実行し、キャッシュヒットすればその値を取得、キャッシュミスした場合はRDSでSELECT文を実行し、結果をSTRING型でElastiCache(Redis)に保存する。
planet(1)は、ID=1のレコードを検索する。キャッシュヒットすればその値を取得し、キャッシュミスした場合はRDS(MySQL)にSELECT文を実行し、結果をhash型でElastiCache(Redis)に保存する。
これらの2つの命令の実行の前後に日時を取得し、2つの命令の所要時間を取得する。

結果は以下の通り。

初回実行時は、ElastiCache(Redis)にデータが存在しないため、キャッシュミスとなり、RDS(MySQL)へのクエリ及びElastiCache(Redis)への書き込みが発生する。
2回目実行時には、ElastiCache(Redis)にキャッシュが存在するため、そのまま結果が得られる。
1回目の実行時間(キャッシュミス)が0.02秒、2回目の実行時間(キャッシュヒット)が0.01秒となり、キャッシュがあったほうが速くはなっている。

5.4.3 ElastiCache(Redis)动作确认结果(确认ElastiCache(Redis)一侧)

    • 上記のスクリプトの実行時、ElastiCache(Redis)側はどうなっているのかを併せて確認する。

 

    ElastiCache(Redis)には、EC2インスタンスにインストールした redis-cli を用いて接続する。
[ec2-user@ip-10-0-1-50 ~]$ redis-cli -h [Redisエンドポイント]:6379
    スクリプト初回実行前の、データが載っていない時は以下の状態となる。
mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> keys *
(empty list or set)

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> type "SELECT * FROM planet"
none

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> type "planet:1"
none
    スクリプト初回実行後の、データが載っている時は以下の状態となる。
mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> keys *
1) "planet:1"
2) "SELECT * FROM planet"

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> type "SELECT * FROM planet"
string

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> type "planet:1"
hash

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> get "SELECT * FROM planet"
"[{\"id\": 1, \"name\": \"Mercury\"}, {\"id\": 2, \"name\": \"Venus\"}, {\"id\": 3, \"name\": \"Earth\"}, {\"id\": 4, \"name\": \"Mars\"}, {\"id\": 5, \"name\": \"Jupiter\"}, {\"id\": 6, \"name\": \"Saturn\"}, {\"id\": 7, \"name\": \"Uranus\"}, {\"id\": 8, \"name\": \"Neptune\"}]"

mksamba-qiita-redis-001.xxxxx.xxxx.xxxxx.cache.amazonaws.com:6379> hgetall "planet:1"
1) "id"
2) "1"
3) "name"
4) "Mercury"
    • スクリプトによって実行された2つの命令(fetch(“SELECT * FROM planet”), planet(1))の結果がそれぞれstring型とhash型で保存されていることが確認できる。なお、今回はTTL=60にしているので、60秒間でこれらのデータは自動消去される。

 

    「keys *」は登録されているKey一覧を取得する。「type」は、Keyの種別を取得する。「get」はstring型の値の取得、「hgetall」はhash型の値の取得を行う。

5. 参考网站

    • 「Redis に保存されてる値を見ようと思った時に覚えておきたい redis コマンド」

Redisのデータの中身を見たくて操作方法を探していたが、このサイトがコマンドの一覧として使いやすかった。

「あと2時間でElastiCacheのメモリが枯渇!そのときあなたは何をしますか?」

ElastiCache+RDSのユースケースを探していたら出てきた、Akatsuki社のゲームインフラのトラブルシューティングの記事。これくらいのことができるように頑張っていきたい。

广告
将在 10 秒后关闭
bannerAds