SprintBoot 2.X Page Optimization Technology (Page Cache + Object Cache)

Keywords: Database network github Redis

(9) SprintBoot 2.X page optimization technology (page caching + object caching)

1. Page optimization technology

1.1 Page Cache + URL Cache + Object Cache
  • Because of the concurrency bottleneck in the database, adding caching can reduce access to the database.
1.2 Page Static, Front-end and Back-end Separation
  • The back end only passes data to the front end, such as GoodDetail Vo, and the front end gets the data before rendering in the browser, not by the server.
  • The main purpose of page statization is to speed up the loading of pages. The product details and order details pages are made into static HTML (pure HTML). The loading of data only needs to request the server through ajax, and the static HTML pages can be cached in the client's browser.
1.3 Static Resource Optimization
  • JS/CSS compression, reduce traffic.
  • JS/CSS combination, reduce the number of connections.
1.4 CDN optimization
  • Content Distribution Network, Near Access

3.Caching problem

3.1 Cache Penetration
  • This refers to a request for data that must not exist, which will penetrate the cache and reach the database.

  • Solution:

    • Cache an empty data for these non-existent data;
    • Filter such requests.
3.2 Cache Avalanche
  • It refers to a large number of requests arriving in the database because the data is not loaded into the cache, or because the cached data is out of date in a large area at the same time, or because the cached server is down.
  • In a cached system, the system relies heavily on caching, which shares a large portion of data requests. When a cache avalanche occurs, the database cannot handle such a large request, resulting in a database crash.
  • Solution:
    • In order to prevent cache avalanche caused by large area expiration at the same time, it can be achieved by observing user behavior and reasonably setting the expiration time of cache.
    • In order to prevent cache avalanche caused by cache server downtime, distributed caching can be used. Each node in the distributed caching only caches part of the data. When one node is down, the caching of other nodes can be guaranteed to be still available.
    • Cache preheating can also be used to avoid caching avalanches caused by not caching a large amount of data shortly after the system is started.
3.3 Cache Consistency
  • Cache consistency requires that data be updated while cached data can be updated in real time.
  • Solution:
    • Update the cache immediately while updating the data.
    • Determine whether the cache is up-to-date before reading it, and update it if it is not up-to-date.
  • To ensure cache consistency, it costs a lot. The best way to cache data is to allow some dirty data to exist in the cache data.
3.4 Cache Bottomless Phenomenon
  • It refers to the phenomenon that a large number of caching nodes have been added to satisfy the business requirements, but the performance has not improved but decreased instead.
  • Cause: Cache system usually uses hash function to map key to the corresponding cached nodes. With the increase of the number of cached nodes, the key values are distributed to more nodes. As a result, one batch operation of the client will involve multiple network operations, which means that the time consumption of batch operation will not increase with the increase of the number of nodes. The break increases. In addition, the number of network connections increases, which also has a certain impact on the performance of nodes.
  • Solution:
    • Optimize batch data operation commands;
    • Reduce the number of network communications;
    • Reduce access costs, use long connection/connection pool, NIO, etc.

4. Project application

4.1 Page Cache
  • Cache html pages from manual rendering to redis (such as to_list)
  • Page caching and URL caching are relatively short and suitable for scenarios: pages that do not change much. If pagination, not all caches, generally caching the first two pages. It can prevent large concurrent access in a short time.
  • Process:
    • Caching
    • Manual rendering template
    • Output of results
  • Code
    @RequestMapping(value = "/to_list", produces = "text/html")
    @ResponseBody
    public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {

        //Caching
        String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
        if (!StringUtils.isEmpty(html)) {
            return html;
        }

        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        model.addAttribute("user", user);
        model.addAttribute("goodsList", goodsList);

        //Manual rendering
        IWebContext ctx =new WebContext(request,response,
                request.getServletContext(),request.getLocale(),model.asMap());
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);

        if (!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsList, "", html);
        }
        //Output of results
        return html;
    }
4.2 URL cache
  • It's essentially the same as page caching, but with a merchandise id added
@RequestMapping(value = "/to_detail/{goodsId}", produces = "text/html")
    @ResponseBody
    public String detail2(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user, @PathVariable("goodsId") long goodsId) {
        model.addAttribute("user", user);

        //Caching
        String html = redisService.get(GoodsKey.getGoodsDetail, "" + goodsId, String.class);
        if (!StringUtils.isEmpty(html)) {
            return html;
        }

        //Inquire about the details of the goods according to id
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        model.addAttribute("goods", goods);

        long startTime = goods.getStartDate().getTime();
        long endTime = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();

        int miaoshaStatus = 0;
        int remainSeconds = 0;

        if (now < startTime) {//The second kill hasn't started yet. Countdown
            miaoshaStatus = 0;
            remainSeconds = (int) ((startTime - now) / 1000);
        } else if (now > endTime) {//The second kill is over
            miaoshaStatus = 2;
            remainSeconds = -1;
        } else {//Stopkill in progress
            miaoshaStatus = 1;
            remainSeconds = 0;
        }
        model.addAttribute("miaoshaStatus",miaoshaStatus);
        model.addAttribute("remainSeconds",remainSeconds);

        //Manual rendering
        IWebContext ctx =new WebContext(request,response,
                request.getServletContext(),request.getLocale(),model.asMap());
        html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx);
        if (!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsDetail, "" + goodsId, html);
        }
        return html;
    }
}
4.3 Object Caching
  • Including user information, commodity information, order information and token data caching, using caching to reduce access to the database, greatly accelerating the query speed.
    public MiaoshaUser getById(long id){
        //Object caching
        MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, "" + id, MiaoshaUser.class);
        if (user != null) {
            return user;
        }
        //Get the database
        user = miaoShaUserDao.getById(id);
        //Re-store cache
        if (user != null) {
            redisService.set(MiaoshaUserKey.getById, "" + id, user);
        }
        return user;
    }
4.4 Typical Cache Synchronization Scenario: Updating Password
  • Updating databases and caches must ensure data consistency. When updating objects, caches should also be invalidated (token is an exception in the project, because it will not be able to perform subsequent operations after token fails).
    /**
     * Typical cache synchronization scenario: update password
     */
    public boolean updatePassword(String token, long id, String formPass) {
        //Fetch user
        MiaoshaUser user = getById(id);
        if(user == null) {
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //Update database
        MiaoshaUser toBeUpdate = new MiaoshaUser();
        toBeUpdate.setId(id);
        toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
        miaoShaUserDao.update(toBeUpdate);
        //Update Cache: Delete before Insert
        redisService.delete(MiaoshaUserKey.getById, ""+id);
        user.setPassword(toBeUpdate.getPassword());
        redisService.set(MiaoshaUserKey.token, token, user);
        return true;
    }

5. Consistency between caching and database, from https://www.cnblogs.com/johnsblog/p/6426287.html

Why can't you first process the cache and then update the database?
Why does your cache update strategy update the database first and then delete the cache? What's the problem with other situations?
Question: How to keep the cache consistent with the database?
To answer this question, let's first look at some inconsistencies. I divide inconsistencies into three situations

  1. The database has data and the cache has no data.
  2. The database has data, the cache also has data, the data is not equal;
  3. The database has no data and the cache has data.
  • Strategy:
    1. First, try to read from the cache, and return the data directly; if not, read the database, and write the data to the cache, and return.
    2. When data needs to be updated, the database is updated first, and then the corresponding data in the cache is invalidated (deleted).
  • There are probably the following solutions:
  1. The higher the consistency requirement is, the faster the retry is.

  2. Regular full updates, in short, are that I periodically remove all caches, and then load them in full.

  3. Give all caches an expiration period.

  • The third scheme can be said to be a big killer. Any inconsistency can be solved by the expiration period. The shorter the expiration period, the higher the data consistency. But the shorter the expiration period, the more frequent the database will be checked. Therefore, the expiration period should be determined according to the business.

Posted by digitalmartyr on Sat, 03 Aug 2019 23:51:23 -0700