1. 为什么"内存Join"是个无法绕过的话题首先,我们先简单解释下,什么是“内存Join” 。
相信大家对关系数据库的 join 语句肯定不陌生,其作用就是通过关联关系从多个表中查询数据 , 关联条件和数据聚合全部由 数据库服务完成 。
![DDD死党:内存Join——将复用和扩展用到极致](http://img.jiangsulong.com/231214/1H5296424-0.jpg)
文章插图
而 内存 Join,简单来说就是把原本数据库帮我们完成的数据聚合操作迁移到应用服务,在应用服务的内存中完成 。
![DDD死党:内存Join——将复用和扩展用到极致](http://img.jiangsulong.com/231214/1H52a5S-1.jpg)
文章插图
数据库join非常简单,但随着系统的发展 , 内存join变得越来越重要,其核心驱动力有:
- 微服务 。微服务要求“数据资产私有化”,也就是说每个服务的数据库是私有资产,不允许其他服务的直接访问 。如果需要访问,只能通过服务所提供的接口完成
- 分库分表的限制 。当数据量超过 MySQL 单实例承载能力时,通常会通过“分库分表”这一技术手段来解决,分库分表后,数据被分散到多个分区中,导致 join 语句失效
- 性能瓶颈 。在高并发情况下,join 存在一定的性能问题,高并发、高性能端场景不适合使用 。很多公司规范中对 join 的使用做出了明确的限制
本篇文章从查询订单这个业务场景为入口,针对数据的内存join进行多次抽象和封装,最终实现“内存Join声明化” 。
首先,先看下最终的效果 , 从直观上感受下“抽象”带来的效率提升 。
![DDD死党:内存Join——将复用和扩展用到极致](http://img.jiangsulong.com/231214/1H52961U-2.jpg)
文章插图
通过抽象,可以达到如下效果:
- 左边一坨“模板代码” 等价于右边一个注解
- 模型需要绑定 UserVO 数据,只需使用 @JoinUserVOOnId 注解进行声明配置即可
- @JoinInMemoryConfig 注解的 PARALLEL 配置将开启多线程并行处理 , 以提供性能
能力声明化 , 是抽象的一种高级表现,无需编写代码,通过配置的方式为特定组件进行能力加强 。
在正式开始之前 , 可以先了解下整体的推演流程:
![DDD死党:内存Join——将复用和扩展用到极致](http://img.jiangsulong.com/231214/1H5292395-3.jpg)
文章插图
3.【案例分析】订单查询假设,我们是订单中心的一位研发伙伴,需要开发 “我的订单” 模块 , 其核心接口包括:
- 我的订单,查询用户的全部订单,包括 订单信息、用户信息、邮寄地址信息、商品信息等;
- 订单详情,查询某个订单的详细信息,包括 订单信息、用户信息、邮寄地址信息、商品信息、支付信息等;
public interface OrderService {// 我的订单List<OrderListVO> getByUserId(Long userId);// 订单详情OrderDetAIlVO getDetailByOrderId(Long orderId);}// 为配合多种实现策略 , 使用抽象类进行统一public abstract class OrderListVO {public abstract OrderVO getOrder();public abstract UserVO getUser();public abstract AddressVO getAddress();public abstract ProductVO getProduct();}// 为配合多种实现策略,使用抽象类进行统一public abstract class OrderDetailVO {public abstract OrderVO getOrder();public abstract UserVO getUser();public abstract AddressVO getAddress();public abstract ProductVO getProduct();public abstract List<PayInfoVO> getPayInfo();}
3.1. Foreach + 单条抓取方案这么简单的需求,那不是信手拈来,很快就提供了一版![DDD死党:内存Join——将复用和扩展用到极致](http://img.jiangsulong.com/231214/1H5295210-4.jpg)
文章插图
代码具体如下:
@Servicepublic class OrderServiceCodingV1 implements OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate AddressRepository addressRepository;@Autowiredprivate ProductRepository productRepository;@Autowiredprivate UserRepository userRepository;@Autowiredprivate PayInfoRepository payInfoRepository;@Overridepublic List<OrderListVO> getByUserId(Long userId) {// 获取用户订单List<Order> orders = this.orderRepository.getByUserId(userId);// 依次进行数据绑定return orders.stream().map(order -> convertToOrderListVO(order)).collect(toList());}private OrderListVOCodingV1 convertToOrderListVO(Order order) {OrderVO orderVO = OrderVO.Apply(order);OrderListVOCodingV1 orderDetailVO = new OrderListVOCodingV1(orderVO);// 绑定地址信息Address address = this.addressRepository.getById(order.getAddressId());AddressVO addressVO = AddressVO.apply(address);orderDetailVO.setAddress(addressVO);// 绑定用户信息User user = this.userRepository.getById(order.getUserId());UserVO userVO = UserVO.apply(user);orderDetailVO.setUser(userVO);// 绑定商品信息Product product = this.productRepository.getById(order.getProductId());ProductVO productVO = ProductVO.apply(product);orderDetailVO.setProduct(productVO);return orderDetailVO;}@Overridepublic OrderDetailVO getDetailByOrderId(Long orderId) {// 暂时忽略Order order = this.orderRepository.getById(orderId);return convertToOrderDetailVO(order);}private OrderDetailVO convertToOrderDetailVO(Order order) {OrderDetailVOCodingV1 orderDetail = new OrderDetailVOCodingV1(OrderVO.apply(order));// 获取地址并进行绑定Address address = this.addressRepository.getById(order.getAddressId());AddressVO addressVO = AddressVO.apply(address);orderDetail.setAddress(addressVO);// 获取用户并进行绑定User user = this.userRepository.getById(order.getUserId());UserVO userVO = UserVO.apply(user);orderDetail.setUser(userVO);// 获取商品并进行绑定Product product = this.productRepository.getById(order.getProductId());ProductVO productVO = ProductVO.apply(product);orderDetail.setProduct(productVO);// 获取支付信息并进行绑定List<PayInfo> payInfos = this.payInfoRepository.getByOrderId(order.getId());List<PayInfoVO> payInfoVOList = payInfos.stream().map(PayInfoVO::apply).collect(toList());orderDetail.setPayInfo(payInfoVOList);return orderDetail;}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 能如何看电脑内存,如何查看电脑c盘和d盘内存
- 你知道怎样在 Python 中管理内存吗
- 如何查看手机运行内存大小,如何查看手机软件占用的内存
- 能咋滴看电脑内存,怎么查看自己电脑内存多大
- sd卡和内存卡有什么区别
- 电脑能插3根内存条,电脑能咋滴共存两个窗口
- 苹果手机怎么在电脑上清理内存,苹果手机怎样可以清理内存拉圾
- oppo reno ace能插内存卡吗
- 手机内存满了该怎么处理,手机里照片太多,占内存太多该怎么办?
- 能咋地看电脑内存,如何查看电脑的内存条型号大小