微服务复杂查询之缓存策略

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 微服务复杂查询之缓存策略

在上一篇 缓存设计的好,服务基本不会倒 介绍了db层缓存,回顾一下,db层缓存主要设计可以总结为:

  • 缓存只删除不更新
  • 行记录始终只存储一份,即主键对应行记录
  • 唯一索引仅缓存主键值,不直接缓存行记录(参考mysql索引思想)
  • 防缓存穿透设计,默认一分钟,防止缓存击穿和雪崩
  • 不缓存多行记录

前言

在大型业务系统中,通过对持久层添加缓存,对于大多数单行记录查询,相信缓存能够帮持久层减轻很大的访问压力,但在实际业务中,数据读取不仅仅只是单行记录,面对大量多行记录的查询,这对持久层也会造成不小的访问压力,除此之外,像秒杀系统、选课系统这种高并发的场景,单纯靠持久层的缓存是不现实的,本文我们来介绍 go-zero 实践中的缓存设计之biz cache

适用场景举例

  • 选课系统
  • 内容社交系统
  • 秒杀

像这些系统,我们可以在业务层再增加一层缓存来存储系统中的关键信息,如选课系统中学生选课信息,课程剩余名额;内容社交系统中某一段时间之间的内容信息等。

接下来,我们以内容社交系统来进行举例说明。

在内容社交系统中,我们一般是先查询一批内容列表,然后点击某条内容查看详情,

在没有添加biz缓存前,内容信息的查询流程图应该为:

从上图以及上一篇文章 缓存设计的好,服务基本不会倒 中我们可以知道,内容列表的获取是没办法依赖缓存的, 如果我们在业务层添加一层缓存用来存储列表中的关键信息(甚至完整信息),那么多行记录的访问不再是一个问题,这就是biz redis要做的事情。接下来我们来看一下设计方案,假设内容系统中单行记录包含以下字段

字段名称 字段类型 备注
id string 内容id
title string 标题
content string 详细内容
createTime time.Time 创建时间

我们的目标是获取一批内容列表,而尽量避免内容列表走db造成访问压力,首先我们采用redis的sort set数据结构来存储,根需要存储的字段信息量,有两种redis存储方案:

  • 缓存局部信息 1724681080445.png 对其关键字段信息(如:id等)按照一定规则压缩,并存储,score我们用createTime毫秒值(时间值相等这里不讨论),这种存储方案的好处是节约redis存储空间, 那另一方面,缺点就是需要对列表详细内容进行二次回查(但这次回查是会利用到持久层的行记录缓存的)
  • 缓存完整信息 1724681100306.png 对发布的所有内容按照一定规则压缩后均进行存储,同样score我们还是用createTime毫秒值,这种存储方案的好处是业务的增、删、查、改均走reids,而db层这时候 就可以不用考虑行记录缓存了,持久层仅提供数据备份和恢复使用,从另一方面来看,其缺点也很明显,需要的存储空间、配置要求更高,费用也会随之增大。

示例代码:

type Content struct {
    Id         string    `json:"id"`
    Title      string    `json:"title"`
    Content    string    `json:"content"`
    CreateTime time.Time `json:"create_time"`
}
const bizContentCacheKey = `biz#content#cache`
// AddContent 提供内容存储
func AddContent(r redis.Redis, c *Content) error {
    v := compress(c)
    _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v)
    return err
}
// DelContent 提供内容删除
func DelContent(r redis.Redis, c *Content) error {
    v := compress(c)
    _, err := r.Zrem(bizContentCacheKey, v)
    return err
}
// 内容压缩
func compress(c *Content) string {
    // todo: do it yourself
    var ret string
    return ret
}
// 内容解压
func uncompress(v string) *Content {
 // todo: do it yourself
 var ret Content
 return &ret
}
// ListByRangeTime提供根据时间段进行数据查询
func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) {
 kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6)
 if err != nil {
  return nil, err
 }
 var list []*Content
 for _, kv := range kvs {
  data := uncompress(kv.Key)
  list = append(list, data)
 }
 return list, nil
}

在以上例子中,redis是没有设置过期时间的,我们将增、删、改、查操作均同步到redis,我们认为内容社交系统的列表访问请求是比较高的情况下才做这样的方案设计, 除此之外,还有一些数据访问,没有像内容设计系统这么频繁的访问, 可能是某一时间段内访问量突如其来的增加,之后可能很长一段时间才会再访问一次,以此间隔,或者说不会再访问了,面对这种场景,我们又该如何考虑缓存的设计呢?在 go-zero 内部实践中,有两种方案可以解决这种问题:

  • 增加内存缓存:通过内存缓存来存储当前可能突发访问量比较大的数据,常用的存储方案采用map数据结构来存储,map数据存储实现比较简单,但缓存过期处理则需要增加定时器来处理,go-zero库中的 Cache 是专门用于内存缓存管理的
  • 采用 biz redis,并设置合理的过期时间

总结

以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务监控来衡量何时需要引入biz cache。

项目地址

https://github.com/tal-tech/go-zero

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
20 5
|
6天前
|
存储 NoSQL 分布式数据库
微服务架构下的数据库设计与优化策略####
本文深入探讨了在微服务架构下,如何进行高效的数据库设计与优化,以确保系统的可扩展性、低延迟与高并发处理能力。不同于传统单一数据库模式,微服务架构要求更细粒度的服务划分,这对数据库设计提出了新的挑战。本文将从数据库分片、复制、事务管理及性能调优等方面阐述最佳实践,旨在为开发者提供一套系统性的解决方案框架。 ####
|
4天前
|
Kubernetes 负载均衡 Cloud Native
云原生架构下的微服务治理策略
随着云原生技术的不断成熟,微服务架构已成为现代应用开发的主流选择。本文探讨了在云原生环境下实施微服务治理的策略和方法,重点分析了服务发现、负载均衡、故障恢复和配置管理等关键技术点,以及如何利用Kubernetes等容器编排工具来优化微服务的部署和管理。文章旨在为开发者提供一套实用的微服务治理框架,帮助其在复杂的云环境中构建高效、可靠的分布式系统。
18 5
|
4天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####
|
9天前
|
缓存 负载均衡 监控
微服务架构下的接口性能优化策略####
在当今快速迭代的软件开发领域,微服务架构以其灵活性和可扩展性成为众多企业的首选。然而,随着系统复杂性的增加,接口性能问题日益凸显,成为制约用户体验与系统稳定性的关键因素。本文旨在探讨微服务架构下接口性能优化的有效策略,通过具体案例分析,揭示从代码层面到系统架构层面的全方位优化路径,为开发者提供实战指南。 ####
|
9天前
|
消息中间件 数据库 云计算
微服务架构下的数据库事务管理策略####
在微服务架构中,传统的单体应用被拆分为多个独立的服务单元,每个服务维护自己的数据库实例。这种设计提高了系统的可扩展性和灵活性,但同时也带来了分布式环境下事务管理的复杂性。本文探讨了微服务架构下数据库事务的挑战,并深入分析了几种主流的事务管理策略,包括Saga模式、两阶段提交(2PC)以及基于消息的最终一致性方案,旨在为开发者提供一套适应不同业务场景的事务处理框架。 ####
|
11天前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;
|
15天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
52 4
|
16天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
9天前
|
设计模式 存储 缓存
微服务架构下的数据库设计策略
本文探讨了在微服务架构中进行数据库设计时,如何平衡数据的一致性、独立性与系统整体性能之间的关系。文章首先介绍了微服务架构的基本概念及其对数据库设计的影响,随后深入分析了三种主流的数据库设计模式——集中式、去中心化和混合模式,并结合实际案例讨论了它们的适用场景与优缺点。此外,还提出了一系列最佳实践建议,旨在帮助开发者更好地应对微服务环境下的数据管理挑战。