为什么
单机性能已经无法处理海量规模的数据请求,所有的动作仅仅只包含读或者写,但是读得快,写不会引起冲突这是非常重要的环节。 一旦引入了新的一层,各种琐碎堆积在一起,就形成了各种处理冲突的算法。
读
为了应对高并发读场景下数据库的性能瓶颈,引入缓存机制成为必然选择。常见方案是使用 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 技术栈: 更轻量级,性能更高,上手更快,但需要更多手动配置和代码实现,对开发者的要求更高。