学习

如何使用 Redis 实现 Write through 缓存策略

Prasan Kumar
作者
Prasan Kumar, Redis 技术解决方案开发者
Will Johnston
作者
Will Johnston, Redis 开发者增长经理

什么是 Write-through 缓存?#

想象您构建了一个电影流媒体应用。您使用 PostgreSQL 作为数据存储,并随着需要扩展而使用 Redis 实现了缓存。然而,现在您遇到了更新用户资料或订阅反映缓慢的问题。

例如,当用户购买或修改订阅时,用户期望更改立即反映在其账户上,以便新的订阅允许观看所需的电影/节目。因此,您需要一种快速提供用户数据强一致性的方法。在这种情况下,您需要的就是所谓的“Write-through 模式”。

使用 Write-through 模式,应用程序每次将数据写入缓存时,也会更新数据库中的记录,这与 Write behind 不同,Write-through 模式中线程会等待直到数据写入数据库完成。

下面是应用程序的 Write-through 模式图

该模式的工作原理如下

  1. 1.应用程序读写数据到 Redis。
  2. 2.Redis 同步/立即将任何更改的数据同步到 PostgreSQL 数据库。

注意:在收到主数据库的响应之前, Redis 服务器会被阻塞

有两种相关的写入模式,它们的主要区别如下

Write Behind

Write through

异步同步数据

同步/立即同步数据

缓存和主记录系统(数据库)之间的数据在 短时间内可能不一致

缓存和主记录系统(数据库)之间的数据始终 一致

了解更多关于 Write behind 模式

为什么应该使用 Redis 实现 Write-through 缓存#

使用 Redis 实现 Write-through 缓存确保(关键)数据缓存始终与数据库保持同步,提供了 强一致性 并 提高了应用性能

考虑以下不同应用的场景

  • 电商应用:在电商应用中,可以使用 Write-through 缓存来确保产品库存的一致性。每当客户购买产品时,库存数量应立即更新以避免超卖。Redis 可用于缓存库存数量,并且每次对数量的更新都可以 Write-through 到数据库。这确保了缓存中的库存数量始终是最新的,并且客户无法购买缺货商品。
  • 银行应用:在银行应用中,可以使用 Write-through 缓存来确保账户余额的一致性。每当发生交易时,账户余额应立即更新以避免透支或其他问题。Redis 可用于缓存账户余额,并且每笔交易都可以 Write-through 到数据库。这确保了缓存中的余额始终是最新的,并且可以以强一致性处理交易。
  • 在线游戏平台:假设您有一个在线游戏平台,用户可以在其中相互对战。使用 Write-through 缓存,对用户分数或游戏状态所做的任何更改都将保存到数据库中,同时也会缓存在 Redis 中。这确保了随后对该用户分数或游戏状态的任何读取都将首先命中缓存。这有助于减轻数据库负载,并确保向用户显示的游戏状态始终是最新的。
  • 索赔处理系统:在保险索赔处理系统中,索赔数据需要在不同的系统和应用之间保持一致和最新。通过 Redis 中的 Write-through 缓存,新的索赔数据可以同时写入数据库和 Redis 缓存。这确保了不同的应用始终拥有关于索赔的最新信息,使理赔员更容易访问所需信息,从而更快、更高效地处理索赔。
  • 医疗保健应用:在医疗保健应用中,患者数据需要在不同的系统和应用之间保持一致和最新。通过 Redis 中的 Write-through 缓存,更新的患者数据可以同时写入数据库和 Redis 缓存,确保不同的应用始终拥有最新的患者信息。这可以通过向医疗保健提供者提供准确及时的信息来帮助改善患者护理。
  • 社交媒体应用:在社交媒体应用中,可以使用 Write-through 缓存来确保用户资料的一致性。每当用户更新其资料时,更改应立即反映,以避免向其他用户显示过时的信息。Redis 可用于缓存用户资料,并且每次更新都可以 Write-through 到数据库。这确保了缓存中的资料信息始终是最新的,并且用户可以看到关于彼此的准确信息。

使用 RedisGears 实现 Write-through 缓存的 Redis 可编程性#

提示

如果您已经熟悉 RedisGears,可以跳过本节)

什么是 RedisGears?#

RedisGears 是一个可编程的无服务器引擎,用于事务、批处理和事件驱动的数据处理,允许用户在 Redis 中存储的数据上编写和运行自己的函数。

函数可以用不同的语言实现,包括 Python 和 C,并可以通过 RedisGears 引擎以两种方式之一执行

  1. 1.批处理 (Batch):由 Run 操作触发,立即执行,处理现有数据
  2. 2.事件 (Event):由 Register 操作触发,由新事件触发执行,处理事件关联的数据

RedisGears 可以执行的一些批处理类型操作

  • 对 KeySpace 中的所有键或匹配特定模式的键运行操作,例如:
    • 为所有 KeyNames 加上前缀 person:
    • 删除所有值小于零的键
    • 将所有以 person: 开头的 KeyNames 写入一个集合
  • 对所有(或匹配的)键运行一系列操作,其中一个操作的输出是另一个操作的输入,例如
    • 查找所有带有前缀 person: 的键(假设它们都是 hash 类型)
    • 将用户的 days_old 增加 1,然后按年龄组(10-20、20-30 等)对它们求和
    • 将今天的统计信息添加到每个客户端的有序集合中,计算最近 7 天的平均值并将计算结果保存在字符串中

RedisGears 可以执行的一些事件类型操作

  • RedisGears 还可以注册事件监听器,每当监视的键发生更改时就会触发函数执行,例如
    • 监听所有键上的所有操作,并维护 KeySpace 中所有 KeyNames 的列表
    • 监听带有前缀 I-AM-IMPORTANT: 的键上的 DEL 操作,并将其异步转储到“已删除键”日志文件中
    • 监听带有前缀 player: 的键的元素分数上的所有 HINCRBY 操作,并在分数达到 1000 时同步更新用户级别

如何使用 RedisGears?#

运行 Docker 容器

docker run -p 6379:6379 redislabs/redisgears:latest

例如,对于一个非常简单的例子,它列出 Redis 数据库中所有带有前缀 person: 的键,创建以下 python 脚本并将其命名为 hello_gears.py

gb = GearsBuilder() gb.run('person:*')

执行您的函数

docker exec -i redisgears redis-cli RG.PYEXECUTE "`cat hello_gears.py`"

使用 gears-cli#

gears-cli 工具提供了一种更简单的方式来执行 RedisGears 函数,特别是当您需要传递一些参数时。

它用 Python 编写,可以使用 pip 进行安装

pip install gears-cli
gears-cli hello_gears.py REQUIREMENTS rgsync

用法

gears-cli --help
usage: gears-cli [-h] [--host HOST] [--port PORT]
[--requirements REQUIREMENTS] [--password PASSWORD] path [extra_args [extra_args ...]]

RedisGears 参考资料#

使用 Write-through 模式进行 Redis 编程#

对于我们的示例代码,我们将演示如何将 users 写入 Redis,然后 Write-through 到 PostgreSQL。使用下面的 docker-compose.yml 文件来设置所需环境:

docker-compose.yml
version: '3.9'
services:
  redis:
    container_name: redis
    image: 'redislabs/redismod:latest'
    ports:
      - 6379:6379
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
  postgres:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: password
      POSTGRES_DB: example
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

要运行 docker-compose 文件,请运行以下命令

$ docker compose up -d

这将创建一个 Redis 服务器、一个 PostgreSQL 服务器和一个 Adminer 服务器。Adminer 是一个基于 Web 的数据库管理工具,允许您查看和编辑数据库中的数据。

接下来,在浏览器中打开 https://:8080/?pgsql=postgres&username=root&db=example&ns=public&sql=。您需要输入密码(在上面的示例中是 password),

然后您将被带到 SQL 命令页面。运行以下 SQL 命令创建表

users.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(255) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    date_of_birth DATE,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

开发者需要在使用 Write-through 模式(将数据从 Redis 同步到主记录系统)之前将一些代码(例如本例中的 python)加载到 Redis 服务器。Redis 服务器有一个 RedisGears 模块,可以解释 python 代码并将数据从 Redis 同步到主记录系统。

现在,我们需要创建一个 RedisGears Recipe,它将 Write-through 到 PostgreSQL 数据库。以下 Python 代码将 Write-through 到 PostgreSQL 数据库

write-through.py
from rgsync import RGWriteThrough
from rgsync.Connectors import PostgresConnector, PostgresConnection

'''
Create Postgres connection object
'''
connection = PostgresConnection('root', 'password', 'postgres:5432/example')

'''
Create Postgres users connector
'''
usersConnector = PostgresConnector(connection, 'users', 'id')

usersMappings = {
    'username': 'username',
    'email': 'email',
    'pwhash': 'password_hash',
    'first': 'first_name',
    'last': 'last_name',
    'dob': 'date_of_birth',
    'created_at': 'created_at',
    'updated_at': 'updated_at',
}

RGWriteThrough(GB, keysPrefix='__',     mappings=usersMappings,
               connector=usersConnector, name='UsersWriteThrough', version='99.99.99')

确保您创建了文件“write-through.py”,因为后续说明会用到它。出于本示例的目的,我们展示了如何将 Redis hash 字段映射到 PostgreSQL 表列。 RGWriteThrough 函数接受 usersMapping,其中键是 Redis hash 键,值是 PostgreSQL 表列。

什么是 REDISGEARS RECIPE?

一个由 RedisGears 函数及其可能拥有的任何依赖项组成的集合,用于实现高层功能目的,称为 recipe。示例:上面 Python 代码中的 "RGJSONWriteThrough" 函数

该 Python 文件为了正常工作需要一些依赖项。下面是包含这些依赖项的 requirements.txt 文件,请将其与 "write-through.py" 文件一同创建。

requirements.txt
rgsync
psycopg2-binary
cryptography

有两种方法(gears CLI 和 RG.PYEXECUTE)可以将该 Python 文件加载到 Redis 服务器中

  1. 1.使用 gears 命令行界面 (CLI)

关于 Gears CLI 的更多信息请参见 gears-cli 和 rgsync

# install
pip install gears-cli

要使用 gears-cli 运行我们的 write-through recipe,需要运行以下命令:

$ gears-cli run --host localhost --port 6379 write-through.py --requirements requirements.txt

您应该会收到一个响应,显示 "OK"。这样您就知道已成功将 Python 文件加载到 Redis 服务器中。

提示

如果您使用的是 Windows,建议使用 WSL 安装和使用 gears-cli。

2. 使用 Redis 命令行中的 RG.PYEXECUTE。

# Via redis cli
RG.PYEXECUTE 'pythonCode' REQUIREMENTS rgsync psycopg2-binary cryptography
提示

RG.PYEXECUTE 命令也可以从 Node.js 代码中执行(有关更多详细信息,请参阅 示例 Node 文件

提示

在 Redis Gears GitHub 仓库 中找到更多示例。

使用 RedisInsight 验证 write-through 模式#

提示

RedisInsight 是用于在 Redis 中查看数据的免费 Redis GUI。 点击此处下载。

下一步是验证 RedisGears 是否正在 Redis 和 PostgreSQL 之间同步数据。请注意,在我们的 Python 文件中,我们为键指定了一个前缀。在这种情况下,我们指定了 __ 作为前缀,users 作为表,以及 id 作为唯一标识符。这指示 RedisGears 查找以下键格式:__{users:<id>}。尝试在 Redis 命令行中运行以下命令

hset __{users:1} username john email [email protected] pwhash d1e8a70b5ccab1dc2f56bbf7e99f064a660c08e361a35751b9c483c88943d082 first John last Doe dob 1990-01-01 created_at 2023-04-20 updated_at 2023-04-20

检查 RedisInsight 以验证哈希值是否已进入 Redis。RedisGears 处理完 __{users:1} 键后,该键将从 Redis 中删除,并被 users:1 键替换。检查 RedisInsight 以验证 users:1 键是否存在于 Redis 中。

接下来,通过打开 Adminer 中的 users 选择页面 来确认用户也已插入到 PostgreSQL 中。您应该会看到用户已插入到表中。

这就是如何使用 RedisGears 将数据 write through 到 PostgreSQL 的方法,到目前为止,我们只添加了一个哈希键。您还可以更新特定的哈希字段,这将在您的 PostgreSQL 数据库中体现。运行以下命令来更新 username 字段:

> hset __{users:1} username bar

在 RedisInsight 中,验证 username 字段已更新

现在进入 Adminer 检查 username 字段。您应该会看到它已更新为 bar

准备好使用 Redis 进行 write-through 缓存了吗?#

您现在知道了如何使用 Redis 进行 write-through 缓存。可以根据需要使用不同的策略/模式逐步采用 Redis。有关缓存主题的更多资源,请查看以下链接

附加资源#