为什么

单机性能已经无法处理海量规模的数据请求,所有的动作仅仅只包含读或者写,但是读得快,写不会引起冲突这是非常重要的环节。 一旦引入了新的一层,各种琐碎堆积在一起,就形成了各种处理冲突的算法。

为了应对高并发读场景下数据库的性能瓶颈,引入缓存机制成为必然选择。常见方案是使用 Redis作为缓存层,将热点数据存储在内存中,从而大幅度降低数据库访问压力,提升读取速度。Redis可用二个节点的主从架构,甚至更复杂的集群模式。 主节点负责处理写请求,并将数据同步到从节点,而读请求可以分摊到多个从节点上,从而提高读性能和系统可用性。

  • 主从架构: 主节点负责处理写请求,并将数据同步到从节点。读请求可以分摊到多个从节点,提高读性能和系统可用性。
  • 集群模式: 将数据分散存储在多个节点上,提供更高的读写性能和数据容灾能力。

所以说,为了缓解读而诞生的缓存方案也是要考虑写的。

增加缓存带来的风险以及应对策略

缓存穿透:

  • 问题描述: 当缓存和数据库中都不存在某个数据时,每次请求都会穿透缓存,直接访问数据库,给数据库带来巨大压力。
  • 解决方法:
    缓存空值 : 对于查询结果为空的情况,依然将其缓存起来,并设置较短的过期时间,避免大量无效请求穿透到数据库。 布隆过滤器: 在缓存前置一个布隆过滤器,快速判断数据是否存在于数据库中,避免无效查询。

缓存击穿:

  • 问题描述: 当缓存中某个热点数据失效时,大量并发请求同时访问数据库,导致数据库压力骤增。
  • 解决方法: 互斥锁: 使用分布式锁,保证只有一个线程重建缓存,其他线程等待缓存重建完成。 逻辑过期: 不设置物理过期时间,而是逻辑上判断数据是否过期,并异步更新缓存。

缓存雪崩:

  • 问题描述: 当缓存中大量数据同时失效时,导致大量请求穿透到数据库,压垮数据库。
  • 解决方法: 分散缓存过期时间: 为缓存数据设置随机的过期时间,避免同一时间大量数据失效。 多级缓存: 使用多级缓存,例如 L1 缓存使用本地缓存,L2 缓存使用 Redis,可以有效降低缓存失效的影响。
  • 高可用架构 为了保证缓存的高可用性,通常采用 Redis 的主从架构或集群模式。

高可用架构下,数据一致性成为需要重点关注的问题。简单的直接写入数据库的方式已经无法满足需求,需要引入更复杂的机制来保证数据一致性。

一种常见的解决方案是使用消息队列,例如 RabbitMQ 或 Kafka。写操作可以先将数据写入消息队列,然后由专门的消费者程序异步地将数据写入数据库。这种异步写入的方式可以降低数据库压力,提高系统吞吐量。同时,消息队列可以保证消息的可靠传递,即使数据库出现故障,数据也不会丢失。

为了进一步保证数据一致性,可以结合使用分布式事务框架,例如 Seata。Seata 可以协调多个数据库的操作,保证它们要么全部成功,要么全部回滚,从而避免数据不一致的情况发生。

消息队列

消息队列可以实现异步写入,降低数据库压力,提高系统吞吐量。同时,消息队列可以保证消息的可靠传递,即使数据库出现故障,数据也不会丢失。

选择消息队列: 常用的消息队列有 RabbitMQ、Kafka 等,需要根据业务需求选择合适的队列。

  • 消息可靠性: 为了保证消息不丢失,需要设置消息持久化、确认机制等。
  • 消息幂等性: 由于网络波动等原因,消息可能会重复投递,需要保证消费者处理消息的幂等性。

分布式事务

分布式事务可以协调多个数据库的操作,保证它们要么全部成功,要么全部回滚,从而避免数据不一致的情况发生。

选择分布式事务框架: 常用的分布式事务框架有 Seata、TX-LCN 等,需要根据业务需求选择合适的框架。

  • 两阶段提交: 大多数分布式事务框架都采用两阶段提交协议,保证事务的一致性。 性能损耗: 分布式事务会带来一定的性能损耗,需要根据实际情况权衡。

异步处理:

将耗时的写操作异步化,例如用户注册后发送邮件通知,可以将发送邮件的操作放入消息队列,提升用户注册接口的响应速度。

  • 系统解耦: 将不同模块之间的依赖关系通过消息队列解耦,例如订单系统和库存系统,订单系统下单成功后发送消息到消息队列,库存系统监听消息并扣减库存,避免了系统之间的直接调用。
  • 流量削峰: 当系统面临突发流量时,可以将请求先写入消息队列,然后由消费者程序按照预设的速率进行消费,避免数据库和后端服务过载。 虽然消息队列本身不是为解决“写”而生,但在高并发、高可用的架构设计中,它常常与“写”操作搭配使用,以实现以下目标:

提高写性能: 异步写入数据库,降低数据库压力,提高系统吞吐量。 保证数据最终一致性: 通过消息队列的可靠传递机制,保证数据最终写入数据库。 总而言之,消息队列在高并发系统设计中扮演着重要的角色,可以有效解决系统解耦、异步处理、流量削峰等问题,并与其他技术(如分布式事务)结合,共同保障数据一致性和系统稳定性。

Spring Boot & Golang 技术栈对比:构建高并发、高可用分布式应用

本文档比较了使用 Spring Boot 和 Golang 技术栈构建高并发、高可用分布式应用时常用的框架/库。

功能类别 Spring Boot 技术栈 Golang 技术栈 比较
Web框架 Spring MVC Gin, Echo, Fiber Spring MVC 功能更强大,配置更复杂;Golang 框架更轻量级,性能更高,上手更快。
持久化框架 Spring Data JPA, MyBatis GORM, XORM, sqlx Spring Data JPA 提供更高级的 ORM 功能;Golang 的 ORM 框架相对简单,更注重性能和灵活性。
安全框架 Spring Security Casbin, jwt-go Spring Security 功能更全面,配置更繁琐;Golang 的安全框架更轻量级,需要更多手动配置。
缓存 Spring Cache, Redis (via Spring Data Redis) Redis (via go-redis) 功能类似,Spring Data Redis 提供更便捷的集成方式。
消息队列 Spring AMQP, Spring Kafka RabbitMQ (via amqp091-go), Kafka (via confluent-kafka-go) 功能类似,Spring 提供更高级的抽象和配置。
分布式配置中心 Spring Cloud Config etcd, Consul Spring Cloud Config 与 Spring 生态集成更紧密;Golang 的方案更灵活,需要更多代码实现。
服务注册与发现 Spring Cloud Netflix Eureka, Nacos etcd, Consul, go-micro/registry Spring Cloud 提供更完整的服务治理功能;Golang 的方案更轻量级,需要更多代码实现。
负载均衡 Spring Cloud Netflix Ribbon, Zuul go-micro/loadbalancer, Traefik Spring Cloud 提供更丰富的负载均衡策略;Golang 的方案更灵活,需要更多代码实现。
API 网关 Spring Cloud Gateway Kong, Tyk, Gin Spring Cloud Gateway 与 Spring 生态集成更紧密;Golang 的方案更灵活,需要更多代码实现。
熔断限流降级 Spring Cloud Netflix Hystrix, Resilience4j hystrix-go, go-kit/circuitbreaker Spring Cloud 提供更完整的熔断限流降级功能;Golang 的方案更轻量级,需要更多代码实现。
分布式事务 Spring Cloud Alibaba Seata Seata-Golang, DTM Spring Cloud Alibaba Seata 与 Spring 生态集成更紧密;Golang 的方案需要更多代码实现。
分布式锁 Redis (via Redisson) Redis (via go-redis) 功能类似,Spring 提供更便捷的集成方式。
日志监控 Spring Boot Actuator, ELK Stack Prometheus, Grafana Spring Boot Actuator 提供更丰富的监控指标;Golang 的方案更灵活,需要更多代码实现。
任务调度 Spring Task, Quartz cron, go-cron Spring Task 和 Quartz 提供更丰富的任务调度功能;Golang 的方案更轻量级,需要更多代码实现。

总结

Spring Boot 技术栈: 提供了丰富的功能和高度的封装,可以快速开发复杂的分布式应用,但配置相对复杂,学习曲线较陡峭。

Golang 技术栈: 更轻量级,性能更高,上手更快,但需要更多手动配置和代码实现,对开发者的要求更高。