在库存服务中实现缓存与数据库双写一致性保障方案(四)

读请求去重优化:
如果一个读请求过来,发现前面已经有一个写请求和一个读请求了
那么这个读请求就不需要压入队列中了
因为那个写请求肯定会更新数据库,然后那个读请求肯定会从数据库中读取最新数据。
刷新到缓存中,自己只需要hang一会就可以从缓存中读到数据了。

 

空数据读请求过滤优化:

可能某个数据,在数据库里面压根没有,那么这个读请求是不需要放入内存队列里的,且读请求在controller那一层,直接就可以返回了,不需要等待。

如果缓存中没有数据,那就说明,第一个 是数据库里没有数据,缓存肯定也没数据,第二个是数据库更新操作过来了,先删除缓存,此时缓存是空的,但是数据库是由的,

我们之前做读请求去重优化,用一个flagmap,只要前面由数据库更新操作,flag就肯定是存在的,你只不过可以根据true或者false,判断你前面执行的是写请求还是读请求,

但是如果flag压根就没有呢,说明这个数据,不论是写请求还是读请求都没有过。

那个时候过来的读请求,发现flag是null,就可以认为数据库里肯定也是空的,就不会去读取了。

或者说,我们也可以认为每个商品最初都有库存,但是因为初始库存肯定会同步到缓存中去,有一种特殊的情况,就是说,商品库存本来在redis中有缓存的。

但是因为redis内存满了,就给干掉了,但是此时数据库中是有值的。

那么在这种情况下,可能就是之前没有任何的写请求和读请求的flag的值,此时还是需要从数据库中重新加载一次数据到缓存中的队列。

对一个商品的库存的读取 操作,要求读取的数据库的库存数据,然后更新到缓存中,多个读,这多个读,其实只要有一个压到内存队列里就可以了。

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
public void process(Request request) {
    try {
 
        Map<Integer, Boolean> flagMap = RequestQueue.getInstance().getFlagMap();
 
        //更新的操作
        if(request instanceof DataUpdateRequest){
            flagMap.put(request.getProductId(),true);
        }
 
        else if(request instanceof ProductInventoryCacheRefreshRequest){
 
 
 
            Boolean flag = flagMap.get(request.getProductId());
            if(flag==null){
                flagMap.put(request.getProductId(),false);
            }
            //如果是缓存刷新的请求,那么就判断如果标识不为空,且是true,说明之前有一个更新请求
            if(flag!=null&&flag){
              flagMap.put(request.getProductId(),false);
          }
          //如果说缓存刷新的请求,且标识不为空,但是标识是false,说明前面有一个缓存更新请求+缓存刷新请求了
                if(flag!=null&&!flag){
                    return;
                }
        }
        /**
         * 根据id去hash集合中的位置,获取到位置的queue,把数据加到queue中。
         */
        ArrayBlockingQueue<Request> routingQueue =
                getRoutingQueue(request.getProductId());
        routingQueue.put(request);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
1
如果把上面的全部都放入这个类里面,涉及到很多多线程修改操作,所以把里面的数据都挪到
WorkerThread类的->call。

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Override
   public Boolean call() throws Exception {
       try {
           while(true) {
               // ArrayBlockingQueue
               // Blocking就是说明,如果队列满了,或者是空的,那么都会在执行操作的时候,阻塞住
               Request request = queue.take();
               boolean forceRfresh = request.isForceRefresh();
 
               // 先做读请求的去重
               if(!forceRfresh) {
                   RequestQueue requestQueue = RequestQueue.getInstance();
                   Map<Integer, Boolean> flagMap = requestQueue.getFlagMap();
 
                   if(request instanceof DataUpdateRequest) {
                       // 如果是一个更新数据库的请求,那么就将那个productId对应的标识设置为true
                       flagMap.put(request.getProductId(), true);
                   } else if(request instanceof ProductInventoryCacheRefreshRequest) {
                       Boolean flag = flagMap.get(request.getProductId());
 
                       // 如果flag是null
                       if(flag == null) {
                           flagMap.put(request.getProductId(), false);
                       }
 
                       // 如果是缓存刷新的请求,那么就判断,如果标识不为空,而且是true,就说明之前有一个这个商品的数据库更新请求
                       if(flag != null && flag) {
                           flagMap.put(request.getProductId(), false);
                       }
 
                       // 如果是缓存刷新的请求,而且发现标识不为空,但是标识是false
                       // 说明前面已经有一个数据库更新请求+一个缓存刷新请求了,大家想一想
                       if(flag != null && !flag) {
                           // 对于这种读请求,直接就过滤掉,不要放到后面的内存队列里面去了
                           return true;
                       }
                   }
               }
 
               System.out.println("===========日志===========: 工作线程处理请求,商品id=" + request.getProductId());
               // 执行这个request操作
               request.process();
 
               // 假如说,执行完了一个读请求之后,假设数据已经刷新到redis中了
               // 但是后面可能redis中的数据会因为内存满了,被自动清理掉
               // 如果说数据从redis中被自动清理掉了以后
               // 然后后面又来一个读请求,此时如果进来,发现标志位是false,就不会去执行这个刷新的操作了
               // 所以在执行完这个读请求之后,实际上这个标志位是停留在false的
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
       return true;
   }

  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Override
 public Boolean call() throws Exception {
     try {
         while(true) {
             // ArrayBlockingQueue
             // Blocking就是说明,如果队列满了,或者是空的,那么都会在执行操作的时候,阻塞住
             Request request = queue.take();
             boolean forceRfresh = request.isForceRefresh();
 
             // 先做读请求的去重
             if(!forceRfresh) {
                 RequestQueue requestQueue = RequestQueue.getInstance();
                 Map<Integer, Boolean> flagMap = requestQueue.getFlagMap();
 
                 if(request instanceof DataUpdateRequest) {
                     // 如果是一个更新数据库的请求,那么就将那个productId对应的标识设置为true
                     flagMap.put(request.getProductId(), true);
                 } else if(request instanceof ProductInventoryCacheRefreshRequest) {
                     Boolean flag = flagMap.get(request.getProductId());
 
                     // 如果flag是null
                     if(flag == null) {
                         flagMap.put(request.getProductId(), false);
                     }
 
                     // 如果是缓存刷新的请求,那么就判断,如果标识不为空,而且是true,就说明之前有一个这个商品的数据库更新请求
                     if(flag != null && flag) {
                         flagMap.put(request.getProductId(), false);
                     }
 
                     // 如果是缓存刷新的请求,而且发现标识不为空,但是标识是false
                     // 说明前面已经有一个数据库更新请求+一个缓存刷新请求了,大家想一想
                     if(flag != null && !flag) {
                         // 对于这种读请求,直接就过滤掉,不要放到后面的内存队列里面去了
                         return true;
                     }
                 }
             }
 
             System.out.println("===========日志===========: 工作线程处理请求,商品id=" + request.getProductId());
             // 执行这个request操作
             request.process();
 
             // 假如说,执行完了一个读请求之后,假设数据已经刷新到redis中了
             // 但是后面可能redis中的数据会因为内存满了,被自动清理掉
             // 如果说数据从redis中被自动清理掉了以后
             // 然后后面又来一个读请求,此时如果进来,发现标志位是false,就不会去执行这个刷新的操作了
             // 所以在执行完这个读请求之后,实际上这个标志位是停留在false的
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     return true;
 }

  参数从前端传。

 

posted @   三号小玩家  阅读(117)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
Title
三号小玩家的 Mail: 17612457115@163.com, 联系QQ: 1359720840 微信: QQ1359720840

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示