Просмотр исходного кода

开发双表异步存储,加快速度

lutong 2 месяцев назад
Родитель
Сommit
62550e79d0

+ 60 - 25
alien-dining/src/main/java/shop/alien/dining/service/impl/CartServiceImpl.java

@@ -3,6 +3,7 @@ package shop.alien.dining.service.impl;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -28,6 +29,9 @@ import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
 /**
@@ -45,6 +49,13 @@ public class CartServiceImpl implements CartService {
     private static final String COUPON_USED_KEY_PREFIX = "coupon:used:table:";
     private static final int CART_EXPIRE_SECONDS = 24 * 60 * 60; // 24小时过期
 
+    // 异步写入数据库的线程池(专门用于购物车数据库写入)
+    private static final ExecutorService CART_DB_WRITE_EXECUTOR = Executors.newFixedThreadPool(5, r -> {
+        Thread t = new Thread(r, "cart-db-write-" + System.currentTimeMillis());
+        t.setDaemon(true);
+        return t;
+    });
+
     private final BaseRedisService baseRedisService;
     private final StoreTableMapper storeTableMapper;
     private final StoreCuisineMapper storeCuisineMapper;
@@ -722,14 +733,21 @@ public class CartServiceImpl implements CartService {
     }
 
     /**
-     * 保存购物车到Redis和数据库(双写策略)
+     * 保存购物车到Redis和数据库(优化后的双写策略)
+     * Redis同步写入(保证实时性),数据库异步批量写入(提高性能)
      */
     private void saveCart(CartDTO cart) {
-        // 保存到Redis
+        // 1. 同步保存到Redis(保证实时性)
         saveCartToRedis(cart);
 
-        // 保存到数据库
-        saveCartToDatabase(cart);
+        // 2. 异步保存到数据库(不阻塞主流程,提高性能)
+        CompletableFuture.runAsync(() -> {
+            try {
+                saveCartToDatabase(cart);
+            } catch (Exception e) {
+                log.error("异步保存购物车到数据库失败, tableId={}, error={}", cart.getTableId(), e.getMessage(), e);
+            }
+        }, CART_DB_WRITE_EXECUTOR);
     }
 
     /**
@@ -742,29 +760,25 @@ public class CartServiceImpl implements CartService {
     }
 
     /**
-     * 保存购物车到数据库
+     * 保存购物车到数据库(优化后的批量操作版本)
+     * 使用批量逻辑删除和批量插入,提高性能
      */
     private void saveCartToDatabase(CartDTO cart) {
         try {
-            // 先删除该桌号的所有购物车记录(逻辑删除)
-            LambdaQueryWrapper<StoreCart> deleteWrapper = new LambdaQueryWrapper<>();
-            deleteWrapper.eq(StoreCart::getTableId, cart.getTableId());
-            deleteWrapper.eq(StoreCart::getDeleteFlag, 0);
-            List<StoreCart> existingCarts = storeCartMapper.selectList(deleteWrapper);
-            if (existingCarts != null && !existingCarts.isEmpty()) {
-                Date now = new Date();
-                for (StoreCart existing : existingCarts) {
-                    existing.setDeleteFlag(1);
-                    existing.setUpdatedTime(now);
-                    storeCartMapper.updateById(existing);
-                }
-            }
+            Date now = new Date();
+            Integer userId = TokenUtil.getCurrentUserId();
 
-            // 插入新的购物车记录
-            if (cart.getItems() != null && !cart.getItems().isEmpty()) {
-                Integer userId = TokenUtil.getCurrentUserId();
-                Date now = new Date();
+            // 1. 批量逻辑删除该桌号的所有购物车记录(使用批量更新,而不是循环单个更新)
+            LambdaUpdateWrapper<StoreCart> deleteWrapper = new LambdaUpdateWrapper<>();
+            deleteWrapper.eq(StoreCart::getTableId, cart.getTableId())
+                    .eq(StoreCart::getDeleteFlag, 0)
+                    .set(StoreCart::getDeleteFlag, 1)
+                    .set(StoreCart::getUpdatedTime, now);
+            storeCartMapper.update(null, deleteWrapper);
 
+            // 2. 批量插入新的购物车记录
+            if (cart.getItems() != null && !cart.getItems().isEmpty()) {
+                List<StoreCart> cartList = new ArrayList<>(cart.getItems().size());
                 for (CartItemDTO item : cart.getItems()) {
                     StoreCart storeCart = new StoreCart();
                     storeCart.setTableId(cart.getTableId());
@@ -783,19 +797,40 @@ public class CartServiceImpl implements CartService {
                     storeCart.setCreatedTime(now);
                     storeCart.setCreatedUserId(userId);
                     storeCart.setUpdatedTime(now);
-                    storeCartMapper.insert(storeCart);
+                    cartList.add(storeCart);
+                }
+
+                // 批量插入(如果数量较少,直接循环插入;如果数量较多,可以考虑分批插入)
+                if (cartList.size() <= 50) {
+                    // 小批量直接插入
+                    for (StoreCart storeCart : cartList) {
+                        storeCartMapper.insert(storeCart);
+                    }
+                } else {
+                    // 大批量分批插入(每批50条)
+                    int batchSize = 50;
+                    for (int i = 0; i < cartList.size(); i += batchSize) {
+                        int end = Math.min(i + batchSize, cartList.size());
+                        List<StoreCart> batch = cartList.subList(i, end);
+                        for (StoreCart storeCart : batch) {
+                            storeCartMapper.insert(storeCart);
+                        }
+                    }
                 }
             }
 
-            // 更新桌号表的购物车统计
+            // 3. 更新桌号表的购物车统计
             StoreTable table = storeTableMapper.selectById(cart.getTableId());
             if (table != null) {
                 table.setCartItemCount(cart.getTotalQuantity());
                 table.setCartTotalAmount(cart.getTotalAmount());
                 storeTableMapper.updateById(table);
             }
+
+            log.debug("购物车数据已异步保存到数据库, tableId={}, itemCount={}", 
+                    cart.getTableId(), cart.getItems() != null ? cart.getItems().size() : 0);
         } catch (Exception e) {
-            log.error("保存购物车到数据库失败: {}", e.getMessage(), e);
+            log.error("保存购物车到数据库失败, tableId={}, error={}", cart.getTableId(), e.getMessage(), e);
             // 数据库保存失败不影响Redis,继续执行
         }
     }