更快的 Redis:客户端库支持客户端侧缓存

1. 原创声明

原文地址:https://redis.io/blog/faster-redis-client-library-support-for-client-side-caching/


2. 概述

比 Redis 更快不容易,但现在可以读取访问最频繁的数据,减少延迟,并且更有效地利用资源。官方 Redis 客户端库已支持客户端侧缓存。

Redis 社区版从版本 6 开始支持客户端侧缓存。但到目前为止,需要商用的第三方客户端库才能使用,因为 Redis 未在官方客户端库中提供支持。现在,可以直接使用 Redis 官方的开源客户端库使用近缓存,仅需几行代码,即可在建立连接时启用缓存。

下面回顾什么是近缓存以及使用近缓存的动机,看看客户端侧缓存如何加快数据访问。

Redis 的客户端-服务端模型意味着性能受网络限制。每个操作都需要往返通信,网络可能影响性能,特别是在高吞吐系统中或存在网络延迟问题时。

减少网络开销,同时提高应用程序性能的一种有效方法是通过客户端侧缓存,也被称为近缓存near cache)。该技术支持从运行(Redis)客户端的应用服务上的缓存直接服务频繁的读取操作。启用客户端侧缓存后,客户端应用程序将使用后端应用程序缓存的数据,从而减少网络流量和延迟。

以下是其作用:


3. 工作原理

下面通过示例说明客户端侧缓存的工作方式。

  1. SET foo bar
    1. 数据存储在服务端
  1. GET foo
    1. 命令被发送到服务端
    1. 客户端在本地内存中缓存响应
  1. GET foo
    1. 客户端从本地内存获取 bar

当任意客户端修改被追踪的数据时,服务端将向当前追踪该数据的所有客户端发布失效消息。

  1. SET foo qux
    1. 客户端 A 修改数据
  1. 失效消息及缓存移除
    1. 客户端 B 在最初用于请求该数据的同一连接上收到实效消息
    1. 客户端 B 从缓存中移除数据
  1. GET foo
    1. 客户端 B 再次请求数据
    1. 客户端 B 在本地内存缓存响应
  1. GET foo
    1. 客户端 B 从本地内存获取 qux

4. 测试客户端侧缓存

客户端侧缓存需要 RESP3 协议,因为需要推送通知。在设置连接时,需要确保选择正确的协议版本。

为简化所有 Redis 产品的开发体验,需要 Redis CE 7.4,Redis Stack 7.4 或更新版本。也可以测试最新的 Redis 8 M01 里程碑版本。客户端侧缓存与 Redis Software、Redis Cloud 和 Azure 的 Redis Enterprise 完全兼容,该功能目前处于预览阶段,将很快推出。

如前所述只需几行代码即可很容易地设置客户端侧缓存,下面就是证明。下面的示例不会复杂化客户端设置。仅需在建立连接时启用缓存,如下面的 Python 示例所示。

r = redis.Redis(
protocol=3,
cache_config=CacheConfig(),
decode_responses=True
)
 
r.set("city", "New York")
cityNameAttempt1 = r.get("city") # Retrieved from Redis server and cached
cityNameAttempt2 = r.get("city") # Retrieved from cache

第一次检索键“city”时,将从数据库读取,并且将其缓存在调用进程的内存中。后续读取将透明地由本地缓存提供,因此具有最小的延迟,并且不会给数据库带来额外的负载。

如果使用 Java,那么由 Jedis 完成所有繁重工作。可以像下面这样创建连接,启用客户端侧缓存。

HostAndPort endpoint = new HostAndPort("localhost", 6379);
DefaultJedisClientConfig config = DefaultJedisClientConfig.builder().protocol(RedisProtocol.RESP3).build();
CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build();
UnifiedJedis client = new UnifiedJedis(endpoint, config, cacheConfig);
 
Map<String, String> usr = new HashMap<>();
usr.put("id", "Johnny");
usr.put("first", "John");
usr.put("last", "Doe");
client.hset("session:e788eeb2", usr);
// Retrieved from Redis server and cached
client.hgetAll("session:e788eeb2"); 
// Retrieved from cache
client.hgetAll("session:e788eeb2"); 
// Peek into the cacheCache cache = client.getCache();
System.out.println(cache.getSize());
System.out.println(cache.getCacheEntries());
System.out.println(cache.getStats());

5. 问答

5.1. 客户端库支持连接池吗?

可以为独立连接和连接池启用客户端侧缓存。也支持 Cluster 和 Sentinel API。

5.2. 应该何时启用缓存?

每当缓存键的值被修改时,Redis 将向缓存该键的所有客户端推送失效消息。告诉客户端刷新该键的无效本地缓存值。该行为意味着需要在本地缓存命中和失效消息之间进行权衡:本地缓存命中率大于失效消息率的键是本地追踪和缓存的最佳候选。

比如表示为字符串或嵌入到 hash、排行榜等的计数器 - 可以在标准连接上读取这样的键,以防止过量的失效消息。

如果不希望缓存某些键,该如何做?

为控制缓存的键,实例化未启用客户端侧缓存的标准连接。

5.3. 缓存哪些数据?

客户端缓存发送到数据库的命令的规范化版本以及返回结果。缓存除以下命令外的所有只读命令:

5.4. 如何预估本地缓存的内存消耗?

缓存条目的大小是可变的,因此本地缓存的内存消耗与工作负载和存储的数据大小有关。客户端支持检查缓存,因此关于本地存储数据的相关统计信息支持基准测试。

5.5. 如何验证缓存是否适用于命令?

如何希望知道底层发生什么,那么在 Redis Insight 中启动分析器,查看发送的命令,或者从终端在 redis-cli 会话中使用 MONITOR 命令。


6. 使用客户端库

阅读 https://redis.io/docs/latest/develop/connect/clients/client-side-caching/,获取有关客户端侧缓存的更多信息,使用 GA 版本的客户端库。官方承诺将很快为其他语言的客户端库添加客户端侧缓存。