Redis from theory to actual combat: use Redis to implement store query cache (step by step analysis of cache update strategy)


Come on, come on, don't be too anxious (#^.^#)

1. What is cache

Cache is the buffer area for data exchange, which is a temporary place to store data, and generally has high read and write performance .
We can do caching in many places, such as browser caching, application layer caching, database caching, etc.
insert image description here


Second, the role of the cache

  • We can use caching to reduce backend load;
  • Using cache can improve read and write efficiency and reduce response time.

3. Add merchant cache

Idea analysis:

  • First query whether the data exists from Redis: if it exists, return the data; if it does not exist, access the database;
  • Then query whether the data exists from the database: if it exists, return the data from the database and write it into the redis cache; if it does not exist, prompt the user that it does not exist.

Code:

	//controller层
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
    
    
        return shopService.queryShopById(id);
    }
	//service层
    @Autowired
    private ShopMapper shopMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Result queryShopById(Long id) {
    
    
        String key = CACHE_SHOP_KEY + id;
        String shopCache = redisTemplate.opsForValue().get(key);
        //如果在缓存中查询到商户,则返回数据给前端
        if (StrUtil.isNotBlank(shopCache)) {
    
    
            Shop shop = JSONUtil.toBean(shopCache, Shop.class);
            System.out.println("shopCache" + shopCache);
            return Result.ok(shop);
        }
        //不存在则根据id在数据库中查找
        Shop shop = shopMapper.selectById(id);
        if (shop == null) {
    
    
            return Result.fail("店铺不存在");
        }
        //店铺存在,写入缓存
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));
        return Result.ok(shop);
    }

The overall idea is as follows:

insert image description here


Fourth, analyze the cache update strategy

  • The code just now has some problems in updating data, so let’s discuss the cache update strategy of redis together.

Redis has three cache update strategies:

memory obsolescence timeout culling active update
illustrate You don't need to maintain it yourself, use the memory elimination mechanism of redis to automatically eliminate some data when the memory is insufficient. Update cache on next query Add ttl (expiration) time to the cached data, and automatically delete the cache after expiration. Update cache on next query Write business logic to update the cache while modifying the database
consistency Difference generally good
maintenance cost none Low high

Use of business scenarios:

Business scene low consistency requirements High Consistency Requirements
Use the memory elimination mechanism Use active updates, and use timeout culling as a bottom-up solution

For the cache of merchant queries, the active update strategy is used here, and there are three options for the selection of the active update strategy, and the first option is used here:

Option One By the caller of the cache, the cache is updated at the same time as the database is updated
Option II The cache and database are integrated into one service, and the consistency is maintained by the service. The caller invokes the service without caring about cache consistency issues
third solution The caller only operates the cache, and other threads asynchronously persist the cached data to the database to ensure final consistency

The solution is right. When operating the cache and database, we need to consider three issues:

1. Delete cache or update cache?

If the update cache is used, the cache will be updated every time the database is updated, and there are many invalid write operations, so we do not use it; choose to delete the cache, invalidate the cache when updating the database, and update the cache when querying.

2. How to ensure that the cache and database operations succeed or fail at the same time?

Monolithic system: put cache and database operations in one thing
Distributed system: use distributed things such as TCC

3. Do you operate the cache first or the database first?

insert image description here

  • If you delete the cache first and then operate the database, as shown in the figure, when thread 1 deletes the cache and starts to update the database, thread 2 comes in, and the database has not been updated at this time, then the cache written by thread 2 is not the updated data of the database
  • If you operate the database first and then delete the cache, as shown in the figure, when thread 1 queries the database and writes to the cache, thread 2 comes in. At this time, thread 2 updates the database and deletes the cache, then the cache written by thread 1 is not the database updated data
  • But since the latter is written to the cache at a lower time than the database is updated, the second option is better, and the probability of data out of sync is lower!

Small summary:

  • For read operations: if the cache hits, return directly; if the cache misses, query the database, write to the cache, and set the timeout period.
  • For write operations: write to the database first, and then delete the cache; ensure the atomicity of database and cache operations.

Code:

  • When querying a store based on id, if the cache misses, query the database, write the database result to the cache, and set the timeout period.
  • When modifying the store according to the id, first modify the database, and then delete the cache.

service layer code:

	//查询商户
	@Override
    public Result queryShopById(Long id) {
    
    
        String key = CACHE_SHOP_KEY + id;
        String shopCache = redisTemplate.opsForValue().get(key);
        //如果在缓存中查询到商户,则返回数据给前端
        if (StrUtil.isNotBlank(shopCache)) {
    
    
            Shop shop = JSONUtil.toBean(shopCache, Shop.class);
            return Result.ok(shop);
        }
        //不存在则根据id在数据库中查找
        Shop shop = shopMapper.selectById(id);
        if (shop == null) {
    
    
            return Result.fail("店铺不存在");
        }
        //店铺存在,写入缓存,过期时间设置为30分钟
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return Result.ok(shop);
    }
    //更新商户
    @Override
    @Transactional
    public Result updateShop(Shop shop) {
    
    
        Long shopId = shop.getId();
        if (shopId == null) {
    
    
            return Result.fail("店铺id不能为空");
        }
        //先更新数据库
        shopMapper.updateById(shop);
        //再删除缓存
        redisTemplate.delete(CACHE_SHOP_KEY + shopId);
        return Result.ok();
    }

Controller layer code:

    /**
     * 根据id查询商铺信息
     * @param id 商铺id
     * @return 商铺详情数据
     */
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
    
    
        return shopService.queryShopById(id);
    }
    /**
     * 更新商铺信息
     * @param shop 商铺数据
     * @return 无
     */
    @PutMapping
    public Result updateShop(@RequestBody Shop shop) {
    
    
        return shopService.updateShop(shop);
    }

This is the end, if you have any questions, you can discuss together~

Guess you like

Origin blog.csdn.net/weixin_59654772/article/details/127832958