AWS 入门

使用 Amazon ElastiCache for Redis 提升 MySQL 数据库性能

模块 4:缓存和最佳实践

连接到 Redis,学习存储和检索数据的两种技术。

学习内容

  • 存储和检索数据的两种技术(在选择要在应用程序中使用哪种方法时,请根据数据访问模式,选择能够简化架构的方法。)

 时长

60 分钟

操作步骤

  • 4.1 — 在左侧栏中选择 Redis。

    4.2 — 选择您为本教程创建的 Redis 集群。

    4.3 — 复制 Primary Endpoint(主要端点)。

    在这些示例中,每次提到端点时,都应使用配置端点的主机名。

    4.4 — 在 EC2 实例中,进入 Python 交互式解释器:

    语法:shell

    $ python

    4.5 — 现在,运行以下命令来测试与 Redis 节点的连接。

    如果命令挂起,请查看示例后面的说明。

    语法:python

    syntax: python
    >>> import redis
    >>> client = redis.Redis.from_url('redis://endpoint:6379')
    >>> client.ping()
    
    True

    注意:如果命令挂起,很可能是被安全组设置阻止了。请确保 EC2 实例有权访问分配给 ElastiCache 实例的安全组。例如,假设 EC2 实例已分配到默认安全组。现在,您可以修改 Amazon ElastiCache 实例的安全组,并添加一条自定义 TCP 规则,允许来自默认安全组中任何实例的 6379 端口连接:

    在 Source(来源)中,您可以输入安全组的名称,然后点击 Security Group ID(安全组 ID)。如果您需要了解有关安全组的更多信息,那么可以查阅此文档安全组规则参考资料

  • 在该存储库中,您可以找到一些能在 EC2 实例中运行的 Python 代码。但首先,您需要配置一些环境变量:

    语法:shell

    $ export REDIS_URL=redis://your_redis_endpoint:6379/
    $ export DB_HOST=your_mysql_endpoint
    $ export DB_USER=admin
    $ export DB_PASS=your_admin_password
    $ export DB_NAME=tutorial

    请注意,mysql_endpoint、redis_endpoint 和 password 的值应为您在之前步骤中保存的值。

  • 示例代码中实现的两种方法中,第一种是通过缓存 SQL 查询结果的序列化表示来实现的。以下 Python 代码片段展示了其逻辑:

    语法:python

    def fetch(sql):
    
      result = cache.get(sql)
      if result:
        return deserialize(result)
      else:
        result = db.query(sql)
        cache.setex(sql, ttl, serialize(result))
        return result

    首先,将 SQL 语句用作 Redis 中的键,并检查缓存中是否存在对应的值。如果值不存在,则使用 SQL 语句查询数据库。数据库查询的结果存储在 Redis 中。ttl 变量必须设置为一个合理的值,具体取决于应用程序的性质。当 ttl 过期时,Redis 会驱逐该键并释放相关内存。这段代码可以在教程代码仓库中找到,您可以按原样运行,但如果想查看某个时间点变量的值,也可以随意添加 print 语句。

    就策略而言,这种方法的缺点是,当数据库中的数据被修改时,如果之前的结果已被缓存且其 ttl 尚未过期,则更改不会自动反映给用户。

    以下是使用 fetch 函数的示例:

    语法:python

    print(fetch("SELECT * FROM planet"))

    结果应如下所示:

    语法:python

    [{'id': 10, 'name': 'Mercury'},
     {'id': 11, 'name': 'Venus'},
     {'id': 12, 'name': 'Earth'},
     {'id': 13, 'name': 'Mars'},
     {'id': 14, 'name': 'Jupiter'},
     {'id': 15, 'name': 'Saturn'},
     {'id': 16, 'name': 'Uranus'},
     {'id': 17, 'name': 'Neptune'}]

    当然,这是一个非常基本的示例,但通过实现这种缓存模式,无论结果来自缓存还是直接来自数据库,应用程序都能获得很大的性能提升。

  • 您将实现的第二个示例将数据库记录映射到 Redis 哈希:

    语法:python

    def planet(id):
    
      key = "planet:" + str(id)
      result = cache.hgetall(key)
    
      if result:
          return result
    
      else:
          sql = "SELECT `id`, `name` FROM `planet` WHERE `id`=%s"
          result = db_record(sql, (id,))
    
          if result:
              cache.hmset(key, result)
              cache.expire(key, ttl)
          return result

    Redis 中的键空间是扁平的,但是可以使用冒号分隔的字符串来模拟结构。在此示例中,ID 为 1 的记录的键是 planet:1。虽然这个代码片段足以展示一种常见模式,但还可以进行更多抽象,例如一个模块可以负责生成键,另一个模块可以负责构建 SQL 字符串等。此外,您使用的编程语言中很可能有为此而构建的工具。

    此示例是从缓存或数据库中检索记录,而与之相仿,也可以写一个函数负责将对象持久化到数据库中。

  • 在这两个示例中,您使用了存留时间 (ttl),该时间到期后 Redis 会驱逐该键。虽然在大多数情况下这已经足够了,但您可能会希望尽快从缓存中删除过期数据。如果这符合您的使用场景,请务必查看其他选项,例如直写式缓存策略。本教程结尾提供了一些链接,可供您获取更多信息。值得一提的是,虽然示例使用了 EXPIRE 命令,但 Redis 还提供了 EXPIREAT 命令,可以让您指定应该驱逐键的精确日期和时间。它接受一个绝对的 Unix 时间戳作为参数(即自 1970 年 1 月 1 日以来经过的秒数)。

  • 当数据量超过配置的最大内存设置时,Redis 会根据选定的驱逐策略采取不同的响应方式。默认情况下,ElastiCache for Redis 被配置为从内存中删除设置了 ttl 的最近最少使用的键。驱逐策略参数名为 maxmemory-policy,而 ElastiCache 中的默认值为 volatile-lru。对于此类使用场景,另一个有趣的选项是 volatile-ttl 策略,它指示 Redis 通过删除具有最短 ttl 的键来回收内存。

  • 实现此策略后,请务必测试您的应用程序,以找到 ttl 的最佳值和最佳驱逐策略。此外,请分别使用空缓存和满缓存检查应用程序的性能。

清理资源和后续步骤