微服务跨服务查询难,数据关联复杂
微服务跨服务查询难,数据关联复杂
你有没有遇到过这种场景:单体应用里一个JOIN查询就能搞定的事,微服务里要调用3个服务,在内存里拼数据;查询性能慢得要死,因为每个服务都要单独查数据库;想优化,却发现数据分散在不同服务里,根本没法建索引。
这就是微服务跨服务查询的典型痛点:查询难,数据关联复杂。
可二次开发的解决方案
好消息是,这些问题都可以通过二次开发解决:
深度文章
微服务跨服务查询难,数据关联复杂
你有没有遇到过这种场景:单体应用里一个JOIN查询就能搞定的事,微服务里要调用3个服务,在内存里拼数据;查询性能慢得要死,因为每个服务都要单独查数据库;想优化,却发现数据分散在不同服务里,根本没法建索引。
这就是微服务跨服务查询的典型痛点:查询难,数据关联复杂。
可二次开发的解决方案
好消息是,这些问题都可以通过二次开发解决:
1. API组合模式
由API网关或专门的聚合服务组合多个服务的数据:
@RestController
public class OrderCompositeController {
@Autowired
private OrderService orderService;
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@GetMapping("/orders/{id}/detail")
public OrderDetail getOrderDetail(@PathVariable Long id) {
// 并行调用多个服务
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(
() -> orderService.getOrder(id));
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(
() -> userService.getUser(order.getUserId()));
CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(
() -> productService.getProduct(order.getProductId()));
// 等待所有结果
CompletableFuture.allOf(orderFuture, userFuture, productFuture).join();
// 组装数据
return buildOrderDetail(orderFuture.get(), userFuture.get(), productFuture.get());
}
}
2. CQRS架构
命令查询职责分离,读写分离:
// 写服务:处理命令,更新数据
@Service
public class OrderCommandService {
public void createOrder(CreateOrderCommand cmd) {
// 更新订单数据库
orderRepository.save(order);
// 发送事件到读数据库
eventPublisher.publish(new OrderCreatedEvent(order));
}
}
// 读服务:专门优化查询
@Service
public class OrderQueryService {
public OrderDetail getOrderDetail(Long orderId) {
// 从读数据库直接查询,已关联好所有数据
return orderReadRepository.findDetailById(orderId);
}
}
3. 数据冗余策略
在查询服务中冗余存储必要的数据:
@Entity
@Table(name = "order_detail_view")
public class OrderDetailView {
@Id
private Long orderId;
// 冗余用户信息
private String userName;
private String userAvatar;
// 冗余商品信息
private String productName;
private BigDecimal productPrice;
// 更新时间
private LocalDateTime updatedAt;
}
4. GraphQL联邦查询
使用GraphQL实现灵活的跨服务查询:
query {
order(id: "123") {
id
status
user {
name
avatar
}
product {
name
price
}
}
}
查询方案对比
| 方案 | 优点 | 缺点 | 适用场景 | |------|------|------|---------| | API组合 | 简单、无侵入 | 性能差、多次查询 | 简单场景 | | CQRS | 性能好、可优化 | 复杂度高、数据同步 | 复杂查询 | | 数据冗余 | 查询快 | 数据一致性难保证 | 读多写少 | | GraphQL | 灵活、按需查询 | 学习成本高 | API网关 |
Microservice Cross-Service Query Hard, Data Association Complex
Have you encountered this scenario: in monolithic app one JOIN query solves it, in microservices need to call 3 services, assemble data in memory; query performance terribly slow, because each service queries database separately; want to optimize, but data scattered across services, can't build indexes.
This is the typical pain point of microservice cross-service query: query hard, data association complex.
Developer Solutions
Good news is, these problems can all be solved through secondary development:
1. API Composition Pattern
API gateway or dedicated aggregation service composes data from multiple services:
@RestController
public class OrderCompositeController {
@Autowired
private OrderService orderService;
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@GetMapping("/orders/{id}/detail")
public OrderDetail getOrderDetail(@PathVariable Long id) {
// Parallel call to multiple services
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(
() -> orderService.getOrder(id));
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(
() -> userService.getUser(order.getUserId()));
CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(
() -> productService.getProduct(order.getProductId()));
// Wait for all results
CompletableFuture.allOf(orderFuture, userFuture, productFuture).join();
// Assemble data
return buildOrderDetail(orderFuture.get(), userFuture.get(), productFuture.get());
}
}
2. CQRS Architecture
Command Query Responsibility Segregation, read-write separation:
// Write service: handle commands, update data
@Service
public class OrderCommandService {
public void createOrder(CreateOrderCommand cmd) {
// Update order database
orderRepository.save(order);
// Send event to read database
eventPublisher.publish(new OrderCreatedEvent(order));
}
}
// Read service: specialized query optimization
@Service
public class OrderQueryService {
public OrderDetail getOrderDetail(Long orderId) {
// Query directly from read database, all data already joined
return orderReadRepository.findDetailById(orderId);
}
}
3. Data Redundancy Strategy
Redundantly store necessary data in query service:
@Entity
@Table(name = "order_detail_view")
public class OrderDetailView {
@Id
private Long orderId;
// Redundant user info
private String userName;
private String userAvatar;
// Redundant product info
private String productName;
private BigDecimal productPrice;
// Update time
private LocalDateTime updatedAt;
}
4. GraphQL Federation Query
Use GraphQL for flexible cross-service queries:
query {
order(id: "123") {
id
status
user {
name
avatar
}
product {
name
price
}
}
}
Query Solution Comparison
| Solution | Pros | Cons | Use Case | |----------|------|------|----------| | API Composition | Simple, non-invasive | Poor performance, multiple queries | Simple scenarios | | CQRS | Good performance, optimizable | High complexity, data sync | Complex queries | | Data Redundancy | Fast queries | Data consistency hard to guarantee | Read-heavy | | GraphQL | Flexible, on-demand query | High learning cost | API gateway |
讨论 (0)
请先登录后参与讨论
还没有评论,成为第一个吐槽的人?