模块 4:缓存和最佳实践
连接到 Redis,学习存储和检索数据的两种技术。
学习内容
- 存储和检索数据的两种技术(在选择要在应用程序中使用哪种方法时,请根据数据访问模式,选择能够简化架构的方法。)
时长
60 分钟
使用的服务
操作步骤
-
测试与 Redis 链接
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
-
配置环境
$ 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 查询结果
示例代码中实现的两种方法中,第一种是通过缓存 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 哈希
您将实现的第二个示例将数据库记录映射到 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 配置为缓存
当数据量超过配置的最大内存设置时,Redis 会根据选定的驱逐策略采取不同的响应方式。默认情况下,ElastiCache for Redis 被配置为从内存中删除设置了 ttl 的最近最少使用的键。驱逐策略参数名为 maxmemory-policy,而 ElastiCache 中的默认值为 volatile-lru。对于此类使用场景,另一个有趣的选项是 volatile-ttl 策略,它指示 Redis 通过删除具有最短 ttl 的键来回收内存。
-
测试实现此策略后,请务必测试您的应用程序,以找到 ttl 的最佳值和最佳驱逐策略。此外,请分别使用空缓存和满缓存检查应用程序的性能。