本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
使用只读副本的最佳实践
许多应用程序,例如会话存储、排行榜和推荐引擎,都需要高可用性,并且处理的读取操作要比写入操作多得多。这些应用程序通常可以容忍稍微陈旧的数据(最终一致性),这意味着如果不同的用户暂时看到相同数据的版本略有不同,这是可以接受的。例如:
缓存的查询结果通常可以容忍稍微陈旧的数据,特别是对于真实来源在外部的缓存旁模式。
在游戏排行榜中,分数更新延迟几秒钟通常不会对用户体验产生重大影响。
对于会话存储,跨副本传播会话数据时出现一些轻微的延迟很少影响应用程序的功能。
推荐引擎通常使用历史数据分析,因此实时一致性不那么重要。
最终一致性意味着复制过程完成后(通常在毫秒内),所有副本节点最终都将返回相同的数据。对于此类用例,实现只读副本是减少从 ElastiCache实例读取时延迟的有效策略。
在 Amazon 中使用只读副本 ElastiCache 可通过以下方式显著提高性能:
增强的读取可扩展性
在多个副本节点之间分配读取操作
从主节点卸载读取流量
通过处理来自地理位置较近的副本的请求来减少读取延迟
优化了主节点性能
将主节点资源专门用于写入操作
减少主节点上的连接开销
提高写入性能并在流量高峰期保持更好的响应时间
在 ElastiCache 无服务器中使用从副本读取
ElastiCache serverless 为不同的一致性要求提供了两个不同的端点。这两个端点使用相同的 DNS 名称但端口不同。要使用该 read-from-replica端口,您必须通过配置您的 VPC 的安全组和网络访问控制列表来授权从您的客户端应用程序访问这两个端口。
主终端节点(端口 6379)
用于需要即时一致性的操作
保证读取最多的 up-to-date数据
最适合关键事务和写入操作
写入操作所必需的
示例:
test-12345.serverless.use1.cache.amazonaws.com:6379
延迟优化端点(端口 6380)
针对可以容忍最终一致性的读取操作进行了优化
如果可能, ElastiCache Serverless 会自动将读取请求路由到客户端本地可用区中的副本节点。这种优化避免了从不同可用区域的节点检索数据时产生的额外网络延迟,从而降低了延迟。
ElastiCache 如果本地节点不可用,serverless 会自动选择其他区域中的可用节点
示例:
test-12345.serverless.use1.cache.amazonaws.com:6380
如果您提供从副本读取配置,则像 Glide 和 Lettuce 这样的客户端将自动检测读取并将其路由到延迟优化的端点。如果您的客户端不支持路由配置(例如 valkey-java 和较早的 jedis 版本),则必须定义正确的端口和客户端配置以从副本读取。
在 ElastiCache Serverless 中连接到只读副本-Valkey 和 Glide
以下代码片段显示了如何在 Valkey glide 库中为 ElastiCache Serverless 配置从副本读取。您无需指定从副本读取的端口,但需要配置路由配置ReadFrom.PREFER_REPLICA
。
package glide.examples; import glide.api.GlideClusterClient; import glide.api.logging.Logger; import glide.api.models.configuration.GlideClusterClientConfiguration; import glide.api.models.configuration.NodeAddress; import glide.api.models.exceptions.ClosingException; import glide.api.models.exceptions.ConnectionException; import glide.api.models.exceptions.TimeoutException; import glide.api.models.configuration.ReadFrom; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class ClusterExample { public static void main(String[] args) { // Set logger configuration Logger.setLoggerConfig(Logger.Level.INFO); GlideClusterClient client = null; try { System.out.println("Connecting to Valkey Glide..."); // Configure the Glide Client GlideClusterClientConfiguration config = GlideClusterClientConfiguration.builder() .address(NodeAddress.builder() .host("your-endpoint") .port(6379) .build()) .useTLS(true) .readFrom(ReadFrom.PREFER_REPLICA) .build(); // Create the GlideClusterClient client = GlideClusterClient.createClient(config).get(); System.out.println("Connected successfully."); // Perform SET operation CompletableFuture<String> setResponse = client.set("key", "value"); System.out.println("Set key 'key' to 'value': " + setResponse.get()); // Perform GET operation CompletableFuture<String> getResponse = client.get("key"); System.out.println("Get response for 'key': " + getResponse.get()); // Perform PING operation CompletableFuture<String> pingResponse = client.ping(); System.out.println("PING response: " + pingResponse.get()); } catch (ClosingException | ConnectionException | TimeoutException | ExecutionException e) { System.err.println("An exception occurred: "); e.printStackTrace(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // Close the client connection if (client != null) { try { client.close(); System.out.println("Client connection closed."); } catch (ClosingException | ExecutionException e) { System.err.println("Error closing client: " + e.getMessage()); } } } } }